diff --git a/README b/README index 4622381..6a41423 100644 --- a/README +++ b/README @@ -1,52 +1,38 @@ ############################################################################## # # -# This is the Conservatory archive of the wmctrl, a Unix command-line # -# tool to interact with an EWMH/NetWM-compatible X Window Manager. # +# wmctrl is free software (open source software), a Unix command-line # +# tool for interacting with an EWMH/NetWM-compatible X Window Manager. # # # -# wmctrl is free software (open source software), written by Tomas Styblo # -# with contributions from others. http://tripie.sweb.cz/utils/wmctrl/ is # -# its home page. # +# wmctrl was originally written by Tomas Styblo with contributions from # +# from others. Its home page is http://tripie.sweb.cz/utils/wmctrl/. # # # -# Because wmctrl does not appear to have any publicly accessible version # -# control repository, we've created this Conservatory Git repository. # -# We're not sure if Tomas Styblo or any of the other contributors are # -# are currently accepting bug reports, but if you run into a bug you # -# could try reporting it upstream and see what happens. (As of this # -# writing on 2018-09-08, the latest wmctrl release was in January 2005.) # +# The version here contains various enhancements that people around the # +# Net had added in their own divergent versions. The two main sources # +# of these enhancements were https://github.com/dancor/wmctrl and # +# https://github.com/geekless/wmctrl. The additions include these # +# new command-line options/actions: # # # -# About the Conservatory and its copy of wmctrl: # -# ---------------------------------------------- # +# -Y: iconify (Vadim Ushakov) # +# -S: sort window list in stacking order (Vadim Ushakov) # +# -j: list current desktop (Kevin Der) # +# -r -y: like -e but reactivate after the move (Chris Piro ) # +# -E: get-title (Dan Corson) # +# -z: lower window (Dan Corson) # # # -# The Conservatory (https://conservatory.github.io/) is a place where # -# free software that has no other public version-controlled home on the # -# Internet is made available in a git repository. For wmctrl, we started # -# from http://tripie.sweb.cz/utils/wmctrl/dist/wmctrl-1.07.tar.gz and # -# then deduced the names of the previous releases. We have committed # -# each upstream release as one git revision, starting from 1.00, using # -# https://github.com/Conservatory/conservatory.github.io/blob/master/\ # -# conservatory-import and the upstream ChangeLog to automate the process. # +# Other improvements are a typo fix for the --help output, and some # +# fixes for build-time compiler warnings. # # # -# There are other version-controlled copies of wmctrl on the Internet. # -# We found at least these two, and there may be others: # +# This repository also contains the full release history of wmctrl (one # +# commit per release), because it is based on the Conservatory archive # +# at https://github.com/Conservatory/wmctrl. # # # -# * https://github.com/geekless/wmctrl # +# I don't know if Tomas Styblo still accepts bug reports for wmctrl, as # +# the last version he released was 1.07 in January 2005. Please feel to # +# file bug reports and enhancements here. Although I don't consider this # +# copy to be "official", and can't commit to putting in much effort as # +# a maintainer, I'll try to review and merge easy fixes and enhancements. # # # -# Adds -Y (iconify) and -S (sort in stacking order) options. # -# Also backports some 64-bit-data fixes from a Debian patch. # -# # -# * https://github.com/dancor/wmctrl # -# # -# Adds -j (list current desktop), -z (lower window), # -# and some other interesting new options. # -# # -# Those two started from a top-skim of wmctrl 1.07, though, so they don't # -# preserve the full upstream release history. It'd be nice if someone # -# were to clone this Conservatory copy and then re-port the enhancements # -# from those other repositories to create a canonical new wmctrl release. # -# # -# In the meantime, this README is the only file we've changed from what's # -# in upstream, and the only change we made was to add this note. Below # -# is the original content of the README file. # +# Below is the original content of the upstream 1.07 README. # # # ############################################################################## diff --git a/main.c b/main.c index 9dc1600..af4ad00 100644 --- a/main.c +++ b/main.c @@ -49,18 +49,21 @@ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. " -l List windows managed by the window manager.\n" \ " -d List desktops. The current desktop is marked\n" \ " with an asterisk.\n" \ +" -j List current desktop.\n" \ " -s Switch to the specified desktop.\n" \ " -a Activate the window by switching to its desktop and\n" \ " raising it.\n" \ " -c Close the window gracefully.\n" \ " -R Move the window to the current desktop and\n" \ " activate it.\n" \ +" -Y Iconify (minimize) the window.\n" \ " -r -t Move the window to the specified desktop.\n" \ " -r -e Resize and move the window around the desktop.\n" \ " The format of the argument is described below.\n" \ +" -r -y Resize and move like above, then reactivate.\n" \ " -r -b Change the state of the window. Using this option it's\n" \ " possible for example to make the window maximized,\n" \ -" minimized or fullscreen. The format of the \n" \ +" shaded or fullscreen. The format of the \n" \ " argument and list of possible states is given below.\n" \ " -r -N Set the name (long title) of the window.\n" \ " -r -I Set the icon name (short title) of the window.\n" \ @@ -79,6 +82,7 @@ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. " -h Print help.\n" \ "\n" \ "Options:\n" \ +" -S List windows in stacking order (bottom to top).\n" \ " -i Interpret as a numerical window ID.\n" \ " -p Include PIDs in the window list. Very few\n" \ " X applications support this feature.\n" \ @@ -109,7 +113,7 @@ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. "\n" \ " The -x option may be used to interpret the argument\n" \ " as a string, which is matched against the window's\n" \ -" class name (WM_CLASS property). Th first matching\n" \ +" class name (WM_CLASS property). The first matching\n" \ " window is used. The matching isn't case sensitive\n" \ " and the string may appear in any position\n" \ " of the class name. So it's recommended to always use\n" \ @@ -194,6 +198,7 @@ static int client_msg(Display *disp, Window win, char *msg, unsigned long data2, unsigned long data3, unsigned long data4); static int list_windows (Display *disp); +static int list_current_desktop (Display *disp); static int list_desktops (Display *disp); static int showing_desktop (Display *disp); static int change_viewport (Display *disp); @@ -229,6 +234,7 @@ static struct { int show_class; int show_pid; int show_geometry; + int stacking_order; int match_by_id; int match_by_cls; int full_window_title_match; @@ -247,10 +253,10 @@ int main (int argc, char **argv) { /* {{{ */ Display *disp; memset(&options, 0, sizeof(options)); /* just for sure */ - + /* necessary to make g_get_charset() and g_locale_*() work */ setlocale(LC_ALL, ""); - + /* make "--help" and "--version" work. I don't want to use * getopt_long for portability reasons */ if (argc == 2 && argv[1]) { @@ -264,12 +270,15 @@ int main (int argc, char **argv) { /* {{{ */ } } - while ((opt = getopt(argc, argv, "FGVvhlupidmxa:r:s:c:t:w:k:o:n:g:e:b:N:I:T:R:")) != -1) { + while ((opt = getopt(argc, argv, "FGVvhSlupidjmxa:r:s:c:t:w:k:o:n:g:e:y:b:z:E:N:I:T:R:Y:")) != -1) { missing_option = 0; switch (opt) { case 'F': options.full_window_title_match = 1; break; + case 'S': + options.stacking_order = 1; + break; case 'G': options.show_geometry = 1; break; @@ -289,14 +298,14 @@ int main (int argc, char **argv) { /* {{{ */ case 'p': options.show_pid = 1; break; - case 'a': case 'c': case 'R': + case 'a': case 'c': case 'R': case 'z': case 'Y': case 'E': options.param_window = optarg; action = opt; break; case 'r': options.param_window = optarg; break; - case 't': case 'e': case 'b': case 'N': case 'I': case 'T': + case 't': case 'e': case 'b': case 'N': case 'I': case 'T': case 'y': options.param = optarg; action = opt; break; @@ -346,6 +355,9 @@ int main (int argc, char **argv) { /* {{{ */ case 'l': ret = list_windows(disp); break; + case 'j': + ret = list_current_desktop(disp); + break; case 'd': ret = list_desktops(disp); break; @@ -355,8 +367,9 @@ int main (int argc, char **argv) { /* {{{ */ case 'm': ret = wm_info(disp); break; - case 'a': case 'c': case 'R': - case 't': case 'e': case 'b': case 'N': case 'I': case 'T': + case 'a': case 'c': case 'R': case 'z': case 'E': + case 't': case 'e': case 'b': case 'N': case 'I': case 'T': case 'y': + case 'Y': if (! options.param_window) { fputs("No window was specified.\n", stderr); return EXIT_FAILURE; @@ -381,323 +394,323 @@ int main (int argc, char **argv) { /* {{{ */ ret = change_geometry(disp); break; } - + XCloseDisplay(disp); return ret; } /* }}} */ static void init_charset (void) {/*{{{*/ - const gchar *charset; /* unused */ - gchar *lang = getenv("LANG") ? g_ascii_strup(getenv("LANG"), -1) : NULL; - gchar *lc_ctype = getenv("LC_CTYPE") ? g_ascii_strup(getenv("LC_CTYPE"), -1) : NULL; - - /* this glib function doesn't work on my system ... */ - envir_utf8 = g_get_charset(&charset); - - /* ... therefore we will examine the environment variables */ - if (lc_ctype && (strstr(lc_ctype, "UTF8") || strstr(lc_ctype, "UTF-8"))) { - envir_utf8 = TRUE; - } - else if (lang && (strstr(lang, "UTF8") || strstr(lang, "UTF-8"))) { - envir_utf8 = TRUE; - } - - g_free(lang); - g_free(lc_ctype); - - if (options.force_utf8) { - envir_utf8 = TRUE; - } - p_verbose("envir_utf8: %d\n", envir_utf8); + const gchar *charset; /* unused */ + gchar *lang = getenv("LANG") ? g_ascii_strup(getenv("LANG"), -1) : NULL; + gchar *lc_ctype = getenv("LC_CTYPE") ? g_ascii_strup(getenv("LC_CTYPE"), -1) : NULL; + + /* this glib function doesn't work on my system ... */ + envir_utf8 = g_get_charset(&charset); + + /* ... therefore we will examine the environment variables */ + if (lc_ctype && (strstr(lc_ctype, "UTF8") || strstr(lc_ctype, "UTF-8"))) { + envir_utf8 = TRUE; + } + else if (lang && (strstr(lang, "UTF8") || strstr(lang, "UTF-8"))) { + envir_utf8 = TRUE; + } + + g_free(lang); + g_free(lc_ctype); + + if (options.force_utf8) { + envir_utf8 = TRUE; + } + p_verbose("envir_utf8: %d\n", envir_utf8); }/*}}}*/ static int client_msg(Display *disp, Window win, char *msg, /* {{{ */ - unsigned long data0, unsigned long data1, - unsigned long data2, unsigned long data3, - unsigned long data4) { - XEvent event; - long mask = SubstructureRedirectMask | SubstructureNotifyMask; - - event.xclient.type = ClientMessage; - event.xclient.serial = 0; - event.xclient.send_event = True; - event.xclient.message_type = XInternAtom(disp, msg, False); - event.xclient.window = win; - event.xclient.format = 32; - event.xclient.data.l[0] = data0; - event.xclient.data.l[1] = data1; - event.xclient.data.l[2] = data2; - event.xclient.data.l[3] = data3; - event.xclient.data.l[4] = data4; - - if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) { - return EXIT_SUCCESS; - } - else { - fprintf(stderr, "Cannot send %s event.\n", msg); - return EXIT_FAILURE; - } + unsigned long data0, unsigned long data1, + unsigned long data2, unsigned long data3, + unsigned long data4) { + XEvent event; + long mask = SubstructureRedirectMask | SubstructureNotifyMask; + + event.xclient.type = ClientMessage; + event.xclient.serial = 0; + event.xclient.send_event = True; + event.xclient.message_type = XInternAtom(disp, msg, False); + event.xclient.window = win; + event.xclient.format = 32; + event.xclient.data.l[0] = data0; + event.xclient.data.l[1] = data1; + event.xclient.data.l[2] = data2; + event.xclient.data.l[3] = data3; + event.xclient.data.l[4] = data4; + + if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) { + return EXIT_SUCCESS; + } + else { + fprintf(stderr, "Cannot send %s event.\n", msg); + return EXIT_FAILURE; + } }/*}}}*/ static gchar *get_output_str (gchar *str, gboolean is_utf8) {/*{{{*/ - gchar *out; - - if (str == NULL) { - return NULL; + gchar *out; + + if (str == NULL) { + return NULL; + } + + if (envir_utf8) { + if (is_utf8) { + out = g_strdup(str); } - - if (envir_utf8) { - if (is_utf8) { - out = g_strdup(str); - } - else { - if (! (out = g_locale_to_utf8(str, -1, NULL, NULL, NULL))) { - p_verbose("Cannot convert string from locale charset to UTF-8.\n"); - out = g_strdup(str); - } - } + else { + if (! (out = g_locale_to_utf8(str, -1, NULL, NULL, NULL))) { + p_verbose("Cannot convert string from locale charset to UTF-8.\n"); + out = g_strdup(str); + } + } + } + else { + if (is_utf8) { + if (! (out = g_locale_from_utf8(str, -1, NULL, NULL, NULL))) { + p_verbose("Cannot convert string from UTF-8 to locale charset.\n"); + out = g_strdup(str); + } } else { - if (is_utf8) { - if (! (out = g_locale_from_utf8(str, -1, NULL, NULL, NULL))) { - p_verbose("Cannot convert string from UTF-8 to locale charset.\n"); - out = g_strdup(str); - } - } - else { - out = g_strdup(str); - } + out = g_strdup(str); } + } - return out; + return out; }/*}}}*/ static int wm_info (Display *disp) {/*{{{*/ - Window *sup_window = NULL; - gchar *wm_name = NULL; - gchar *wm_class = NULL; - unsigned long *wm_pid = NULL; - unsigned long *showing_desktop = NULL; - gboolean name_is_utf8 = TRUE; - gchar *name_out; - gchar *class_out; - + Window *sup_window = NULL; + gchar *wm_name = NULL; + gchar *wm_class = NULL; + unsigned long *wm_pid = NULL; + unsigned long *showing_desktop = NULL; + gboolean name_is_utf8 = TRUE; + gchar *name_out; + gchar *class_out; + + if (! (sup_window = (Window *)get_property(disp, DefaultRootWindow(disp), + XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL))) { if (! (sup_window = (Window *)get_property(disp, DefaultRootWindow(disp), - XA_WINDOW, "_NET_SUPPORTING_WM_CHECK", NULL))) { - if (! (sup_window = (Window *)get_property(disp, DefaultRootWindow(disp), - XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) { - fputs("Cannot get window manager info properties.\n" - "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr); - return EXIT_FAILURE; - } + XA_CARDINAL, "_WIN_SUPPORTING_WM_CHECK", NULL))) { + fputs("Cannot get window manager info properties.\n" + "(_NET_SUPPORTING_WM_CHECK or _WIN_SUPPORTING_WM_CHECK)\n", stderr); + return EXIT_FAILURE; } + } - /* WM_NAME */ + /* WM_NAME */ + if (! (wm_name = get_property(disp, *sup_window, + XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) { + name_is_utf8 = FALSE; if (! (wm_name = get_property(disp, *sup_window, - XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL))) { - name_is_utf8 = FALSE; - if (! (wm_name = get_property(disp, *sup_window, - XA_STRING, "_NET_WM_NAME", NULL))) { - p_verbose("Cannot get name of the window manager (_NET_WM_NAME).\n"); - } + XA_STRING, "_NET_WM_NAME", NULL))) { + p_verbose("Cannot get name of the window manager (_NET_WM_NAME).\n"); } - name_out = get_output_str(wm_name, name_is_utf8); - - /* WM_CLASS */ - if (! (wm_class = get_property(disp, *sup_window, - XInternAtom(disp, "UTF8_STRING", False), "WM_CLASS", NULL))) { - name_is_utf8 = FALSE; - if (! (wm_class = get_property(disp, *sup_window, - XA_STRING, "WM_CLASS", NULL))) { - p_verbose("Cannot get class of the window manager (WM_CLASS).\n"); - } - } - class_out = get_output_str(wm_class, name_is_utf8); - + } + name_out = get_output_str(wm_name, name_is_utf8); - /* WM_PID */ - if (! (wm_pid = (unsigned long *)get_property(disp, *sup_window, - XA_CARDINAL, "_NET_WM_PID", NULL))) { - p_verbose("Cannot get pid of the window manager (_NET_WM_PID).\n"); - } - - /* _NET_SHOWING_DESKTOP */ - if (! (showing_desktop = (unsigned long *)get_property(disp, DefaultRootWindow(disp), - XA_CARDINAL, "_NET_SHOWING_DESKTOP", NULL))) { - p_verbose("Cannot get the _NET_SHOWING_DESKTOP property.\n"); - } - - /* print out the info */ - printf("Name: %s\n", name_out ? name_out : "N/A"); - printf("Class: %s\n", class_out ? class_out : "N/A"); - - if (wm_pid) { - printf("PID: %lu\n", *wm_pid); - } - else { - printf("PID: N/A\n"); - } - - if (showing_desktop) { - printf("Window manager's \"showing the desktop\" mode: %s\n", - *showing_desktop == 1 ? "ON" : "OFF"); - } - else { - printf("Window manager's \"showing the desktop\" mode: N/A\n"); - } - - g_free(name_out); - g_free(sup_window); - g_free(wm_name); - g_free(wm_class); - g_free(wm_pid); - g_free(showing_desktop); - - return EXIT_SUCCESS; + /* WM_CLASS */ + if (! (wm_class = get_property(disp, *sup_window, + XInternAtom(disp, "UTF8_STRING", False), "WM_CLASS", NULL))) { + name_is_utf8 = FALSE; + if (! (wm_class = get_property(disp, *sup_window, + XA_STRING, "WM_CLASS", NULL))) { + p_verbose("Cannot get class of the window manager (WM_CLASS).\n"); + } + } + class_out = get_output_str(wm_class, name_is_utf8); + + + /* WM_PID */ + if (! (wm_pid = (unsigned long *)get_property(disp, *sup_window, + XA_CARDINAL, "_NET_WM_PID", NULL))) { + p_verbose("Cannot get pid of the window manager (_NET_WM_PID).\n"); + } + + /* _NET_SHOWING_DESKTOP */ + if (! (showing_desktop = (unsigned long *)get_property(disp, DefaultRootWindow(disp), + XA_CARDINAL, "_NET_SHOWING_DESKTOP", NULL))) { + p_verbose("Cannot get the _NET_SHOWING_DESKTOP property.\n"); + } + + /* print out the info */ + printf("Name: %s\n", name_out ? name_out : "N/A"); + printf("Class: %s\n", class_out ? class_out : "N/A"); + + if (wm_pid) { + printf("PID: %lu\n", *wm_pid); + } + else { + printf("PID: N/A\n"); + } + + if (showing_desktop) { + printf("Window manager's \"showing the desktop\" mode: %s\n", + *showing_desktop == 1 ? "ON" : "OFF"); + } + else { + printf("Window manager's \"showing the desktop\" mode: N/A\n"); + } + + g_free(name_out); + g_free(sup_window); + g_free(wm_name); + g_free(wm_class); + g_free(wm_pid); + g_free(showing_desktop); + + return EXIT_SUCCESS; }/*}}}*/ static int showing_desktop (Display *disp) {/*{{{*/ - unsigned long state; - - if (strcmp(options.param, "on") == 0) { - state = 1; - } - else if (strcmp(options.param, "off") == 0) { - state = 0; - } - else { - fputs("The argument to the -k option must be either \"on\" or \"off\"\n", stderr); - return EXIT_FAILURE; - } - - return client_msg(disp, DefaultRootWindow(disp), "_NET_SHOWING_DESKTOP", - state, 0, 0, 0, 0); + unsigned long state; + + if (strcmp(options.param, "on") == 0) { + state = 1; + } + else if (strcmp(options.param, "off") == 0) { + state = 0; + } + else { + fputs("The argument to the -k option must be either \"on\" or \"off\"\n", stderr); + return EXIT_FAILURE; + } + + return client_msg(disp, DefaultRootWindow(disp), "_NET_SHOWING_DESKTOP", + state, 0, 0, 0, 0); }/*}}}*/ static int change_viewport (Display *disp) {/*{{{*/ - unsigned long x, y; - const char *argerr = "The -o option expects two integers separated with a comma.\n"; - - if (sscanf(options.param, "%lu,%lu", &x, &y) == 2) { - return client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_VIEWPORT", - x, y, 0, 0, 0); - } - else { - fputs(argerr, stderr); - return EXIT_FAILURE; - } + unsigned long x, y; + const char *argerr = "The -o option expects two integers separated with a comma.\n"; + + if (sscanf(options.param, "%lu,%lu", &x, &y) == 2) { + return client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_VIEWPORT", + x, y, 0, 0, 0); + } + else { + fputs(argerr, stderr); + return EXIT_FAILURE; + } }/*}}}*/ static int change_geometry (Display *disp) {/*{{{*/ - unsigned long x, y; - const char *argerr = "The -g option expects two integers separated with a comma.\n"; - - if (sscanf(options.param, "%lu,%lu", &x, &y) == 2) { - return client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_GEOMETRY", - x, y, 0, 0, 0); - } - else { - fputs(argerr, stderr); - return EXIT_FAILURE; - } + unsigned long x, y; + const char *argerr = "The -g option expects two integers separated with a comma.\n"; + + if (sscanf(options.param, "%lu,%lu", &x, &y) == 2) { + return client_msg(disp, DefaultRootWindow(disp), "_NET_DESKTOP_GEOMETRY", + x, y, 0, 0, 0); + } + else { + fputs(argerr, stderr); + return EXIT_FAILURE; + } }/*}}}*/ static int change_number_of_desktops (Display *disp) {/*{{{*/ - unsigned long n; + unsigned long n; - if (sscanf(options.param, "%lu", &n) != 1) { - fputs("The -n option expects an integer.\n", stderr); - return EXIT_FAILURE; - } - - return client_msg(disp, DefaultRootWindow(disp), "_NET_NUMBER_OF_DESKTOPS", - n, 0, 0, 0, 0); + if (sscanf(options.param, "%lu", &n) != 1) { + fputs("The -n option expects an integer.\n", stderr); + return EXIT_FAILURE; + } + + return client_msg(disp, DefaultRootWindow(disp), "_NET_NUMBER_OF_DESKTOPS", + n, 0, 0, 0, 0); }/*}}}*/ static int switch_desktop (Display *disp) {/*{{{*/ - int target = -1; - - target = atoi(options.param); - if (target == -1) { - fputs("Invalid desktop ID.\n", stderr); - return EXIT_FAILURE; - } - - return client_msg(disp, DefaultRootWindow(disp), "_NET_CURRENT_DESKTOP", - (unsigned long)target, 0, 0, 0, 0); + int target = -1; + + target = atoi(options.param); + if (target == -1) { + fputs("Invalid desktop ID.\n", stderr); + return EXIT_FAILURE; + } + + return client_msg(disp, DefaultRootWindow(disp), "_NET_CURRENT_DESKTOP", + (unsigned long)target, 0, 0, 0, 0); }/*}}}*/ static void window_set_title (Display *disp, Window win, /* {{{ */ - char *title, char mode) { - gchar *title_utf8; - gchar *title_local; - - if (envir_utf8) { - title_utf8 = g_strdup(title); - title_local = NULL; + char *title, char mode) { + gchar *title_utf8; + gchar *title_local; + + if (envir_utf8) { + title_utf8 = g_strdup(title); + title_local = NULL; + } + else { + if (! (title_utf8 = g_locale_to_utf8(title, -1, NULL, NULL, NULL))) { + title_utf8 = g_strdup(title); + } + title_local = g_strdup(title); + } + + if (mode == 'T' || mode == 'N') { + /* set name */ + if (title_local) { + XChangeProperty(disp, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace, + (guchar *) title_local, strlen(title_local)); } else { - if (! (title_utf8 = g_locale_to_utf8(title, -1, NULL, NULL, NULL))) { - title_utf8 = g_strdup(title); - } - title_local = g_strdup(title); - } - - if (mode == 'T' || mode == 'N') { - /* set name */ - if (title_local) { - XChangeProperty(disp, win, XA_WM_NAME, XA_STRING, 8, PropModeReplace, - title_local, strlen(title_local)); - } - else { - XDeleteProperty(disp, win, XA_WM_NAME); - } - XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_NAME", False), - XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace, - title_utf8, strlen(title_utf8)); + XDeleteProperty(disp, win, XA_WM_NAME); } + XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_NAME", False), + XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace, + (guchar *) title_utf8, strlen(title_utf8)); + } - if (mode == 'T' || mode == 'I') { - /* set icon name */ - if (title_local) { - XChangeProperty(disp, win, XA_WM_ICON_NAME, XA_STRING, 8, PropModeReplace, - title_local, strlen(title_local)); - } - else { - XDeleteProperty(disp, win, XA_WM_ICON_NAME); - } - XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_ICON_NAME", False), - XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace, - title_utf8, strlen(title_utf8)); + if (mode == 'T' || mode == 'I') { + /* set icon name */ + if (title_local) { + XChangeProperty(disp, win, XA_WM_ICON_NAME, XA_STRING, 8, PropModeReplace, + (guchar *) title_local, strlen(title_local)); } - - g_free(title_utf8); - g_free(title_local); - + else { + XDeleteProperty(disp, win, XA_WM_ICON_NAME); + } + XChangeProperty(disp, win, XInternAtom(disp, "_NET_WM_ICON_NAME", False), + XInternAtom(disp, "UTF8_STRING", False), 8, PropModeReplace, + (guchar *) title_utf8, strlen(title_utf8)); + } + + g_free(title_utf8); + g_free(title_local); + }/*}}}*/ static int window_to_desktop (Display *disp, Window win, int desktop) {/*{{{*/ - unsigned long *cur_desktop = NULL; - Window root = DefaultRootWindow(disp); - - if (desktop == -1) { - if (! (cur_desktop = (unsigned long *)get_property(disp, root, - XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) { - if (! (cur_desktop = (unsigned long *)get_property(disp, root, - XA_CARDINAL, "_WIN_WORKSPACE", NULL))) { - fputs("Cannot get current desktop properties. " - "(_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)" - "\n", stderr); - return EXIT_FAILURE; - } - } - desktop = *cur_desktop; + unsigned long *cur_desktop = NULL; + Window root = DefaultRootWindow(disp); + + if (desktop == -1) { + if (! (cur_desktop = (unsigned long *)get_property(disp, root, + XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) { + if (! (cur_desktop = (unsigned long *)get_property(disp, root, + XA_CARDINAL, "_WIN_WORKSPACE", NULL))) { + fputs("Cannot get current desktop properties. " + "(_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)" + "\n", stderr); + return EXIT_FAILURE; + } } - g_free(cur_desktop); + desktop = *cur_desktop; + } + g_free(cur_desktop); - return client_msg(disp, win, "_NET_WM_DESKTOP", (unsigned long)desktop, - 0, 0, 0, 0); + return client_msg(disp, win, "_NET_WM_DESKTOP", (unsigned long)desktop, + 0, 0, 0, 0); }/*}}}*/ static int activate_window (Display *disp, Window win, /* {{{ */ @@ -729,11 +742,42 @@ static int activate_window (Display *disp, Window win, /* {{{ */ return EXIT_SUCCESS; }/*}}}*/ +static int iconify_window (Display *disp, Window win) {/* {{{ */ + return !XIconifyWindow(disp, win, DefaultScreen(disp)); +}/*}}}*/ + static int close_window (Display *disp, Window win) {/*{{{*/ return client_msg(disp, win, "_NET_CLOSE_WINDOW", 0, 0, 0, 0, 0); }/*}}}*/ +static gchar * normalize_wm_state_name(const char * name) +{ + char * short_names[] = { + "modal", "sticky", "maximized_vert", "maximized_horz", + "shaded", "skip_taskbar", "skip_pager", "hidden", + "fullscreen", "above", "below", 0}; + + int i; + for (i = 0; short_names[i]; i++) + { + if (strcmp(short_names[i], name) == 0) + { + gchar * upcase = g_ascii_strup(name, -1); + gchar * result = g_strdup_printf("_NET_WM_STATE_%s", upcase); + g_free(upcase); + return result; + } + } + + if (strcmp("undecorated", name) == 0) + { + return g_strdup("_OB_WM_STATE_UNDECORATED"); + } + + return g_strdup(name); +} + static int window_state (Display *disp, Window win, char *arg) {/*{{{*/ unsigned long action; Atom prop1 = 0; @@ -747,8 +791,8 @@ static int window_state (Display *disp, Window win, char *arg) {/*{{{*/ } if ((p1 = strchr(arg, ','))) { - gchar *tmp_prop1, *tmp1; - + gchar *tmp_prop1; + *p1 = '\0'; /* action */ @@ -769,17 +813,16 @@ static int window_state (Display *disp, Window win, char *arg) {/*{{{*/ /* the second property */ if ((p2 = strchr(p1, ','))) { - gchar *tmp_prop2, *tmp2; + gchar *tmp_prop2; *p2 = '\0'; p2++; if (strlen(p2) == 0) { fputs("Invalid zero length property.\n", stderr); return EXIT_FAILURE; } - tmp_prop2 = g_strdup_printf("_NET_WM_STATE_%s", tmp2 = g_ascii_strup(p2, -1)); + tmp_prop2 = normalize_wm_state_name(p2); p_verbose("State 2: %s\n", tmp_prop2); prop2 = XInternAtom(disp, tmp_prop2, False); - g_free(tmp2); g_free(tmp_prop2); } @@ -788,13 +831,11 @@ static int window_state (Display *disp, Window win, char *arg) {/*{{{*/ fputs("Invalid zero length property.\n", stderr); return EXIT_FAILURE; } - tmp_prop1 = g_strdup_printf("_NET_WM_STATE_%s", tmp1 = g_ascii_strup(p1, -1)); + tmp_prop1 = normalize_wm_state_name(p1); p_verbose("State 1: %s\n", tmp_prop1); prop1 = XInternAtom(disp, tmp_prop1, False); - g_free(tmp1); g_free(tmp_prop1); - return client_msg(disp, win, "_NET_WM_STATE", action, (unsigned long)prop1, (unsigned long)prop2, 0, 0); } @@ -874,12 +915,23 @@ static int window_move_resize (Display *disp, Window win, char *arg) {/*{{{*/ } }/*}}}*/ +static int window_say_title (Display *disp, Window win) { + gchar *title_utf8 = get_window_title(disp, win); + printf("%s\n", title_utf8); + g_free(title_utf8); + return EXIT_SUCCESS; +} + static int action_window (Display *disp, Window win, char mode) {/*{{{*/ + int rv; p_verbose("Using window: 0x%.8lx\n", win); switch (mode) { case 'a': return activate_window(disp, win, TRUE); + case 'Y': + return iconify_window(disp, win); + case 'c': return close_window(disp, win); @@ -887,6 +939,12 @@ static int action_window (Display *disp, Window win, char mode) {/*{{{*/ /* resize/move the window around the desktop => -r -e */ return window_move_resize(disp, win, options.param); + case 'y': + /* resize/move the window, then activate it */ + rv = window_move_resize(disp, win, options.param); + activate_window(disp, win, TRUE); + return rv; + case 'b': /* change state of a window => -r -b */ return window_state(disp, win, options.param); @@ -910,6 +968,12 @@ static int action_window (Display *disp, Window win, char mode) {/*{{{*/ window_set_title(disp, win, options.param, mode); return EXIT_SUCCESS; + case 'z': + // iconify + return XLowerWindow(disp, win); + case 'E': + return window_say_title(disp, win); + default: fprintf(stderr, "Unknown action: '%c'\n", mode); return EXIT_FAILURE; @@ -1015,6 +1079,25 @@ static int action_window_str (Display *disp, char mode) {/*{{{*/ } }/*}}}*/ + +static int list_current_desktop (Display *disp) {/*{{{*/ + unsigned long *cur_desktop = NULL; + Window root = DefaultRootWindow(disp); + if (! (cur_desktop = (unsigned long *)get_property(disp, root, + XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) { + if (! (cur_desktop = (unsigned long *)get_property(disp, root, + XA_CARDINAL, "_WIN_WORKSPACE", NULL))) { + fputs("Cannot get current desktop properties. " + "(_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)" + "\n", stderr); + g_free(cur_desktop); + return EXIT_FAILURE; + } + } + printf("%-2d\n", *((int *) cur_desktop)); + return EXIT_SUCCESS; +} + static int list_desktops (Display *disp) {/*{{{*/ unsigned long *num_desktops = NULL; unsigned long *cur_desktop = NULL; @@ -1259,19 +1342,28 @@ static int longest_str (gchar **strv) {/*{{{*/ }/*}}}*/ static Window *get_client_list (Display *disp, unsigned long *size) {/*{{{*/ - Window *client_list; + Window *client_list = NULL; + char * msg = NULL; - if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp), - XA_WINDOW, "_NET_CLIENT_LIST", size)) == NULL) { - if ((client_list = (Window *)get_property(disp, DefaultRootWindow(disp), - XA_CARDINAL, "_WIN_CLIENT_LIST", size)) == NULL) { - fputs("Cannot get client list properties. \n" - "(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)" - "\n", stderr); - return NULL; - } + if (options.stacking_order) + { + msg = "_NET_CLIENT_LIST_STACKING"; + client_list = (Window *) get_property(disp, DefaultRootWindow(disp), + XA_WINDOW, "_NET_CLIENT_LIST_STACKING", size); + } + else + { + msg = "_NET_CLIENT_LIST or _WIN_CLIENT_LIST"; + client_list = (Window *)get_property(disp, DefaultRootWindow(disp), + XA_WINDOW, "_NET_CLIENT_LIST", size); + if (!client_list) + client_list = (Window *)get_property(disp, DefaultRootWindow(disp), + XA_CARDINAL, "_WIN_CLIENT_LIST", size); } + if (!client_list) + fprintf(stderr, "Cannot get client list properties.\n(%s)\n", msg); + return client_list; }/*}}}*/ @@ -1425,6 +1517,16 @@ static gchar *get_property (Display *disp, Window win, /*{{{*/ * * long_length = Specifies the length in 32-bit multiples of the * data to be retrieved. + * + * NOTE: see + * http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html + * In particular: + * + * When the X window system was ported to 64-bit architectures, a + * rather peculiar design decision was made. 32-bit quantities such + * as Window IDs, atoms, etc, were kept as longs in the client side + * APIs, even when long was changed to 64 bits. + * */ if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False, xa_prop_type, &xa_ret_type, &ret_format, @@ -1440,7 +1542,10 @@ static gchar *get_property (Display *disp, Window win, /*{{{*/ } /* null terminate the result to make string handling easier */ + tmp_size = (ret_format / 8) * ret_nitems; + /* Correct 64 Architecture implementation of 32 bit data */ + if(ret_format==32) tmp_size *= sizeof(long)/4; ret = g_malloc(tmp_size + 1); memcpy(ret, ret_prop, tmp_size); ret[tmp_size] = '\0'; diff --git a/wmctrl.1 b/wmctrl.1 index 1a5dfc6..59d57dd 100644 --- a/wmctrl.1 +++ b/wmctrl.1 @@ -71,6 +71,12 @@ Close the window .I gracefully. +.TP +.BI \-Y " " +Iconify the window +.I +\[char46] + .TP .B \-d List all desktops managed by the window manager. One line is output @@ -219,6 +225,10 @@ Include geometry information in the output of the .B \-l action. +.TP +.B \-S +List windows in stacking order (bottom to top). + .TP .B \-i Interpret window arguments