/* * delwin - like xkill, but sends a WM_DELETE_WINDOW ICCCM request * instead of issuing a KillClient protocol request. * * Command-line options: * * -display DISP * As usual for X programs. * * -id ID * Requests deletion of window ID. delwin does not * perform any checks (for example, that the ID given * names a top-level client window, or that the window has * a WM_PROTOCOLS property including WM_DELETE_WINDOW). * * -focus * Requests deletion of the window which currently has * keyboard focus. (This errors if keyboard focus is set * to PointerRoot or None.) * * If neither -id nor -focus is given, delwin will grab the pointer and * expect the user to click on the relevant window (like xkill in that * respect). * * This program is in the public domain. */ /* * Include files. and are obvious. because * we want to print things under some circumstances. * because we use things from it, such as exit(). */ #include #include #include #include // Program name, for printing errors. extern const char *__progname; /* * How the window ID is specified. IDK_PICK lets the user pick one by * clicking. IDK_GIVEN corresponds to -id: the window ID is specified * by the command line. IDK_FOCUS corresponds to -focus: the window * to kill is the keyboard focus window. */ typedef enum { IDK_PICK = 1, IDK_GIVEN, IDK_FOCUS, } IDKIND; /* * Command-line values. displaystr is the -display arg. id is the -id * argument. how specifies how we're going to pick the window (see * the comment on IDKIND, above). */ static const char *displaystr = 0; static LX_XID id; static IDKIND how = IDK_PICK; // The X connection. static LX_CONN *xc; // Which screen we're using. static int scr; // The root window. static LX_XID root; // For IDK_PICK, the cursor for the pointer grab. static LX_XID curs; // For IDK_GIVEN and IDK_FOCUS, a dummy window to get a timestamp. static LX_XID tswin; // For IDK_PICK, the status of the pointer grab. static LX_GRABSTATUS grabstatus; // ATOM values for WM_PROTOCOLS and WM_DELETE_WINDOW. static LX_ATOM atom_WM_PROTOCOLS; static LX_ATOM atom_WM_DELETE_WINDOW; // For IDK_FOCUS, the focus window. static LX_XID focus; /* * Handle -id's argument. Parse the argument and make sure it's in * range, then set state correspondingly. */ static int setid(const char *s) { unsigned long int v; char *ep; v = strtol(s,&ep,0); if (ep == s) { fprintf(stderr,"%s: %s: can't find an ID there\n",__progname,s); return(1); } if (*ep) { fprintf(stderr,"%s: %s: junk after ID\n",__progname,s); return(1); } if (v > 0x1fffffff) { fprintf(stderr,"%s: %s: invalid ID (reserved bit(s) set)\n",__progname,s); return(1); } id = v; how = IDK_GIVEN; return(0); } /* * Crack the command line. See the file header comment for what we * support on the command line. */ static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: stray argument `%s'\n",__progname,*av); errs = 1; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displaystr = av[skip]; continue; } if (!strcmp(*av,"-id")) { WANTARG(); if (setid(av[skip])) errs = 1; continue; } if (!strcmp(*av,"-focus")) { how = IDK_FOCUS; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (errs) exit(1); } /* * When LX_PICK, check the pointer grab status. If it's not Success, * cough and die. * * It looks odd to do nothing in the Success case. The next action * after this is not ours; it is the user clicking, which comes in as * a ButtonPress event. */ static void grab_done(void *arg __attribute__((__unused__))) { const char *why; switch (grabstatus) { case LX_GRABSTATUS_Success: break; case LX_GRABSTATUS_InvalidTime: why = "CurrentTime is invalid"; if (0) { case LX_GRABSTATUS_NotViewable: why = "root isn't viewable"; } fprintf(stderr,"%s: impossible grab failure: %s\n",__progname,why); exit(1); break; case LX_GRABSTATUS_AlreadyGrabbed: why = "already grabbed"; if (0) { case LX_GRABSTATUS_Frozen: why = "frozen"; } fprintf(stderr,"%s: grab failed: pointer %s by some other client\n",__progname,why); exit(1); break; default: fprintf(stderr,"%s: grab failed for incomprehensible reason %#lx\n",__progname,(unsigned long int)grabstatus); exit(1); break; } } /* * Called (via lx_op_callback on the GetInputFocus's LX_OP) once the * WM_DELETE_WINDOW message is sent. At this point we have nothing * more to do. */ static void send_done(void *arg __attribute__((__unused__))) { exit(0); } /* * This is called once we know what window ID we're going to request * deletion of and have the timestamp for doing so. We just construct * a ClientMessage and SendEvent it, then do a GetInputFocus to let us * know once it's happened. */ static void send_delete_window(LX_XID w, LX_TIME t) { LX_EVENT e; e.type = LX_EV_ClientMessage; e.send_event = 1; e.u.ClientMessage.format = 32; e.u.ClientMessage.seq = 0; e.u.ClientMessage.window = w; e.u.ClientMessage.type = atom_WM_PROTOCOLS; e.u.ClientMessage.u.data32[0] = atom_WM_DELETE_WINDOW; e.u.ClientMessage.u.data32[1] = t; e.u.ClientMessage.u.data32[2] = 0; e.u.ClientMessage.u.data32[3] = 0; e.u.ClientMessage.u.data32[4] = 0; lx_SendEvent(xc,w,0,0,&e); lx_op_callback(lx_GetInputFocus(xc,0,0),&send_done,0,0); } /* * When doing IDK_PICK, the user's choice of window comes in as a * ButtonPress event. This provides the window (in the form of the * child window, since, because of the grab, the event window is the * root) and, conveniently, a timestamp. */ static void button_press(const LX_EVENT_ButtonPress *e) { if (how != IDK_PICK) return; if (e->button != 1) exit(0); send_delete_window(e->childw,e->time); } /* * We got the PropertyNotify event talked about in the comment on * get_tstamp(). (The reason we return instead of aborting when * IDK_PICK is that I'm not quite certain that's a can't-happen.) */ static void property_notify(const LX_EVENT_PropertyNotify *e) { if (how == IDK_PICK) return; send_delete_window(id,e->time); } /* * Our event handler. We care about only two events: ButtonPress * when doing IDK_PICK (the user's click) and PropertyNotify when * doing IDK_GIVEN or IDK_FOCUS (see the comment on get_tstamp). */ static void handle_event(LX_CONN *conn, const LX_EVENT *e) { if (conn != xc) abort(); switch (e->type) { case LX_EV_ButtonPress: button_press(&e->u.ButtonPress); break; case LX_EV_PropertyNotify: property_notify(&e->u.PropertyNotify); break; } } /* * For IDK_FOCUS and IDK_GIVEN, we need a timestamp for the * ClientMessage event. Since, in these cases, we don't have any * other way to get one, we create a `dummy' window for the purpose * and set a zero-length property on it, taking the timestamp from the * resulting PropertyNotify event. */ static void get_tstamp(void) { tswin = lx_CreateWindow_va(xc,root,0,0,1,1,0,0,LX_WCLASS_InputOnly,LX_VISUAL_CopyFromParent, LX_CWV_EventMask(LX_EM_PropertyChange),LX_CWV_END); lx_ChangeProperty(xc,tswin,LX_ATOM_STRING,LX_ATOM_STRING,8,LX_PROPERTYMODE_Replace,0,0); } /* * When doing IDK_FOCUS, this is called once the GetInputFocus * completes. We just check for the three possible non-window focus * values, in which case we cough and die; otherwise, we save the * window and get a timestamp. Doing the request is driven off * getting the timestamp. */ static void got_focus(void *arg __attribute__((__unused__))) { switch (focus) { case LX_FOCUS_None: fprintf(stderr,"%s: focus is None\n",__progname); exit(0); break; case LX_FOCUS_PointerRoot: fprintf(stderr,"%s: focus is PointerRoot\n",__progname); exit(0); break; case LX_FOCUS_Parent: fprintf(stderr,"%s: focus is Parent (?""?)\n",__progname); exit(0); break; } id = focus; get_tstamp(); } /* * Called when the X open completes. We just set up a few things and * switch out based on what kind of operation we've been told to do. * * The reason we lx_op_drop the InternAtom ops is that they "can't * fail" and we don't care about completion, as long as the results * are there by the time we use them. Each possible codepath does at * least one server roundtrip before sending the ClientMessage, so we * need no notification of completion - they must be done by the time * control flow reaches the point where they need to be done. (For * IDK_PICK, the pointer grab forces a roundtrip; for IDK_GIVEN, the * timestamp fetch; fpr IDK_FOCUS, the GetInputFocus is enough, but * there's the timestamp fetch in that case too.) */ static void open_done(LX_CONN *conn, void *arg __attribute__((__unused__))) { xc = conn; lx_set_event_handler(xc,&handle_event); scr = lx_default_screen(xc); root = lx_root(xc,scr); lx_op_drop(lx_InternAtom(xc,0,"WM_PROTOCOLS",-1,&atom_WM_PROTOCOLS)); lx_op_drop(lx_InternAtom(xc,0,"WM_DELETE_WINDOW",-1,&atom_WM_DELETE_WINDOW)); switch (how) { case IDK_PICK: curs = lx_font_cursor(xc,LX_FONT_CURSOR_cross,65535,65535,65535,0,0,0); lx_op_callback(lx_GrabPointer( xc,root,0,LX_EM_ButtonPress, LX_GRABMODE_Asynchronous, LX_GRABMODE_Asynchronous, root,curs,LX_TIME_CurrentTime,&grabstatus),&grab_done,0,0); break; case IDK_GIVEN: get_tstamp(); break; case IDK_FOCUS: lx_op_callback(lx_GetInputFocus(xc,&focus,0),&got_focus,0,0); break; } } /* * Just crack the arglist, init libaio, open the display, and drop into * the event loop. Everything else happens in cascade from the X open * succeeding. */ int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); aio_poll_init(); lx_open(displaystr,0,&open_done,0,0,0); aio_event_loop(); return(0); }