tabbed.c - tabbed - tab interface for application supporting Xembed | |
git clone git://git.suckless.org/tabbed | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tabbed.c (30256B) | |
--- | |
1 /* | |
2 * See LICENSE file for copyright and license details. | |
3 */ | |
4 | |
5 #include <sys/wait.h> | |
6 #include <locale.h> | |
7 #include <signal.h> | |
8 #include <stdarg.h> | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <string.h> | |
12 #include <unistd.h> | |
13 #include <X11/Xatom.h> | |
14 #include <X11/keysym.h> | |
15 #include <X11/Xlib.h> | |
16 #include <X11/Xproto.h> | |
17 #include <X11/Xutil.h> | |
18 #include <X11/XKBlib.h> | |
19 #include <X11/Xft/Xft.h> | |
20 | |
21 #include "arg.h" | |
22 | |
23 /* XEMBED messages */ | |
24 #define XEMBED_EMBEDDED_NOTIFY 0 | |
25 #define XEMBED_WINDOW_ACTIVATE 1 | |
26 #define XEMBED_WINDOW_DEACTIVATE 2 | |
27 #define XEMBED_REQUEST_FOCUS 3 | |
28 #define XEMBED_FOCUS_IN 4 | |
29 #define XEMBED_FOCUS_OUT 5 | |
30 #define XEMBED_FOCUS_NEXT 6 | |
31 #define XEMBED_FOCUS_PREV 7 | |
32 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ | |
33 #define XEMBED_MODALITY_ON 10 | |
34 #define XEMBED_MODALITY_OFF 11 | |
35 #define XEMBED_REGISTER_ACCELERATOR 12 | |
36 #define XEMBED_UNREGISTER_ACCELERATOR 13 | |
37 #define XEMBED_ACTIVATE_ACCELERATOR 14 | |
38 | |
39 /* Details for XEMBED_FOCUS_IN: */ | |
40 #define XEMBED_FOCUS_CURRENT 0 | |
41 #define XEMBED_FOCUS_FIRST 1 | |
42 #define XEMBED_FOCUS_LAST 2 | |
43 | |
44 /* Macros */ | |
45 #define MAX(a, b) ((a) > (b) ? (a) : (b)) | |
46 #define MIN(a, b) ((a) < (b) ? (a) : (b)) | |
47 #define LENGTH(x) (sizeof((x)) / sizeof(*(x))) | |
48 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) | |
49 #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) | |
50 | |
51 enum { ColFG, ColBG, ColLast }; /* color */ | |
52 enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, | |
53 XEmbed, WMSelectTab, WMLast }; /* default atoms */ | |
54 | |
55 typedef union { | |
56 int i; | |
57 const void *v; | |
58 } Arg; | |
59 | |
60 typedef struct { | |
61 unsigned int mod; | |
62 KeySym keysym; | |
63 void (*func)(const Arg *); | |
64 const Arg arg; | |
65 } Key; | |
66 | |
67 typedef struct { | |
68 int x, y, w, h; | |
69 XftColor norm[ColLast]; | |
70 XftColor sel[ColLast]; | |
71 XftColor urg[ColLast]; | |
72 Drawable drawable; | |
73 GC gc; | |
74 struct { | |
75 int ascent; | |
76 int descent; | |
77 int height; | |
78 XftFont *xfont; | |
79 } font; | |
80 } DC; /* draw context */ | |
81 | |
82 typedef struct { | |
83 char name[256]; | |
84 Window win; | |
85 int tabx; | |
86 Bool urgent; | |
87 Bool closed; | |
88 } Client; | |
89 | |
90 /* function declarations */ | |
91 static void buttonpress(const XEvent *e); | |
92 static void cleanup(void); | |
93 static void clientmessage(const XEvent *e); | |
94 static void configurenotify(const XEvent *e); | |
95 static void configurerequest(const XEvent *e); | |
96 static void createnotify(const XEvent *e); | |
97 static void destroynotify(const XEvent *e); | |
98 static void die(const char *errstr, ...); | |
99 static void drawbar(void); | |
100 static void drawtext(const char *text, XftColor col[ColLast]); | |
101 static void *ecalloc(size_t n, size_t size); | |
102 static void *erealloc(void *o, size_t size); | |
103 static void expose(const XEvent *e); | |
104 static void focus(int c); | |
105 static void focusin(const XEvent *e); | |
106 static void focusonce(const Arg *arg); | |
107 static void focusurgent(const Arg *arg); | |
108 static void fullscreen(const Arg *arg); | |
109 static char *getatom(int a); | |
110 static int getclient(Window w); | |
111 static XftColor getcolor(const char *colstr); | |
112 static int getfirsttab(void); | |
113 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int si… | |
114 static void initfont(const char *fontstr); | |
115 static Bool isprotodel(int c); | |
116 static void keypress(const XEvent *e); | |
117 static void killclient(const Arg *arg); | |
118 static void manage(Window win); | |
119 static void maprequest(const XEvent *e); | |
120 static void move(const Arg *arg); | |
121 static void movetab(const Arg *arg); | |
122 static void propertynotify(const XEvent *e); | |
123 static void resize(int c, int w, int h); | |
124 static void rotate(const Arg *arg); | |
125 static void run(void); | |
126 static void sendxembed(int c, long msg, long detail, long d1, long d2); | |
127 static void setcmd(int argc, char *argv[], int); | |
128 static void setup(void); | |
129 static void spawn(const Arg *arg); | |
130 static int textnw(const char *text, unsigned int len); | |
131 static void toggle(const Arg *arg); | |
132 static void unmanage(int c); | |
133 static void unmapnotify(const XEvent *e); | |
134 static void updatenumlockmask(void); | |
135 static void updatetitle(int c); | |
136 static int xerror(Display *dpy, XErrorEvent *ee); | |
137 static void xsettitle(Window w, const char *str); | |
138 | |
139 /* variables */ | |
140 static int screen; | |
141 static void (*handler[LASTEvent]) (const XEvent *) = { | |
142 [ButtonPress] = buttonpress, | |
143 [ClientMessage] = clientmessage, | |
144 [ConfigureNotify] = configurenotify, | |
145 [ConfigureRequest] = configurerequest, | |
146 [CreateNotify] = createnotify, | |
147 [UnmapNotify] = unmapnotify, | |
148 [DestroyNotify] = destroynotify, | |
149 [Expose] = expose, | |
150 [FocusIn] = focusin, | |
151 [KeyPress] = keypress, | |
152 [MapRequest] = maprequest, | |
153 [PropertyNotify] = propertynotify, | |
154 }; | |
155 static int bh, obh, wx, wy, ww, wh; | |
156 static unsigned int numlockmask; | |
157 static Bool running = True, nextfocus, doinitspawn = True, | |
158 fillagain = False, closelastclient = False, | |
159 killclientsfirst = False; | |
160 static Display *dpy; | |
161 static DC dc; | |
162 static Atom wmatom[WMLast]; | |
163 static Window root, win; | |
164 static Client **clients; | |
165 static int nclients, sel = -1, lastsel = -1; | |
166 static int (*xerrorxlib)(Display *, XErrorEvent *); | |
167 static int cmd_append_pos; | |
168 static char winid[64]; | |
169 static char **cmd; | |
170 static char *wmname = "tabbed"; | |
171 static const char *geometry; | |
172 | |
173 char *argv0; | |
174 | |
175 /* configuration, allows nested code to access above variables */ | |
176 #include "config.h" | |
177 | |
178 void | |
179 buttonpress(const XEvent *e) | |
180 { | |
181 const XButtonPressedEvent *ev = &e->xbutton; | |
182 int i, fc; | |
183 Arg arg; | |
184 | |
185 if (ev->y < 0 || ev->y > bh) | |
186 return; | |
187 | |
188 if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x… | |
189 return; | |
190 | |
191 for (i = fc; i < nclients; i++) { | |
192 if (clients[i]->tabx > ev->x) { | |
193 switch (ev->button) { | |
194 case Button1: | |
195 focus(i); | |
196 break; | |
197 case Button2: | |
198 focus(i); | |
199 killclient(NULL); | |
200 break; | |
201 case Button4: /* FALLTHROUGH */ | |
202 case Button5: | |
203 arg.i = ev->button == Button4 ? -1 : 1; | |
204 rotate(&arg); | |
205 break; | |
206 } | |
207 break; | |
208 } | |
209 } | |
210 } | |
211 | |
212 void | |
213 cleanup(void) | |
214 { | |
215 int i; | |
216 | |
217 for (i = 0; i < nclients; i++) { | |
218 focus(i); | |
219 killclient(NULL); | |
220 XReparentWindow(dpy, clients[i]->win, root, 0, 0); | |
221 unmanage(i); | |
222 } | |
223 free(clients); | |
224 clients = NULL; | |
225 | |
226 XFreePixmap(dpy, dc.drawable); | |
227 XFreeGC(dpy, dc.gc); | |
228 XDestroyWindow(dpy, win); | |
229 XSync(dpy, False); | |
230 free(cmd); | |
231 } | |
232 | |
233 void | |
234 clientmessage(const XEvent *e) | |
235 { | |
236 const XClientMessageEvent *ev = &e->xclient; | |
237 | |
238 if (ev->message_type == wmatom[WMProtocols] && | |
239 ev->data.l[0] == wmatom[WMDelete]) { | |
240 if (nclients > 1 && killclientsfirst) { | |
241 killclient(0); | |
242 return; | |
243 } | |
244 running = False; | |
245 } | |
246 } | |
247 | |
248 void | |
249 configurenotify(const XEvent *e) | |
250 { | |
251 const XConfigureEvent *ev = &e->xconfigure; | |
252 | |
253 if (ev->window == win && (ev->width != ww || ev->height != wh)) { | |
254 ww = ev->width; | |
255 wh = ev->height; | |
256 XFreePixmap(dpy, dc.drawable); | |
257 dc.drawable = XCreatePixmap(dpy, root, ww, wh, | |
258 DefaultDepth(dpy, screen)); | |
259 | |
260 if (!obh && (wh <= bh)) { | |
261 obh = bh; | |
262 bh = 0; | |
263 } else if (!bh && (wh > obh)) { | |
264 bh = obh; | |
265 obh = 0; | |
266 } | |
267 | |
268 if (sel > -1) | |
269 resize(sel, ww, wh - bh); | |
270 XSync(dpy, False); | |
271 } | |
272 } | |
273 | |
274 void | |
275 configurerequest(const XEvent *e) | |
276 { | |
277 const XConfigureRequestEvent *ev = &e->xconfigurerequest; | |
278 XWindowChanges wc; | |
279 int c; | |
280 | |
281 if ((c = getclient(ev->window)) > -1) { | |
282 wc.x = 0; | |
283 wc.y = bh; | |
284 wc.width = ww; | |
285 wc.height = wh - bh; | |
286 wc.border_width = 0; | |
287 wc.sibling = ev->above; | |
288 wc.stack_mode = ev->detail; | |
289 XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &… | |
290 } | |
291 } | |
292 | |
293 void | |
294 createnotify(const XEvent *e) | |
295 { | |
296 const XCreateWindowEvent *ev = &e->xcreatewindow; | |
297 | |
298 if (ev->window != win && getclient(ev->window) < 0) | |
299 manage(ev->window); | |
300 } | |
301 | |
302 void | |
303 destroynotify(const XEvent *e) | |
304 { | |
305 const XDestroyWindowEvent *ev = &e->xdestroywindow; | |
306 int c; | |
307 | |
308 if ((c = getclient(ev->window)) > -1) | |
309 unmanage(c); | |
310 } | |
311 | |
312 void | |
313 die(const char *errstr, ...) | |
314 { | |
315 va_list ap; | |
316 | |
317 va_start(ap, errstr); | |
318 vfprintf(stderr, errstr, ap); | |
319 va_end(ap); | |
320 exit(EXIT_FAILURE); | |
321 } | |
322 | |
323 void | |
324 drawbar(void) | |
325 { | |
326 XftColor *col; | |
327 int c, cc, fc, width; | |
328 char *name = NULL; | |
329 | |
330 if (nclients == 0) { | |
331 dc.x = 0; | |
332 dc.w = ww; | |
333 XFetchName(dpy, win, &name); | |
334 drawtext(name ? name : "", dc.norm); | |
335 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0,… | |
336 XSync(dpy, False); | |
337 | |
338 return; | |
339 } | |
340 | |
341 width = ww; | |
342 cc = ww / tabwidth; | |
343 if (nclients > cc) | |
344 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; | |
345 | |
346 if ((fc = getfirsttab()) + cc < nclients) { | |
347 dc.w = TEXTW(after); | |
348 dc.x = width - dc.w; | |
349 drawtext(after, dc.sel); | |
350 width -= dc.w; | |
351 } | |
352 dc.x = 0; | |
353 | |
354 if (fc > 0) { | |
355 dc.w = TEXTW(before); | |
356 drawtext(before, dc.sel); | |
357 dc.x += dc.w; | |
358 width -= dc.w; | |
359 } | |
360 | |
361 cc = MIN(cc, nclients); | |
362 for (c = fc; c < fc + cc; c++) { | |
363 dc.w = width / cc; | |
364 if (c == sel) { | |
365 col = dc.sel; | |
366 dc.w += width % cc; | |
367 } else { | |
368 col = clients[c]->urgent ? dc.urg : dc.norm; | |
369 } | |
370 drawtext(clients[c]->name, col); | |
371 dc.x += dc.w; | |
372 clients[c]->tabx = dc.x; | |
373 } | |
374 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); | |
375 XSync(dpy, False); | |
376 } | |
377 | |
378 void | |
379 drawtext(const char *text, XftColor col[ColLast]) | |
380 { | |
381 int i, j, x, y, h, len, olen; | |
382 char buf[256]; | |
383 XftDraw *d; | |
384 XRectangle r = { dc.x, dc.y, dc.w, dc.h }; | |
385 | |
386 XSetForeground(dpy, dc.gc, col[ColBG].pixel); | |
387 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); | |
388 if (!text) | |
389 return; | |
390 | |
391 olen = strlen(text); | |
392 h = dc.font.ascent + dc.font.descent; | |
393 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; | |
394 x = dc.x + (h / 2); | |
395 | |
396 /* shorten text if necessary */ | |
397 for (len = MIN(olen, sizeof(buf)); | |
398 len && textnw(text, len) > dc.w - h; len--); | |
399 | |
400 if (!len) | |
401 return; | |
402 | |
403 memcpy(buf, text, len); | |
404 if (len < olen) { | |
405 for (i = len, j = strlen(titletrim); j && i; | |
406 buf[--i] = titletrim[--j]) | |
407 ; | |
408 } | |
409 | |
410 d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), … | |
411 XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8… | |
412 XftDrawDestroy(d); | |
413 } | |
414 | |
415 void * | |
416 ecalloc(size_t n, size_t size) | |
417 { | |
418 void *p; | |
419 | |
420 if (!(p = calloc(n, size))) | |
421 die("%s: cannot calloc\n", argv0); | |
422 return p; | |
423 } | |
424 | |
425 void * | |
426 erealloc(void *o, size_t size) | |
427 { | |
428 void *p; | |
429 | |
430 if (!(p = realloc(o, size))) | |
431 die("%s: cannot realloc\n", argv0); | |
432 return p; | |
433 } | |
434 | |
435 void | |
436 expose(const XEvent *e) | |
437 { | |
438 const XExposeEvent *ev = &e->xexpose; | |
439 | |
440 if (ev->count == 0 && win == ev->window) | |
441 drawbar(); | |
442 } | |
443 | |
444 void | |
445 focus(int c) | |
446 { | |
447 char buf[BUFSIZ] = "tabbed-"VERSION" ::"; | |
448 size_t i, n; | |
449 XWMHints* wmh; | |
450 XWMHints* win_wmh; | |
451 | |
452 /* If c, sel and clients are -1, raise tabbed-win itself */ | |
453 if (nclients == 0) { | |
454 cmd[cmd_append_pos] = NULL; | |
455 for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i… | |
456 n += snprintf(&buf[n], sizeof(buf) - n, " %s", c… | |
457 | |
458 xsettitle(win, buf); | |
459 XRaiseWindow(dpy, win); | |
460 | |
461 return; | |
462 } | |
463 | |
464 if (c < 0 || c >= nclients) | |
465 return; | |
466 | |
467 resize(c, ww, wh - bh); | |
468 XRaiseWindow(dpy, clients[c]->win); | |
469 XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime… | |
470 sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); | |
471 sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); | |
472 xsettitle(win, clients[c]->name); | |
473 | |
474 if (sel != c) { | |
475 lastsel = sel; | |
476 sel = c; | |
477 } | |
478 | |
479 if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->wi… | |
480 wmh->flags &= ~XUrgencyHint; | |
481 XSetWMHints(dpy, clients[c]->win, wmh); | |
482 clients[c]->urgent = False; | |
483 XFree(wmh); | |
484 | |
485 /* | |
486 * gnome-shell will not stop notifying us about urgency, | |
487 * if we clear only the client hint and don't clear the | |
488 * hint from the main container window | |
489 */ | |
490 if ((win_wmh = XGetWMHints(dpy, win))) { | |
491 win_wmh->flags &= ~XUrgencyHint; | |
492 XSetWMHints(dpy, win, win_wmh); | |
493 XFree(win_wmh); | |
494 } | |
495 } | |
496 | |
497 drawbar(); | |
498 XSync(dpy, False); | |
499 } | |
500 | |
501 void | |
502 focusin(const XEvent *e) | |
503 { | |
504 const XFocusChangeEvent *ev = &e->xfocus; | |
505 int dummy; | |
506 Window focused; | |
507 | |
508 if (ev->mode != NotifyUngrab) { | |
509 XGetInputFocus(dpy, &focused, &dummy); | |
510 if (focused == win) | |
511 focus(sel); | |
512 } | |
513 } | |
514 | |
515 void | |
516 focusonce(const Arg *arg) | |
517 { | |
518 nextfocus = True; | |
519 } | |
520 | |
521 void | |
522 focusurgent(const Arg *arg) | |
523 { | |
524 int c; | |
525 | |
526 if (sel < 0) | |
527 return; | |
528 | |
529 for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients)… | |
530 if (clients[c]->urgent) { | |
531 focus(c); | |
532 return; | |
533 } | |
534 } | |
535 } | |
536 | |
537 void | |
538 fullscreen(const Arg *arg) | |
539 { | |
540 XEvent e; | |
541 | |
542 e.type = ClientMessage; | |
543 e.xclient.window = win; | |
544 e.xclient.message_type = wmatom[WMState]; | |
545 e.xclient.format = 32; | |
546 e.xclient.data.l[0] = 2; | |
547 e.xclient.data.l[1] = wmatom[WMFullscreen]; | |
548 e.xclient.data.l[2] = 0; | |
549 XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); | |
550 } | |
551 | |
552 char * | |
553 getatom(int a) | |
554 { | |
555 static char buf[BUFSIZ]; | |
556 Atom adummy; | |
557 int idummy; | |
558 unsigned long ldummy; | |
559 unsigned char *p = NULL; | |
560 | |
561 XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_ST… | |
562 &adummy, &idummy, &ldummy, &ldummy, &p); | |
563 if (p) | |
564 strncpy(buf, (char *)p, LENGTH(buf)-1); | |
565 else | |
566 buf[0] = '\0'; | |
567 XFree(p); | |
568 | |
569 return buf; | |
570 } | |
571 | |
572 int | |
573 getclient(Window w) | |
574 { | |
575 int i; | |
576 | |
577 for (i = 0; i < nclients; i++) { | |
578 if (clients[i]->win == w) | |
579 return i; | |
580 } | |
581 | |
582 return -1; | |
583 } | |
584 | |
585 XftColor | |
586 getcolor(const char *colstr) | |
587 { | |
588 XftColor color; | |
589 | |
590 if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultC… | |
591 die("%s: cannot allocate color '%s'\n", argv0, colstr); | |
592 | |
593 return color; | |
594 } | |
595 | |
596 int | |
597 getfirsttab(void) | |
598 { | |
599 int cc, ret; | |
600 | |
601 if (sel < 0) | |
602 return 0; | |
603 | |
604 cc = ww / tabwidth; | |
605 if (nclients > cc) | |
606 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth; | |
607 | |
608 ret = sel - cc / 2 + (cc + 1) % 2; | |
609 return ret < 0 ? 0 : | |
610 ret + cc > nclients ? MAX(0, nclients - cc) : | |
611 ret; | |
612 } | |
613 | |
614 Bool | |
615 gettextprop(Window w, Atom atom, char *text, unsigned int size) | |
616 { | |
617 char **list = NULL; | |
618 int n; | |
619 XTextProperty name; | |
620 | |
621 if (!text || size == 0) | |
622 return False; | |
623 | |
624 text[0] = '\0'; | |
625 XGetTextProperty(dpy, w, &name, atom); | |
626 if (!name.nitems) | |
627 return False; | |
628 | |
629 if (name.encoding == XA_STRING) { | |
630 strncpy(text, (char *)name.value, size - 1); | |
631 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= S… | |
632 && n > 0 && *list) { | |
633 strncpy(text, *list, size - 1); | |
634 XFreeStringList(list); | |
635 } | |
636 text[size - 1] = '\0'; | |
637 XFree(name.value); | |
638 | |
639 return True; | |
640 } | |
641 | |
642 void | |
643 initfont(const char *fontstr) | |
644 { | |
645 if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr)) | |
646 && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed"))) | |
647 die("error, cannot load font: '%s'\n", fontstr); | |
648 | |
649 dc.font.ascent = dc.font.xfont->ascent; | |
650 dc.font.descent = dc.font.xfont->descent; | |
651 dc.font.height = dc.font.ascent + dc.font.descent; | |
652 } | |
653 | |
654 Bool | |
655 isprotodel(int c) | |
656 { | |
657 int i, n; | |
658 Atom *protocols; | |
659 Bool ret = False; | |
660 | |
661 if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) { | |
662 for (i = 0; !ret && i < n; i++) { | |
663 if (protocols[i] == wmatom[WMDelete]) | |
664 ret = True; | |
665 } | |
666 XFree(protocols); | |
667 } | |
668 | |
669 return ret; | |
670 } | |
671 | |
672 void | |
673 keypress(const XEvent *e) | |
674 { | |
675 const XKeyEvent *ev = &e->xkey; | |
676 unsigned int i; | |
677 KeySym keysym; | |
678 | |
679 keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); | |
680 for (i = 0; i < LENGTH(keys); i++) { | |
681 if (keysym == keys[i].keysym && | |
682 CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && | |
683 keys[i].func) | |
684 keys[i].func(&(keys[i].arg)); | |
685 } | |
686 } | |
687 | |
688 void | |
689 killclient(const Arg *arg) | |
690 { | |
691 XEvent ev; | |
692 | |
693 if (sel < 0) | |
694 return; | |
695 | |
696 if (isprotodel(sel) && !clients[sel]->closed) { | |
697 ev.type = ClientMessage; | |
698 ev.xclient.window = clients[sel]->win; | |
699 ev.xclient.message_type = wmatom[WMProtocols]; | |
700 ev.xclient.format = 32; | |
701 ev.xclient.data.l[0] = wmatom[WMDelete]; | |
702 ev.xclient.data.l[1] = CurrentTime; | |
703 XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &… | |
704 clients[sel]->closed = True; | |
705 } else { | |
706 XKillClient(dpy, clients[sel]->win); | |
707 } | |
708 } | |
709 | |
710 void | |
711 manage(Window w) | |
712 { | |
713 updatenumlockmask(); | |
714 { | |
715 int i, j, nextpos; | |
716 unsigned int modifiers[] = { 0, LockMask, numlockmask, | |
717 numlockmask | LockMask }; | |
718 KeyCode code; | |
719 Client *c; | |
720 XEvent e; | |
721 | |
722 XWithdrawWindow(dpy, w, 0); | |
723 XReparentWindow(dpy, w, win, 0, bh); | |
724 XSelectInput(dpy, w, PropertyChangeMask | | |
725 StructureNotifyMask | EnterWindowMask); | |
726 XSync(dpy, False); | |
727 | |
728 for (i = 0; i < LENGTH(keys); i++) { | |
729 if ((code = XKeysymToKeycode(dpy, keys[i].keysym… | |
730 for (j = 0; j < LENGTH(modifiers); j++) { | |
731 XGrabKey(dpy, code, keys[i].mod | | |
732 modifiers[j], w, True, | |
733 GrabModeAsync, GrabMode… | |
734 } | |
735 } | |
736 } | |
737 | |
738 c = ecalloc(1, sizeof *c); | |
739 c->win = w; | |
740 | |
741 nclients++; | |
742 clients = erealloc(clients, sizeof(Client *) * nclients); | |
743 | |
744 if(npisrelative) { | |
745 nextpos = sel + newposition; | |
746 } else { | |
747 if (newposition < 0) | |
748 nextpos = nclients - newposition; | |
749 else | |
750 nextpos = newposition; | |
751 } | |
752 if (nextpos >= nclients) | |
753 nextpos = nclients - 1; | |
754 if (nextpos < 0) | |
755 nextpos = 0; | |
756 | |
757 if (nclients > 1 && nextpos < nclients - 1) | |
758 memmove(&clients[nextpos + 1], &clients[nextpos], | |
759 sizeof(Client *) * (nclients - nextpos -… | |
760 | |
761 clients[nextpos] = c; | |
762 updatetitle(nextpos); | |
763 | |
764 XLowerWindow(dpy, w); | |
765 XMapWindow(dpy, w); | |
766 | |
767 e.xclient.window = w; | |
768 e.xclient.type = ClientMessage; | |
769 e.xclient.message_type = wmatom[XEmbed]; | |
770 e.xclient.format = 32; | |
771 e.xclient.data.l[0] = CurrentTime; | |
772 e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; | |
773 e.xclient.data.l[2] = 0; | |
774 e.xclient.data.l[3] = win; | |
775 e.xclient.data.l[4] = 0; | |
776 XSendEvent(dpy, root, False, NoEventMask, &e); | |
777 | |
778 XSync(dpy, False); | |
779 | |
780 /* Adjust sel before focus does set it to lastsel. */ | |
781 if (sel >= nextpos) | |
782 sel++; | |
783 focus(nextfocus ? nextpos : | |
784 sel < 0 ? 0 : | |
785 sel); | |
786 nextfocus = foreground; | |
787 } | |
788 } | |
789 | |
790 void | |
791 maprequest(const XEvent *e) | |
792 { | |
793 const XMapRequestEvent *ev = &e->xmaprequest; | |
794 | |
795 if (getclient(ev->window) < 0) | |
796 manage(ev->window); | |
797 } | |
798 | |
799 void | |
800 move(const Arg *arg) | |
801 { | |
802 if (arg->i >= 0 && arg->i < nclients) | |
803 focus(arg->i); | |
804 } | |
805 | |
806 void | |
807 movetab(const Arg *arg) | |
808 { | |
809 int c; | |
810 Client *new; | |
811 | |
812 if (sel < 0) | |
813 return; | |
814 | |
815 c = (sel + arg->i) % nclients; | |
816 if (c < 0) | |
817 c += nclients; | |
818 | |
819 if (c == sel) | |
820 return; | |
821 | |
822 new = clients[sel]; | |
823 if (sel < c) | |
824 memmove(&clients[sel], &clients[sel+1], | |
825 sizeof(Client *) * (c - sel)); | |
826 else | |
827 memmove(&clients[c+1], &clients[c], | |
828 sizeof(Client *) * (sel - c)); | |
829 clients[c] = new; | |
830 sel = c; | |
831 | |
832 drawbar(); | |
833 } | |
834 | |
835 void | |
836 propertynotify(const XEvent *e) | |
837 { | |
838 const XPropertyEvent *ev = &e->xproperty; | |
839 XWMHints *wmh; | |
840 int c; | |
841 char* selection = NULL; | |
842 Arg arg; | |
843 | |
844 if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelect… | |
845 selection = getatom(WMSelectTab); | |
846 if (!strncmp(selection, "0x", 2)) { | |
847 arg.i = getclient(strtoul(selection, NULL, 0)); | |
848 move(&arg); | |
849 } else { | |
850 cmd[cmd_append_pos] = selection; | |
851 arg.v = cmd; | |
852 spawn(&arg); | |
853 } | |
854 } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HI… | |
855 (c = getclient(ev->window)) > -1 && | |
856 (wmh = XGetWMHints(dpy, clients[c]->win))) { | |
857 if (wmh->flags & XUrgencyHint) { | |
858 XFree(wmh); | |
859 wmh = XGetWMHints(dpy, win); | |
860 if (c != sel) { | |
861 if (urgentswitch && wmh && | |
862 !(wmh->flags & XUrgencyHint)) { | |
863 /* only switch, if tabbed was fo… | |
864 * since last urgency hint if WM… | |
865 * could not be received, | |
866 * default to no switch */ | |
867 focus(c); | |
868 } else { | |
869 /* if no switch should be perfor… | |
870 * mark tab as urgent */ | |
871 clients[c]->urgent = True; | |
872 drawbar(); | |
873 } | |
874 } | |
875 if (wmh && !(wmh->flags & XUrgencyHint)) { | |
876 /* update tabbed urgency hint | |
877 * if not set already */ | |
878 wmh->flags |= XUrgencyHint; | |
879 XSetWMHints(dpy, win, wmh); | |
880 } | |
881 } | |
882 XFree(wmh); | |
883 } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME… | |
884 (c = getclient(ev->window)) > -1) { | |
885 updatetitle(c); | |
886 } | |
887 } | |
888 | |
889 void | |
890 resize(int c, int w, int h) | |
891 { | |
892 XConfigureEvent ce; | |
893 XWindowChanges wc; | |
894 | |
895 ce.x = 0; | |
896 ce.y = wc.y = bh; | |
897 ce.width = wc.width = w; | |
898 ce.height = wc.height = h; | |
899 ce.type = ConfigureNotify; | |
900 ce.display = dpy; | |
901 ce.event = clients[c]->win; | |
902 ce.window = clients[c]->win; | |
903 ce.above = None; | |
904 ce.override_redirect = False; | |
905 ce.border_width = 0; | |
906 | |
907 XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight,… | |
908 XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask, | |
909 (XEvent *)&ce); | |
910 } | |
911 | |
912 void | |
913 rotate(const Arg *arg) | |
914 { | |
915 int nsel = -1; | |
916 | |
917 if (sel < 0) | |
918 return; | |
919 | |
920 if (arg->i == 0) { | |
921 if (lastsel > -1) | |
922 focus(lastsel); | |
923 } else if (sel > -1) { | |
924 /* Rotating in an arg->i step around the clients. */ | |
925 nsel = sel + arg->i; | |
926 while (nsel >= nclients) | |
927 nsel -= nclients; | |
928 while (nsel < 0) | |
929 nsel += nclients; | |
930 focus(nsel); | |
931 } | |
932 } | |
933 | |
934 void | |
935 run(void) | |
936 { | |
937 XEvent ev; | |
938 | |
939 /* main event loop */ | |
940 XSync(dpy, False); | |
941 drawbar(); | |
942 if (doinitspawn == True) | |
943 spawn(NULL); | |
944 | |
945 while (running) { | |
946 XNextEvent(dpy, &ev); | |
947 if (handler[ev.type]) | |
948 (handler[ev.type])(&ev); /* call handler */ | |
949 } | |
950 } | |
951 | |
952 void | |
953 sendxembed(int c, long msg, long detail, long d1, long d2) | |
954 { | |
955 XEvent e = { 0 }; | |
956 | |
957 e.xclient.window = clients[c]->win; | |
958 e.xclient.type = ClientMessage; | |
959 e.xclient.message_type = wmatom[XEmbed]; | |
960 e.xclient.format = 32; | |
961 e.xclient.data.l[0] = CurrentTime; | |
962 e.xclient.data.l[1] = msg; | |
963 e.xclient.data.l[2] = detail; | |
964 e.xclient.data.l[3] = d1; | |
965 e.xclient.data.l[4] = d2; | |
966 XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e); | |
967 } | |
968 | |
969 void | |
970 setcmd(int argc, char *argv[], int replace) | |
971 { | |
972 int i; | |
973 | |
974 cmd = ecalloc(argc + 3, sizeof(*cmd)); | |
975 if (argc == 0) | |
976 return; | |
977 for (i = 0; i < argc; i++) | |
978 cmd[i] = argv[i]; | |
979 cmd[replace > 0 ? replace : argc] = winid; | |
980 cmd_append_pos = argc + !replace; | |
981 cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL; | |
982 } | |
983 | |
984 void | |
985 setup(void) | |
986 { | |
987 int bitm, tx, ty, tw, th, dh, dw, isfixed; | |
988 XWMHints *wmh; | |
989 XClassHint class_hint; | |
990 XSizeHints *size_hint; | |
991 struct sigaction sa; | |
992 | |
993 /* do not transform children into zombies when they terminate */ | |
994 sigemptyset(&sa.sa_mask); | |
995 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; | |
996 sa.sa_handler = SIG_IGN; | |
997 sigaction(SIGCHLD, &sa, NULL); | |
998 | |
999 /* clean up any zombies that might have been inherited */ | |
1000 while (waitpid(-1, NULL, WNOHANG) > 0); | |
1001 | |
1002 /* init screen */ | |
1003 screen = DefaultScreen(dpy); | |
1004 root = RootWindow(dpy, screen); | |
1005 initfont(font); | |
1006 bh = dc.h = dc.font.height + 2; | |
1007 | |
1008 /* init atoms */ | |
1009 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); | |
1010 wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREE… | |
1011 False); | |
1012 wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); | |
1013 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); | |
1014 wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", Fal… | |
1015 wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); | |
1016 wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); | |
1017 | |
1018 /* init appearance */ | |
1019 wx = 0; | |
1020 wy = 0; | |
1021 ww = 800; | |
1022 wh = 600; | |
1023 isfixed = 0; | |
1024 | |
1025 if (geometry) { | |
1026 tx = ty = tw = th = 0; | |
1027 bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&t… | |
1028 (unsigned *)&th); | |
1029 if (bitm & XValue) | |
1030 wx = tx; | |
1031 if (bitm & YValue) | |
1032 wy = ty; | |
1033 if (bitm & WidthValue) | |
1034 ww = tw; | |
1035 if (bitm & HeightValue) | |
1036 wh = th; | |
1037 if (bitm & XNegative && wx == 0) | |
1038 wx = -1; | |
1039 if (bitm & YNegative && wy == 0) | |
1040 wy = -1; | |
1041 if (bitm & (HeightValue | WidthValue)) | |
1042 isfixed = 1; | |
1043 | |
1044 dw = DisplayWidth(dpy, screen); | |
1045 dh = DisplayHeight(dpy, screen); | |
1046 if (wx < 0) | |
1047 wx = dw + wx - ww - 1; | |
1048 if (wy < 0) | |
1049 wy = dh + wy - wh - 1; | |
1050 } | |
1051 | |
1052 dc.norm[ColBG] = getcolor(normbgcolor); | |
1053 dc.norm[ColFG] = getcolor(normfgcolor); | |
1054 dc.sel[ColBG] = getcolor(selbgcolor); | |
1055 dc.sel[ColFG] = getcolor(selfgcolor); | |
1056 dc.urg[ColBG] = getcolor(urgbgcolor); | |
1057 dc.urg[ColFG] = getcolor(urgfgcolor); | |
1058 dc.drawable = XCreatePixmap(dpy, root, ww, wh, | |
1059 DefaultDepth(dpy, screen)); | |
1060 dc.gc = XCreateGC(dpy, root, 0, 0); | |
1061 | |
1062 win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, | |
1063 dc.norm[ColFG].pixel, dc.norm[ColBG].p… | |
1064 XMapRaised(dpy, win); | |
1065 XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask | | |
1066 ButtonPressMask | ExposureMask | KeyPressMask | | |
1067 PropertyChangeMask | StructureNotifyMask | | |
1068 SubstructureRedirectMask); | |
1069 xerrorxlib = XSetErrorHandler(xerror); | |
1070 | |
1071 class_hint.res_name = wmname; | |
1072 class_hint.res_class = "tabbed"; | |
1073 XSetClassHint(dpy, win, &class_hint); | |
1074 | |
1075 size_hint = XAllocSizeHints(); | |
1076 if (!isfixed) { | |
1077 size_hint->flags = PSize | PMinSize; | |
1078 size_hint->height = wh; | |
1079 size_hint->width = ww; | |
1080 size_hint->min_height = bh + 1; | |
1081 } else { | |
1082 size_hint->flags = PMaxSize | PMinSize; | |
1083 size_hint->min_width = size_hint->max_width = ww; | |
1084 size_hint->min_height = size_hint->max_height = wh; | |
1085 } | |
1086 wmh = XAllocWMHints(); | |
1087 XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, … | |
1088 XFree(size_hint); | |
1089 XFree(wmh); | |
1090 | |
1091 XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); | |
1092 | |
1093 snprintf(winid, sizeof(winid), "%lu", win); | |
1094 setenv("XEMBED", winid, 1); | |
1095 | |
1096 nextfocus = foreground; | |
1097 focus(-1); | |
1098 } | |
1099 | |
1100 void | |
1101 spawn(const Arg *arg) | |
1102 { | |
1103 struct sigaction sa; | |
1104 | |
1105 if (fork() == 0) { | |
1106 if(dpy) | |
1107 close(ConnectionNumber(dpy)); | |
1108 | |
1109 setsid(); | |
1110 | |
1111 sigemptyset(&sa.sa_mask); | |
1112 sa.sa_flags = 0; | |
1113 sa.sa_handler = SIG_DFL; | |
1114 sigaction(SIGCHLD, &sa, NULL); | |
1115 | |
1116 if (arg && arg->v) { | |
1117 execvp(((char **)arg->v)[0], (char **)arg->v); | |
1118 fprintf(stderr, "%s: execvp %s", argv0, | |
1119 ((char **)arg->v)[0]); | |
1120 } else { | |
1121 cmd[cmd_append_pos] = NULL; | |
1122 execvp(cmd[0], cmd); | |
1123 fprintf(stderr, "%s: execvp %s", argv0, cmd[0]); | |
1124 } | |
1125 perror(" failed"); | |
1126 exit(0); | |
1127 } | |
1128 } | |
1129 | |
1130 int | |
1131 textnw(const char *text, unsigned int len) | |
1132 { | |
1133 XGlyphInfo ext; | |
1134 XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &… | |
1135 return ext.xOff; | |
1136 } | |
1137 | |
1138 void | |
1139 toggle(const Arg *arg) | |
1140 { | |
1141 *(Bool*) arg->v = !*(Bool*) arg->v; | |
1142 } | |
1143 | |
1144 void | |
1145 unmanage(int c) | |
1146 { | |
1147 if (c < 0 || c >= nclients) { | |
1148 drawbar(); | |
1149 XSync(dpy, False); | |
1150 return; | |
1151 } | |
1152 | |
1153 if (!nclients) | |
1154 return; | |
1155 | |
1156 if (c == 0) { | |
1157 /* First client. */ | |
1158 nclients--; | |
1159 free(clients[0]); | |
1160 memmove(&clients[0], &clients[1], sizeof(Client *) * ncl… | |
1161 } else if (c == nclients - 1) { | |
1162 /* Last client. */ | |
1163 nclients--; | |
1164 free(clients[c]); | |
1165 clients = erealloc(clients, sizeof(Client *) * nclients); | |
1166 } else { | |
1167 /* Somewhere inbetween. */ | |
1168 free(clients[c]); | |
1169 memmove(&clients[c], &clients[c+1], | |
1170 sizeof(Client *) * (nclients - (c + 1))); | |
1171 nclients--; | |
1172 } | |
1173 | |
1174 if (nclients <= 0) { | |
1175 lastsel = sel = -1; | |
1176 | |
1177 if (closelastclient) | |
1178 running = False; | |
1179 else if (fillagain && running) | |
1180 spawn(NULL); | |
1181 } else { | |
1182 if (lastsel >= nclients) | |
1183 lastsel = nclients - 1; | |
1184 else if (lastsel > c) | |
1185 lastsel--; | |
1186 | |
1187 if (c == sel && lastsel >= 0) { | |
1188 focus(lastsel); | |
1189 } else { | |
1190 if (sel > c) | |
1191 sel--; | |
1192 if (sel >= nclients) | |
1193 sel = nclients - 1; | |
1194 | |
1195 focus(sel); | |
1196 } | |
1197 } | |
1198 | |
1199 drawbar(); | |
1200 XSync(dpy, False); | |
1201 } | |
1202 | |
1203 void | |
1204 unmapnotify(const XEvent *e) | |
1205 { | |
1206 const XUnmapEvent *ev = &e->xunmap; | |
1207 int c; | |
1208 | |
1209 if ((c = getclient(ev->window)) > -1) | |
1210 unmanage(c); | |
1211 } | |
1212 | |
1213 void | |
1214 updatenumlockmask(void) | |
1215 { | |
1216 unsigned int i, j; | |
1217 XModifierKeymap *modmap; | |
1218 | |
1219 numlockmask = 0; | |
1220 modmap = XGetModifierMapping(dpy); | |
1221 for (i = 0; i < 8; i++) { | |
1222 for (j = 0; j < modmap->max_keypermod; j++) { | |
1223 if (modmap->modifiermap[i * modmap->max_keypermo… | |
1224 == XKeysymToKeycode(dpy, XK_Num_Lock)) | |
1225 numlockmask = (1 << i); | |
1226 } | |
1227 } | |
1228 XFreeModifiermap(modmap); | |
1229 } | |
1230 | |
1231 void | |
1232 updatetitle(int c) | |
1233 { | |
1234 if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->na… | |
1235 sizeof(clients[c]->name))) | |
1236 gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->nam… | |
1237 sizeof(clients[c]->name)); | |
1238 if (sel == c) | |
1239 xsettitle(win, clients[c]->name); | |
1240 drawbar(); | |
1241 } | |
1242 | |
1243 /* There's no way to check accesses to destroyed windows, thus those cas… | |
1244 * ignored (especially on UnmapNotify's). Other types of errors call Xl… | |
1245 * default error handler, which may call exit. */ | |
1246 int | |
1247 xerror(Display *dpy, XErrorEvent *ee) | |
1248 { | |
1249 if (ee->error_code == BadWindow | |
1250 || (ee->request_code == X_SetInputFocus && | |
1251 ee->error_code == BadMatch) | |
1252 || (ee->request_code == X_PolyText8 && | |
1253 ee->error_code == BadDrawable) | |
1254 || (ee->request_code == X_PolyFillRectangle && | |
1255 ee->error_code == BadDrawable) | |
1256 || (ee->request_code == X_PolySegment && | |
1257 ee->error_code == BadDrawable) | |
1258 || (ee->request_code == X_ConfigureWindow && | |
1259 ee->error_code == BadMatch) | |
1260 || (ee->request_code == X_GrabButton && | |
1261 ee->error_code == BadAccess) | |
1262 || (ee->request_code == X_GrabKey && | |
1263 ee->error_code == BadAccess) | |
1264 || (ee->request_code == X_CopyArea && | |
1265 ee->error_code == BadDrawable)) | |
1266 return 0; | |
1267 | |
1268 fprintf(stderr, "%s: fatal error: request code=%d, error code=%d… | |
1269 argv0, ee->request_code, ee->error_code); | |
1270 return xerrorxlib(dpy, ee); /* may call exit */ | |
1271 } | |
1272 | |
1273 void | |
1274 xsettitle(Window w, const char *str) | |
1275 { | |
1276 XTextProperty xtp; | |
1277 | |
1278 if (XmbTextListToTextProperty(dpy, (char **)&str, 1, | |
1279 XUTF8StringStyle, &xtp) == Success) { | |
1280 XSetTextProperty(dpy, w, &xtp, wmatom[WMName]); | |
1281 XSetTextProperty(dpy, w, &xtp, XA_WM_NAME); | |
1282 XFree(xtp.value); | |
1283 } | |
1284 } | |
1285 | |
1286 void | |
1287 usage(void) | |
1288 { | |
1289 die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" | |
1290 " [-r narg] [-o color] [-O color] [-t color] [-T color… | |
1291 " [-u color] [-U color] command...\n", argv0); | |
1292 } | |
1293 | |
1294 int | |
1295 main(int argc, char *argv[]) | |
1296 { | |
1297 Bool detach = False; | |
1298 int replace = 0; | |
1299 char *pstr; | |
1300 | |
1301 ARGBEGIN { | |
1302 case 'c': | |
1303 closelastclient = True; | |
1304 fillagain = False; | |
1305 break; | |
1306 case 'd': | |
1307 detach = True; | |
1308 break; | |
1309 case 'f': | |
1310 fillagain = True; | |
1311 break; | |
1312 case 'g': | |
1313 geometry = EARGF(usage()); | |
1314 break; | |
1315 case 'k': | |
1316 killclientsfirst = True; | |
1317 break; | |
1318 case 'n': | |
1319 wmname = EARGF(usage()); | |
1320 break; | |
1321 case 'O': | |
1322 normfgcolor = EARGF(usage()); | |
1323 break; | |
1324 case 'o': | |
1325 normbgcolor = EARGF(usage()); | |
1326 break; | |
1327 case 'p': | |
1328 pstr = EARGF(usage()); | |
1329 if (pstr[0] == 's') { | |
1330 npisrelative = True; | |
1331 newposition = atoi(&pstr[1]); | |
1332 } else { | |
1333 newposition = atoi(pstr); | |
1334 } | |
1335 break; | |
1336 case 'r': | |
1337 replace = atoi(EARGF(usage())); | |
1338 break; | |
1339 case 's': | |
1340 doinitspawn = False; | |
1341 break; | |
1342 case 'T': | |
1343 selfgcolor = EARGF(usage()); | |
1344 break; | |
1345 case 't': | |
1346 selbgcolor = EARGF(usage()); | |
1347 break; | |
1348 case 'U': | |
1349 urgfgcolor = EARGF(usage()); | |
1350 break; | |
1351 case 'u': | |
1352 urgbgcolor = EARGF(usage()); | |
1353 break; | |
1354 case 'v': | |
1355 die("tabbed-"VERSION", © 2009-2016 tabbed engineers, " | |
1356 "see LICENSE for details.\n"); | |
1357 break; | |
1358 default: | |
1359 usage(); | |
1360 break; | |
1361 } ARGEND; | |
1362 | |
1363 if (argc < 1) { | |
1364 doinitspawn = False; | |
1365 fillagain = False; | |
1366 } | |
1367 | |
1368 setcmd(argc, argv, replace); | |
1369 | |
1370 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | |
1371 fprintf(stderr, "%s: no locale support\n", argv0); | |
1372 if (!(dpy = XOpenDisplay(NULL))) | |
1373 die("%s: cannot open display\n", argv0); | |
1374 | |
1375 setup(); | |
1376 printf("0x%lx\n", win); | |
1377 fflush(NULL); | |
1378 | |
1379 if (detach) { | |
1380 if (fork() == 0) { | |
1381 fclose(stdout); | |
1382 } else { | |
1383 if (dpy) | |
1384 close(ConnectionNumber(dpy)); | |
1385 return EXIT_SUCCESS; | |
1386 } | |
1387 } | |
1388 | |
1389 run(); | |
1390 cleanup(); | |
1391 XCloseDisplay(dpy); | |
1392 | |
1393 return EXIT_SUCCESS; | |
1394 } |