/* winspew - a EWMH testing tool. Copyright 2003 Matthew Allum SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #define MAX_WINS 32 #define WIN_OPTS_NO_TITLE (1<<1) #define WIN_OPTS_STATE_FULLSCREEN (1<<2) #define WIN_OPTS_STATE_MODAL (1<<3) #define WIN_OPTS_WM_PROTO_ACCEPT (1<<5) #define WIN_OPTS_WM_PROTO_HELP (1<<6) /* XXX possibles */ #define WIN_OPTS_UTF8_TITLE (1<<7) #define WIN_OPTS_NO_DECOR (1<<8) #define WIN_OPTS_WM_PROTO_CUSTOM (1<<9) #define WIN_OPTS_CM_TRANS (1<<10) #define WIN_OPTS_URGENT (1<<12) #define WIN_OPTS_STATE_ABOVE (1<<13) enum { /* Window Atoms */ WINDOW_TYPE_TOOLBAR = 1, WINDOW_TYPE_DOCK, WINDOW_TYPE_DIALOG, WINDOW_TYPE_SPLASH, WINDOW_TYPE_DESKTOP, WINDOW_TYPE, WINDOW_STATE, WINDOW_STATE_FULLSCREEN, WINDOW_STATE_MODAL, WINDOW_STATE_ABOVE, _NET_CLOSE_WINDOW, _NET_WM_NAME, UTF8_STRING, _NET_SHOW_DESKTOP, _NET_WM_ICON, _MOTIF_WM_HINTS, WM_PROTOCOLS, WM_DELETE_WINDOW, _NET_WM_CONTEXT_HELP, _NET_WM_CONTEXT_ACCEPT, _NET_WM_CONTEXT_CUSTOM, _NET_SUPPORTED, CM_TRANSLUCENCY, ATOM_COUNT }; static void paint(Window win, int width, int height); static Display* dpy; static Window win_top_level[MAX_WINS], win_child[MAX_WINS], win_current_top_level, win_root; int win_top_level_idx = 0, win_child_idx = 0; static int screen; static Atom atoms[ATOM_COUNT]; static Bool WinIsFullscreen = False; static Bool WantPattern = True; static int WinTranslucency = -1; static struct { char *key; int flag; char *desc; } Options_lookup[] = { { "no_title", WIN_OPTS_NO_TITLE, "Request no titlebar / border only\n"}, { "no_decor", WIN_OPTS_NO_DECOR, "Request no window decorations\n"}, { "fullscreen", WIN_OPTS_STATE_FULLSCREEN , "Request Fullscreen\n"}, { "modal", WIN_OPTS_STATE_MODAL, "Request dialog be modal\n"}, { "help_button", WIN_OPTS_WM_PROTO_HELP, "Request help button in window title bar\n"}, { "accept_button", WIN_OPTS_WM_PROTO_ACCEPT, "Request 'OK' button in window title bar\n"}, { "custom_button", WIN_OPTS_WM_PROTO_CUSTOM, "Request 'custom' button in window title bar\n"}, { "urgent", WIN_OPTS_URGENT, "Set Urgency flag on window\n" }, { "above", WIN_OPTS_STATE_ABOVE, "Set above state hint on window\n" }, { NULL, 0, NULL } }; static struct { char *key; unsigned char *data; int len; } UTF8_Names_lookup[] = { { "zh_CN.utf8", "\344\275\240\345\245\275\344\270\255\345\233\275!", 13 }, { "ar_AE.utf8", "Arabic ( \357\273\262\357\272\221\357\272\256\357\273\213 )", 23 }, { "mixed.utf8", "Hello from Arabic ( \357\273\262\357\272\221\357\272\256\357\273\213 ) and now from china : \344\275\240\345\245\275\344\270\255\345\233\275! clip this clip this clip this clip this clip this clip this clip this", 139 } , /* { "utf8-ar", "\357\273\262\357\272\221\357\272\256\357\273\213", 12 }, */ { NULL, NULL, 0 } }; void wm_check_supported_features(void) { /* XXX This functioniality here could be greatly enhances, though Im not sure how useful it would be. */ Atom type, *atoms_supported = NULL; int format; long bytes_after; long n_items; int result; unsigned char *atom_str_name; int i; Bool have_help_support = False, have_accept_support = False; result = XGetWindowProperty (dpy, win_root, atoms[_NET_SUPPORTED], 0, 1024L, False, XA_ATOM, &type, &format, &n_items, &bytes_after, (unsigned char **)&atoms_supported); printf("winspew log: Checking wm features..\n"); if (result != Success || atoms_supported == NULL) { if (atoms_supported) XFree (atoms_supported); printf("winspew log: *WARNING* looks like wm is not EWMH compliant\n" " Many features _will_not_ work!"); return; } for (i=0; i ] [ -nopaint] -top [ -dialog ] ...\n\n" "-top, ( -t ) creates a top level window, should consist of;\n\n" " ,<window type>[,[geom],[extra opts]]\n\n" "-dialog, ( -c) creates a dialog for the previous top level, <options> are;\n\n" " <title>[,[geom],[extra opts],]\n\n" " 'window type' is one of normal, dock, toolbar, dialog, splash, desktop, message \n" " 'geom' is a X11 window geometry definition, like 100x100+100+100\n" " 'extra opts' is a ':' seperated list of the following flags;\n\n", bin_name, bin_name); while( Options_lookup[i].key != NULL ) { printf(" %s - %s", Options_lookup[i].key, Options_lookup[i].desc); i++; } printf(" trans=XX - Sets window translucency ( if supported ) 'XX' is 0 - 255." ); printf("\nNote, you may use as many instances of -top and -dialog as you like.\n"); if (error) { printf("\nError: %s\n", error); } exit(1); } void set_motif_no_title_decoration_hint(Window win, Bool border_only) { #define PROP_MOTIF_WM_HINTS_ELEMENTS 5 #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_DECOR_BORDER (1L << 1) typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; long inputMode; unsigned long status; } PropMotifWmHints; PropMotifWmHints *hints; hints = malloc(sizeof(PropMotifWmHints)); memset(hints, 0, sizeof(PropMotifWmHints)); hints->flags = MWM_HINTS_DECORATIONS; if (border_only) hints->decorations = MWM_DECOR_BORDER; else hints->decorations = 0; XChangeProperty(dpy, win, atoms[_MOTIF_WM_HINTS], XA_ATOM, 32, PropModeReplace, (unsigned char *)hints, PROP_MOTIF_WM_HINTS_ELEMENTS); free(hints); return; } int parse_options(const char *options_spec) { char *p = NULL; int i = 0, result_flags = 0; Bool have_trans = False; printf("parsing %s\n", options_spec); while( Options_lookup[i].key != NULL ) { /* Grab translucent key/val if available */ if (!have_trans && (p = strstr(options_spec, "trans=")) != NULL) { char *val = NULL; val = strdup(p+6); p = val; while (*p != ',' && *p != '\0') p++; *p = '\0'; have_trans = True; result_flags |= WIN_OPTS_CM_TRANS; WinTranslucency = atoi(val); free(val); } if (strstr(options_spec, Options_lookup[i].key) != NULL) { printf("parsed %s\n", Options_lookup[i].key); result_flags |= Options_lookup[i].flag; } i++; } return result_flags; } void win_set_utf8_name(Window win, unsigned char *data, int data_len) { XChangeProperty(dpy, win, atoms[_NET_WM_NAME], atoms[UTF8_STRING], 8, PropModeReplace, data, data_len); } void win_set_tranlucency(Window win, int level) { XChangeProperty(dpy, win, atoms[CM_TRANSLUCENCY], XA_INTEGER, 32, PropModeReplace, (unsigned char *) &level, 1L); } void win_set_standard_props(Window win, char *win_title, int x, int y, int h, int w) { XSizeHints size_hints; size_hints.flags = PPosition | PSize | PMinSize; size_hints.x = x; size_hints.y = y; size_hints.width = h; size_hints.height = w; size_hints.min_width = w; size_hints.min_height = h; XSetStandardProperties(dpy, win, win_title, win_title, None, NULL, 0, &size_hints); } void win_set_ewmh_type(Window win, Atom atom_type) { printf("winspew log: Setting type\n"); XChangeProperty(dpy, win, atoms[WINDOW_TYPE], XA_ATOM, 32, PropModeReplace, (unsigned char *) &atom_type, 1); } void win_set_extened_options(Window win, int option_flags, int message_timeout) { Atom wm_protocols[3], wm_states[3]; int wm_protocols_idx = 1, wm_states_idx = 0; wm_protocols[0] = atoms[WM_DELETE_WINDOW]; if (option_flags & WIN_OPTS_NO_TITLE) { printf("winspew log: Setting no decor hint\n"); set_motif_no_title_decoration_hint(win, True); } if (option_flags & WIN_OPTS_NO_DECOR) { printf("winspew log: Setting no decor hint\n"); set_motif_no_title_decoration_hint(win, False); } if (option_flags & WIN_OPTS_STATE_FULLSCREEN) { printf("winspew log: Setting fullscreen hint\n"); wm_states[wm_states_idx] = atoms[WINDOW_STATE_FULLSCREEN]; wm_states_idx++; WinIsFullscreen = True; } if (option_flags & WIN_OPTS_CM_TRANS) { printf("winspew log: Setting window translucency to %i\n", WinTranslucency); win_set_tranlucency(win, WinTranslucency); } if (option_flags & WIN_OPTS_STATE_MODAL) { printf("winspew log: Setting modal hint\n"); wm_states[wm_states_idx] = atoms[WINDOW_STATE_MODAL]; wm_states_idx++; } if (option_flags & WIN_OPTS_STATE_ABOVE) { printf("winspew log: Setting state above hint\n"); wm_states[wm_states_idx] = atoms[WINDOW_STATE_ABOVE]; wm_states_idx++; } if (wm_states_idx) { XChangeProperty(dpy, win, atoms[WINDOW_STATE], XA_ATOM, 32, PropModeReplace, (unsigned char *)wm_states, wm_states_idx); } if (option_flags & WIN_OPTS_URGENT) { XWMHints *wmhints; wmhints = XAllocWMHints(); wmhints->flags = XUrgencyHint; printf("winspew log: Setting urgency hint\n"); XSetWMHints(dpy, win, wmhints); XFree(wmhints); } if (option_flags & WIN_OPTS_WM_PROTO_ACCEPT) { printf("winspew log: Setting accept protocol\n"); wm_protocols[wm_protocols_idx] = atoms[_NET_WM_CONTEXT_ACCEPT]; wm_protocols_idx++; } if (option_flags & WIN_OPTS_WM_PROTO_HELP) { printf("winspew log: Setting help protocol\n"); wm_protocols[wm_protocols_idx] = atoms[_NET_WM_CONTEXT_HELP]; wm_protocols_idx++; } if (option_flags & WIN_OPTS_WM_PROTO_CUSTOM) { printf("winspew log: Setting help protocol\n"); wm_protocols[wm_protocols_idx] = atoms[_NET_WM_CONTEXT_CUSTOM]; wm_protocols_idx++; } if (wm_protocols_idx) XSetWMProtocols(dpy, win, wm_protocols, wm_protocols_idx ); } void win_realize(Window win) { XSelectInput(dpy, win, KeyPressMask |KeyReleaseMask |ButtonPressMask |ButtonReleaseMask |EnterWindowMask |LeaveWindowMask |PointerMotionMask |PointerMotionHintMask |Button1MotionMask |Button2MotionMask |Button3MotionMask |Button4MotionMask |Button5MotionMask |ButtonMotionMask |KeymapStateMask |ExposureMask |VisibilityChangeMask |StructureNotifyMask |SubstructureNotifyMask |FocusChangeMask |PropertyChangeMask |ColormapChangeMask |OwnerGrabButtonMask ); /* XXX really need all this ? */ XMapWindow(dpy, win); } void win_destroy(Window win) { Window win_tmp; int i; for (i=0; i<win_child_idx; i++) /* destroy any transient dialogs */ { if (win_child[i] != None && XGetTransientForHint(dpy, win_child[i], &win_tmp) && win_tmp == win) { XDestroyWindow(dpy, win_child[i]); win_child[i] = None; } } XDestroyWindow(dpy,win); /* Update window arrays */ for (i=0; i<win_top_level_idx; i++) if (win_top_level[i] == win) { win_top_level[i] = None; return; } for (i=0; i<win_child_idx; i++) if (win_child[i] == win) win_child[i] = None; } void create_top_level(int argc, char **argv, char *spec) { char *str; const char delim[] = ","; char *tmp; int i; char *win_title; int win_type = 0, win_x = 0, win_y = 0, win_w = 100, win_h = 50; int option_flags = 0; int message_timeout = 0; int geom_bitmask = 0; struct { char *name; int id; } win_types[] = { {"normal", 0}, {"dock", WINDOW_TYPE_DOCK}, {"dialog", WINDOW_TYPE_DIALOG}, {"splash", WINDOW_TYPE_SPLASH}, {"toolbar", WINDOW_TYPE_TOOLBAR}, {"desktop", WINDOW_TYPE_DESKTOP}, {NULL, 0} }; if ((strchr(spec, delim[0]) != NULL)) { str = strdup(spec); if ((win_title = strsep (&str, delim)) == NULL) usage(NULL, "top title missing"); if ((tmp = strsep (&str, delim)) == NULL) usage(NULL, "top type missing"); if (atoi(tmp) > 0) win_type = atoi(tmp); else { Bool found = False; i = 0; while(win_types[i].name != NULL) { if (!strcmp(tmp, win_types[i].name)) { win_type = win_types[i].id; found = True; break; } i++; } } /* Rest are optional */ if ((tmp = strsep (&str, delim)) != NULL) { geom_bitmask = XParseGeometry(tmp, &win_x, &win_y, &win_w, &win_h); if (!win_w) win_w = 100; if (!win_h) win_h = 100; /* XXX see code for dialogs if (!(geom_bitmask & XNegative)) { win_x = (-1 * DisplayWidth(dpy, screen)) + win_x; } if (!(geom_bitmask & YNegative)) { win_y = (-1 * DisplayHeight(dpy, screen)) + win_y; } */ } if ((tmp = strsep (&str, delim)) != NULL) { option_flags = parse_options(tmp); } if ((tmp = strsep (&str, delim)) != NULL) { /* This is actually now ignored */ message_timeout = atoi(tmp); fprintf(stderr, "*** Message timeout depreciated ***\n"); } free(str); /* Now create actual window */ printf("winspew log: creating top level x:%i, y:%i, w:%i, h:%i\n", win_x, win_y, win_w, win_h); win_top_level[win_top_level_idx] = XCreateSimpleWindow(dpy, win_root, win_x, win_y, win_w, win_h, 0, BlackPixel(dpy, screen), WhitePixel(dpy, screen)); printf("winspew log: window id is %li\n", win_top_level[win_top_level_idx]); win_current_top_level = win_top_level[win_top_level_idx]; win_set_standard_props(win_top_level[win_top_level_idx], win_title, win_x, win_y, win_h, win_w); if (win_type) /* something other than a normal window */ win_set_ewmh_type(win_top_level[win_top_level_idx], atoms[win_type]); win_set_extened_options(win_top_level[win_top_level_idx], option_flags, message_timeout); /* Set utf8 name from lookup */ i = 0; while( UTF8_Names_lookup[i].key != NULL ) { if (!strcmp(win_title, UTF8_Names_lookup[i].key)) { win_set_utf8_name(win_top_level[win_top_level_idx], UTF8_Names_lookup[i].data, UTF8_Names_lookup[i].len); } i++; } win_realize(win_top_level[win_top_level_idx]); win_top_level_idx++; } else { /* Failed */ usage("winspew", "top options missing"); } } void /* XXX this should be merged into above */ create_child_dialog(int argc, char **argv, char *spec) { char *str, *str_start; const char delim[] = ","; char *tmp; char *win_title; int win_x = 0, win_y = 0, win_w = 100, win_h = 50; int option_flags = 0, geom_bitmask = 0; if (spec == NULL) usage("winspew", "child options missing"); if ((strchr(spec, delim[0]) != NULL)) { str_start = str = strdup(spec); if ((win_title = strsep (&str, delim)) == NULL) usage(NULL, "child title missing"); /* Rest are optional */ if ((tmp = strsep (&str, delim)) != NULL) { geom_bitmask = XParseGeometry(tmp, &win_x, &win_y, &win_w, &win_h); if (!win_w) win_w = 100; if (!win_h) win_h = 100; /* XXX we need to do gravitys for the below to work correctly. if (!(geom_bitmask & XNegative)) { win_x = (-1 * DisplayWidth(dpy, screen)) - win_x - win_w; } if (!(geom_bitmask & YNegative)) { win_y = (-1 * DisplayHeight(dpy, screen)) - win_y - win_h; } */ } if ((tmp = strsep (&str, delim)) != NULL) { option_flags = parse_options(tmp); } free(str_start); } else win_title = spec; /* Now create actual window */ printf("winspew log: creating dialog x:%i, y:%i, w:%i, h:%i\n", win_x, win_y, win_w, win_h); win_child[win_child_idx] = XCreateSimpleWindow(dpy, win_root, win_x, win_y, win_w, win_h, 0, BlackPixel(dpy, screen), WhitePixel(dpy, screen)); printf("winspew log: window id is %li\n", win_child[win_child_idx]); XSetTransientForHint(dpy, win_child[win_child_idx], win_current_top_level); win_set_standard_props(win_child[win_child_idx], win_title, win_x, win_y, win_h, win_w); win_set_extened_options(win_child[win_child_idx], option_flags, 0); win_realize(win_child[win_child_idx]); win_child_idx++; } static void /* Fill the window with a simple pattern */ paint(Window win, int width, int height) { XWindowAttributes win_attr; XGCValues gc_values; GC gc; int x, y; Pixmap pxm_backing; XGetWindowAttributes(dpy, win, &win_attr); gc_values.graphics_exposures = False; gc_values.function = GXcopy; gc_values.background = WhitePixel(dpy, DefaultScreen(dpy)); gc = XCreateGC(dpy, win, GCGraphicsExposures|GCFunction|GCForeground, &gc_values); pxm_backing = XCreatePixmap(dpy, win, width, height, win_attr.depth); XSetForeground(dpy, gc, WhitePixel(dpy, DefaultScreen(dpy))); XFillRectangle(dpy, pxm_backing, gc, 0, 0, width, height); XSetForeground(dpy, gc, BlackPixel(dpy, DefaultScreen(dpy))); /* Quick hack for a window pattern, using window ID */ if (win % 2) { for (y = 0; y < height; y += win % 12) XDrawLine(dpy, pxm_backing, gc, 0, y, width, y); } else { for (x = 0; x < width; x += win % 12) XDrawLine(dpy, pxm_backing, gc, x, 0, x, height); } XSetWindowBackgroundPixmap(dpy, win, pxm_backing); XClearWindow(dpy, win); XFreePixmap(dpy, pxm_backing); XFreeGC(dpy, gc); } void toggle_ewmh_fullscreen (Window win_to_toggle) { #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ XEvent ev; Atom atom_win_state, atom_win_state_fullscreen; atom_win_state = XInternAtom(dpy, "_NET_WM_STATE",False); atom_win_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN",False); printf("winspew-log: attempting toggle on %li\n", win_to_toggle); memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = win_to_toggle; ev.xclient.message_type = atom_win_state; ev.xclient.format = 32; ev.xclient.data.l[1] = atom_win_state_fullscreen; ev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE; XSendEvent(dpy, win_root, False, SubstructureRedirectMask , &ev); XSync(dpy, False); } int main(int argc, char **argv) { int i, j = 1; char *dpy_name = NULL; XEvent xevent; for (i = 1; i < argc; i++) { if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) { if (++i>=argc) usage (argv[0], "display missing"); dpy_name = argv[i++]; j++; continue; } if (!strcmp ("--nopattern", argv[i]) || !strcmp ("-np", argv[i])) { j++; WantPattern = False; continue; } } if ((argc - j) == 0) usage(argv[0], "no options"); if ((dpy = XOpenDisplay(dpy_name)) == NULL) { fprintf(stderr, "Cannot connect to X server on display %s.", dpy_name); exit(1); } screen = DefaultScreen(dpy); win_root = DefaultRootWindow(dpy); create_atoms(); wm_check_supported_features(); win_current_top_level = win_root; for (i = j; i < argc; i++) { if (!strcmp ("-top", argv[i]) || !strcmp ("-t", argv[i])) { if (++i>=argc) usage (argv[0], "no -top options"); create_top_level(argc, argv, argv[i]); continue; } if (!strcmp ("-dialog", argv[i]) || !strcmp ("-c", argv[i])) { if (++i>=argc) usage (argv[0], "no -child options"); create_child_dialog(argc, argv, argv[i]); continue; } usage(argv[0], argv[i]); } while(1) { char *tmp; fflush(stdout); XNextEvent(dpy, &xevent); switch (xevent.type) { case ButtonPress: printf("winspew log: %li, Button press event\n", xevent.xbutton.window); if (WinIsFullscreen) toggle_ewmh_fullscreen (xevent.xbutton.window); // XLowerWindow(dpy, win_top_level[0]); break; case Expose: printf("winspew log: %li, Expose event\n", xevent.xexpose.window); /* paint(xevent.xexpose.window, xevent.xexpose.width, xevent.xexpose.height); */ break; case MapNotify: printf("winspew log: %li, MapNotify event\n", xevent.xmap.window); break; case UnmapNotify: printf("winspew log: %li, UnmapNotify event\n", xevent.xunmap.window); break; case ReparentNotify: printf("winspew log: %li, ReparentNotify\n", xevent.xreparent.window); break; case ConfigureNotify: printf("winspew log: %li, ConfigureNotify event\n", xevent.xconfigure.window); /* XXX compress configure notifys ? */ if (WantPattern) paint(xevent.xconfigure.window, xevent.xconfigure.width, xevent.xconfigure.height); break; case ClientMessage: printf("winspew log: %li, ClientMessage event\n", xevent.xclient.window); if ((xevent.xclient.message_type == atoms[WM_PROTOCOLS]) && (xevent.xclient.data.l[0] == atoms[WM_DELETE_WINDOW])) { printf("\tis WM_DELETE, deleting...\n"); win_destroy(xevent.xclient.window); } break; case KeyPress: break; case FocusIn: printf("winspew log: %li, FocusIn event\n", xevent.xfocus.window); break; case FocusOut: printf("winspew log: %li, FocusOut event\n", xevent.xfocus.window); break; case PropertyNotify: tmp = XGetAtomName(dpy, xevent.xproperty.atom); printf("winspew log: %li, PropertyNotify event. Atom '%s' ( ID: %li )\n", xevent.xproperty.window, tmp, xevent.xproperty.atom); if (tmp) XFree(tmp); break; default: break; } } }