1 /* Skippy - Seduces Kids Into Perversion
2 *
3 * Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /**
21 * @file skippy.h
22 *
23 * @brief Global header.
24 */
25
26 #ifndef SKIPPY_H
27 #define SKIPPY_H
28
29 #define BUF_LEN 128
30
31 #define _GNU_SOURCE
32
33 #include <stdbool.h>
34 #include <strings.h>
35 #include <assert.h>
36
37 #include <X11/Xlib.h>
38 #include <X11/Xmd.h>
39 #include <X11/Xatom.h>
40 #include <X11/Xft/Xft.h>
41
42 #include <X11/extensions/Xrender.h>
43 #include <X11/extensions/Xcomposite.h>
44 #include <X11/extensions/Xdamage.h>
45 #include <X11/extensions/Xfixes.h>
46 #include <X11/extensions/shape.h>
47
48 #ifdef CFG_XINERAMA
49 # include <X11/extensions/Xinerama.h>
50 #endif
51
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include <sys/poll.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <math.h>
58 #include <unistd.h>
59 #include <time.h>
60 #include <string.h>
61 #include <inttypes.h>
62 #include <ctype.h>
63
64 #include "dlist.h"
65
66 #define MAX_MOUSE_BUTTONS 4
67
68 /**
69 * @brief Dump raw bytes in HEX format.
70 *
71 * @param data pointer to raw data
72 * @param len length of data
73 */
74 static inline void
hexdump(const char * data,int len)75 hexdump(const char *data, int len) {
76 static const int BYTE_PER_LN = 16;
77
78 if (len <= 0)
79 return;
80
81 // Print header
82 printf("%10s:", "Offset");
83 for (int i = 0; i < BYTE_PER_LN; ++i)
84 printf(" %2d", i);
85 putchar('\n');
86
87 // Dump content
88 for (int offset = 0; offset < len; ++offset) {
89 if (!(offset % BYTE_PER_LN))
90 printf("0x%08x:", offset);
91
92 printf(" %02hhx", data[offset]);
93
94 if ((BYTE_PER_LN - 1) == offset % BYTE_PER_LN)
95 putchar('\n');
96 }
97 if (len % BYTE_PER_LN)
98 putchar('\n');
99
100 fflush(stdout);
101 }
102
103 /// @brief Possible return values.
104 enum {
105 RET_SUCCESS = 0,
106 RET_UNKNOWN,
107 RET_BADARG,
108 RET_XFAIL,
109 RET_BADALLOC,
110 };
111
112 enum progmode {
113 PROGMODE_NORMAL,
114 PROGMODE_ACTV_PICKER,
115 PROGMODE_DEACTV_PICKER,
116 PROGMODE_TOGGLE_PICKER,
117 PROGMODE_DM_STOP,
118 };
119
120 enum cliop {
121 CLIENTOP_NO,
122 CLIENTOP_FOCUS,
123 CLIENTOP_ICONIFY,
124 CLIENTOP_SHADE_EWMH,
125 CLIENTOP_CLOSE_ICCCM,
126 CLIENTOP_CLOSE_EWMH,
127 CLIENTOP_DESTROY,
128 };
129
130 enum align {
131 ALIGN_LEFT,
132 ALIGN_MID,
133 ALIGN_RIGHT,
134 };
135
136 enum buttons {
137 BUTN_CLOSE,
138 BUTN_MINIMIZE,
139 BUTN_SHADE,
140 NUM_BUTN,
141 };
142
143 enum pict_posp_mode {
144 PICTPOSP_ORIG,
145 PICTPOSP_SCALE,
146 PICTPOSP_SCALEK,
147 PICTPOSP_SCALEE,
148 PICTPOSP_SCALEEK,
149 PICTPOSP_TILE,
150 };
151
152 typedef enum {
153 WMPSN_NONE,
154 WMPSN_EWMH,
155 WMPSN_GNOME,
156 } wmpsn_t;
157
158 typedef struct {
159 Pixmap pxmap;
160 Picture pict;
161 int height;
162 int width;
163 int depth;
164 } pictw_t;
165
166 typedef struct {
167 char *path;
168 pictw_t *img;
169 enum pict_posp_mode mode;
170 int twidth;
171 int theight;
172 enum align alg;
173 enum align valg;
174 XRenderColor c;
175 } pictspec_t;
176
177 #define PICTSPECT_INIT { \
178 .path = NULL, \
179 }
180
181 typedef struct {
182 unsigned int key;
183 enum {
184 KEYMOD_CTRL = 1 << 0,
185 KEYMOD_SHIFT = 1 << 1,
186 KEYMOD_META = 1 << 2,
187 } mod;
188 } keydef_t;
189
190 typedef enum {
191 CLIDISP_NONE,
192 CLIDISP_FILLED,
193 CLIDISP_ICON,
194 CLIDISP_THUMBNAIL,
195 CLIDISP_THUMBNAIL_ICON,
196 } client_disp_mode_t;
197
198 /// @brief Option structure.
199 typedef struct {
200 char *config_path;
201 enum progmode mode;
202 bool runAsDaemon;
203 bool synchronize;
204
205 int distance;
206 bool useNetWMFullscreen;
207 bool ignoreSkipTaskbar;
208 bool acceptOvRedir;
209 bool acceptWMWin;
210 double updateFreq;
211 bool lazyTrans;
212 bool useNameWindowPixmap;
213 bool forceNameWindowPixmap;
214 bool includeFrame;
215 char *pipePath;
216 bool movePointerOnStart;
217 bool movePointerOnSelect;
218 bool movePointerOnRaise;
219 bool switchDesktopOnActivate;
220 bool allowUpscale;
221 bool includeAllScreens;
222 bool avoidThumbnailsFromOtherScreens;
223 bool showAllDesktops;
224 bool showUnmapped;
225 int preferredIconSize;
226 client_disp_mode_t *clientDisplayModes;
227 pictspec_t iconFillSpec;
228 pictw_t *iconDefault;
229 pictspec_t fillSpec;
230 char *buttonImgs[NUM_BUTN];
231 pictw_t *background;
232
233 bool xinerama_showAll;
234
235 char *normal_tint;
236 int normal_tintOpacity;
237 int normal_opacity;
238
239 char *highlight_tint;
240 int highlight_tintOpacity;
241 int highlight_opacity;
242
243 bool tooltip_show;
244 bool tooltip_followsMouse;
245 int tooltip_offsetX;
246 int tooltip_offsetY;
247 enum align tooltip_align;
248 char *tooltip_border;
249 char *tooltip_background;
250 int tooltip_opacity;
251 char *tooltip_text;
252 char *tooltip_textShadow;
253 char *tooltip_font;
254
255 enum cliop bindings_miwMouse[MAX_MOUSE_BUTTONS];
256 } options_t;
257
258 #define OPTIONST_INIT { \
259 .config_path = NULL, \
260 .mode = PROGMODE_NORMAL, \
261 .runAsDaemon = false, \
262 .synchronize = false, \
263 \
264 .distance = 50, \
265 .useNetWMFullscreen = true, \
266 .ignoreSkipTaskbar = false, \
267 .acceptOvRedir = false, \
268 .acceptWMWin = false, \
269 .updateFreq = 10.0, \
270 .lazyTrans = false, \
271 .useNameWindowPixmap = false, \
272 .forceNameWindowPixmap = false, \
273 .includeFrame = false, \
274 .pipePath = NULL, \
275 .movePointerOnStart = true, \
276 .movePointerOnSelect = true, \
277 .movePointerOnRaise = true, \
278 .switchDesktopOnActivate = false, \
279 .allowUpscale = true, \
280 .includeAllScreens = false, \
281 .avoidThumbnailsFromOtherScreens = true, \
282 .preferredIconSize = 48, \
283 .clientDisplayModes = NULL, \
284 .iconFillSpec = PICTSPECT_INIT, \
285 .fillSpec = PICTSPECT_INIT, \
286 .showAllDesktops = false, \
287 .showUnmapped = true, \
288 .buttonImgs = { NULL }, \
289 .background = NULL, \
290 .xinerama_showAll = true, \
291 .normal_tint = NULL, \
292 .normal_tintOpacity = 0, \
293 .normal_opacity = 200, \
294 .highlight_tint = NULL, \
295 .highlight_tintOpacity = 64, \
296 .highlight_opacity = 255, \
297 .tooltip_show = true, \
298 .tooltip_followsMouse = true, \
299 .tooltip_offsetX = 20, \
300 .tooltip_offsetY = 20, \
301 .tooltip_align = ALIGN_LEFT, \
302 .tooltip_border = NULL, \
303 .tooltip_background = NULL, \
304 .tooltip_opacity = 128, \
305 .tooltip_text = NULL, \
306 .tooltip_textShadow = NULL, \
307 .tooltip_font = NULL, \
308 }
309
310 /// @brief X information structure.
311 typedef struct {
312 int damage_ev_base;
313 int damage_err_base;
314 int composite_ev_base;
315 int composite_err_base;
316 int render_ev_base;
317 int render_err_base;
318 int fixes_ev_base;
319 int fixes_err_base;
320
321 bool xinerama_exist;
322 int xinerama_err_base;
323 int xinerama_ev_base;
324 } xinfo_t;
325
326 #define XINFOT_INIT { \
327 .xinerama_exist = false, \
328 }
329
330 typedef struct _clientwin_t ClientWin;
331 typedef struct _mainwin_t MainWin;
332
333 /// @brief Session global info structure.
334 typedef struct {
335 /// @brief Program options.
336 options_t o;
337 /// @brief X display.
338 Display *dpy;
339 /// @brief Current screen.
340 int screen;
341 /// @brief Root window ID.
342 Window root;
343 /// @brief Information about X.
344 xinfo_t xinfo;
345 /// @brief Time the program was started, in milliseconds.
346 struct timeval time_start;
347 /// @brief WM personality.
348 wmpsn_t wmpsn;
349 /// @brief Whether we have EWMH fullscreen support.
350 bool has_ewmh_fullscreen;
351 /// @brief ARGB visual of the current screen.
352 Visual *argb_visual;
353 /// @brief File descriptor of command pipe, in daemon mode.
354 int fd_pipe;
355 /// @brief Main window.
356 MainWin *mainwin;
357 } session_t;
358
359 #define SESSIONT_INIT { \
360 .o = OPTIONST_INIT, \
361 .xinfo = XINFOT_INIT, \
362 .time_start = { .tv_sec = 0, .tv_usec = 0 }, \
363 .fd_pipe = -1, \
364 }
365
366 /// @brief Print out a debug message.
367 #define printfd(format, ...) \
368 (fprintf(stdout, format "\n", ## __VA_ARGS__), fflush(stdout))
369
370 /// @brief Print out a debug message with function name.
371 #define printfdf(format, ...) \
372 (fprintf(stdout, "%s" format "\n", __func__, ## __VA_ARGS__), fflush(stdout))
373
374 /// @brief Print out an error message.
375 #define printfe(format, ...) \
376 (fprintf(stderr, format "\n", ## __VA_ARGS__), fflush(stderr))
377
378 /// @brief Print out an error message with function name.
379 #define printfef(format, ...) \
380 printfe("%s" format, __func__, ## __VA_ARGS__)
381
382 /// @brief Return a value if it's true.
383 #define retif(x) do { if (x) return (x); } while (0)
384
385 /// @brief Wrapper for gcc branch prediction builtin, for likely branch.
386 #define likely(x) __builtin_expect(!!(x), 1)
387 /// @brief Wrapper for gcc branch prediction builtin, for unlikely branch.
388 #define unlikely(x) __builtin_expect(!!(x), 0)
389
390 /**
391 * @brief Quit with RETV_BADALLOC if the passed-in pointer is empty.
392 */
393 static inline void *
allocchk_(void * ptr,const char * func_name)394 allocchk_(void *ptr, const char *func_name) {
395 if (unlikely(!ptr)) {
396 printfe("%s(): Failed to allocate memory.", func_name);
397 exit(RET_BADALLOC);
398 }
399
400 return ptr;
401 }
402
403 /// @brief Wrapper of allocchk_().
404 #define allocchk(ptr) allocchk_(ptr, __func__)
405
406 /// @brief Wrapper of malloc().
407 #define smalloc(nmemb, type) ((type *) allocchk(malloc((nmemb) * sizeof(type))))
408
409 /// @brief Wrapper of calloc().
410 #define scalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type))))
411
412 /// @brief Wrapper of ralloc().
413 #define srealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type))))
414
415 /// @brief Return the case string.
416 /// Use #s here to prevent macro expansion
417 #define CASESTRRET(s) case s: return #s
418
419 /// @brief Return number of elements in a constant array.
420 #define CARR_LEN(a) sizeof(a) / sizeof(a[0])
421
422 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
423 #define MIN(a,b) (((a) > (b)) ? (b) : (a))
424
425 #define foreach_dlist_vn(itervar, l) \
426 for (dlist *(itervar) = dlist_first(l); (itervar); (itervar) = (itervar)->next)
427 #define foreach_dlist(l) foreach_dlist_vn(iter, l)
428 #define REDUCE(statement, l) \
429 do { \
430 foreach_dlist(l) \
431 statement; \
432 } while (0)
433
434 /**
435 * @brief Get current time, in milliseconds.
436 */
437 static inline long
time_in_millis(void)438 time_in_millis(void) {
439 struct timeval tp;
440
441 gettimeofday(&tp, NULL);
442
443 return (tp.tv_sec * 1000) + (tp.tv_usec / 1000);
444 }
445
446 /**
447 * @brief Subtracting two struct timeval values.
448 *
449 * Taken from glibc manual.
450 *
451 * Subtract the `struct timeval' values X and Y,
452 * storing the result in RESULT.
453 * Return 1 if the difference is negative, otherwise 0.
454 */
455 static inline int
timeval_subtract(struct timeval * result,struct timeval * x,struct timeval * y)456 timeval_subtract(struct timeval *result,
457 struct timeval *x, struct timeval *y) {
458 // Perform the carry for the later subtraction by updating y
459 if (x->tv_usec < y->tv_usec) {
460 long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
461 y->tv_usec -= 1000000 * nsec;
462 y->tv_sec += nsec;
463 }
464
465 if (x->tv_usec - y->tv_usec > 1000000) {
466 long nsec = (x->tv_usec - y->tv_usec) / 1000000;
467 y->tv_usec += 1000000 * nsec;
468 y->tv_sec -= nsec;
469 }
470
471 // Compute the time remaining to wait.
472 // tv_usec is certainly positive.
473 result->tv_sec = x->tv_sec - y->tv_sec;
474 result->tv_usec = x->tv_usec - y->tv_usec;
475
476 // Return 1 if result is negative.
477 return x->tv_sec < y->tv_sec;
478 }
479
480 /**
481 * @brief Print time passed since program starts execution.
482 */
483 static inline void
print_timestamp(session_t * ps)484 print_timestamp(session_t *ps) {
485 struct timeval tm, diff;
486
487 if (gettimeofday(&tm, NULL)) return;
488
489 timeval_subtract(&diff, &tm, &ps->time_start);
490
491 printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000);
492 }
493
494 /**
495 * @brief Allocate the space and copy some data.
496 */
497 static inline unsigned char *
mmemcpy(const unsigned char * data,int len)498 mmemcpy(const unsigned char *data, int len) {
499 unsigned char *d = smalloc(len, unsigned char);
500 memcpy(d, data, len);
501 return d;
502 }
503
504 /**
505 * @brief Allocate the space and join two strings.
506 */
507 static inline char *
mstrjoin(const char * src1,const char * src2)508 mstrjoin(const char *src1, const char *src2) {
509 char *str = allocchk(calloc((strlen(src1) + strlen(src2) + 1), sizeof(char)));
510
511 strcpy(str, src1);
512 strcat(str, src2);
513
514 return str;
515 }
516
517 /**
518 * @brief Allocate the space and join two strings;
519 */
520 static inline char *
mstrjoin3(const char * src1,const char * src2,const char * src3)521 mstrjoin3(const char *src1, const char *src2, const char *src3) {
522 char *str = allocchk(calloc((strlen(src1) + strlen(src2)
523 + strlen(src3) + 1), sizeof(char)));
524
525 strcpy(str, src1);
526 strcat(str, src2);
527 strcat(str, src3);
528
529 return str;
530 }
531
532 /**
533 * @brief Wrapper of strdup().
534 */
535 static inline char *
mstrdup(const char * src)536 mstrdup(const char *src) {
537 return allocchk(strdup(src));
538 }
539
540 /**
541 * @brief Copy a number of characters to a newly allocated string.
542 */
543 static inline char *
mstrncpy(const char * src,unsigned len)544 mstrncpy(const char *src, unsigned len) {
545 char *str = allocchk(malloc(sizeof(char) * (len + 1)));
546 strncpy(str, src, len);
547 str[len] = '\0';
548
549 return str;
550 }
551
552 /**
553 * @brief Copy and place a string to somewhere.
554 */
555 static inline void
strplace(char ** dst,const char * src)556 strplace(char **dst, const char *src) {
557 free(*dst);
558 *dst = mstrdup(src);
559 }
560
561 /**
562 * @brief Check if a character symbolizes end of a word.
563 */
564 static inline bool
isspace0(char c)565 isspace0(char c) {
566 return !c || isspace(c);
567 }
568
569 /**
570 * @brief Check if a string ends with something, ignore case.
571 */
572 static inline bool
str_endwith(const char * haystick,const char * needle)573 str_endwith(const char *haystick, const char *needle) {
574 int haystick_len = strlen(haystick);
575 int needle_len = strlen(needle);
576 return haystick_len >= needle_len
577 && !strcasecmp(haystick + haystick_len - needle_len, needle);
578 }
579
580 /**
581 * @brief Check if a string starts with some words.
582 */
583 static inline bool
str_startswithword(const char * haystick,const char * needle)584 str_startswithword(const char *haystick, const char *needle) {
585 const int needle_len = strlen(needle);
586 return !strncmp(haystick, needle, needle_len)
587 && isspace0(haystick[needle_len]);
588 }
589
590 /**
591 * @brief Check if a string starts with some words, ignore case.
592 */
593 static inline bool
str_startswithwordi(const char * haystick,const char * needle)594 str_startswithwordi(const char *haystick, const char *needle) {
595 const int needle_len = strlen(needle);
596 return !strncasecmp(haystick, needle, needle_len)
597 && isspace0(haystick[needle_len]);
598 }
599
600 /**
601 * @brief Get first word.
602 *
603 * @param dest place to store pointer to a copy of the first word
604 * @return start of next word
605 */
606 static inline const char *
str_get_word(const char * s,char ** dest)607 str_get_word(const char *s, char **dest) {
608 *dest = NULL;
609 int i = 0;
610 while (isspace(s[i])) ++i;
611 int start = i;
612 while (!isspace0(s[i])) ++i;
613 if (i - start)
614 *dest = mstrncpy(s + start, i - start);
615 while (isspace(s[i])) ++i;
616 if (!s[i]) return NULL;
617 return &s[i];
618 }
619
620 /**
621 * @brief Destroy a <code>Pixmap</code>.
622 */
623 static inline void
free_pixmap(session_t * ps,Pixmap * p)624 free_pixmap(session_t *ps, Pixmap *p) {
625 if (*p) {
626 XFreePixmap(ps->dpy, *p);
627 *p = None;
628 }
629 }
630
631 /**
632 * @brief Destroy a <code>Picture</code>.
633 */
634 static inline void
free_picture(session_t * ps,Picture * p)635 free_picture(session_t *ps, Picture *p) {
636 if (*p) {
637 XRenderFreePicture(ps->dpy, *p);
638 *p = None;
639 }
640 }
641
642 /**
643 * @brief Destroy a <code>Damage</code>.
644 */
645 static inline void
free_damage(session_t * ps,Damage * p)646 free_damage(session_t *ps, Damage *p) {
647 if (*p) {
648 // BadDamage will be thrown if the window is destroyed
649 XDamageDestroy(ps->dpy, *p);
650 *p = None;
651 }
652 }
653
654 /**
655 * @brief Destroy a <code>XserverRegion</code>.
656 */
657 static inline void
free_region(session_t * ps,XserverRegion * p)658 free_region(session_t *ps, XserverRegion *p) {
659 if (*p) {
660 XFixesDestroyRegion(ps->dpy, *p);
661 *p = None;
662 }
663 }
664
665 static inline unsigned short
alphaconv(int alpha)666 alphaconv(int alpha) {
667 return MIN(alpha * 256, 65535);
668 }
669
670 /**
671 * @brief Wrapper of XFree() for convenience.
672 *
673 * Because a NULL pointer cannot be passed to XFree(), its man page says.
674 */
675 static inline void
sxfree(void * data)676 sxfree(void *data) {
677 if (data)
678 XFree(data);
679 }
680
681 /**
682 * @brief Wrapper to sxfree() to turn the pointer NULL as well.
683 */
684 static inline void
spxfree(void * data)685 spxfree(void *data) {
686 sxfree(*(void **) data);
687 *(void **) data = NULL;
688 }
689
690 /**
691 * @brief Checks if a key event matches particular key and modifier
692 * combination.
693 */
694 static inline bool
ev_iskey(XKeyEvent * ev,const keydef_t * k)695 ev_iskey(XKeyEvent *ev, const keydef_t *k) {
696 unsigned int mask = 0;
697 if (KEYMOD_CTRL & k->mod) mask |= ControlMask;
698 if (KEYMOD_SHIFT & k->mod) mask |= ShiftMask;
699 if (KEYMOD_META & k->mod) mask |= Mod1Mask;
700 return k->key == ev->keycode
701 && (ev->state & (ControlMask | ShiftMask | Mod1Mask)) == mask;
702 }
703
704 /**
705 * @brief Return a string representation for the keycode in a KeyEvent.
706 */
707 static inline const char *
ev_key_str(XKeyEvent * ev)708 ev_key_str(XKeyEvent *ev) {
709 return XKeysymToString(XLookupKeysym(ev, 0));
710 }
711
712 #define report_key_ignored(ev) \
713 printfef("(): KeyRelease %u (%s) ignored.", (ev)->xkey.keycode, \
714 ev_key_str(&(ev)->xkey))
715
716 #define report_key_unbinded(ev) \
717 printfef("(): KeyRelease %u (%s) not binded to anything.", \
718 (ev)->xkey.keycode, ev_key_str(&(ev)->xkey))
719
720 #include "img.h"
721 #include "wm.h"
722 #include "mainwin.h"
723 #include "clientwin.h"
724 #include "layout.h"
725 #include "focus.h"
726 #include "config.h"
727 #include "tooltip.h"
728 #include "img-xlib.h"
729 #ifdef CFG_LIBPNG
730 // FreeType uses setjmp.h and libpng-1.2 feels crazy about this...
731 #define PNG_SKIP_SETJMP_CHECK 1
732 #include "img-png.h"
733 #endif
734 #ifdef CFG_JPEG
735 #include "img-jpeg.h"
736 #endif
737 #ifdef CFG_GIFLIB
738 #include "img-gif.h"
739 #endif
740
741 extern session_t *ps_g;
742
743 #endif /* SKIPPY_H */
744