1 /*
2  *                           0BSD
3  *
4  *                    BSD Zero Clause License
5  *
6  *  Copyright (c) 2019 Hermann Meyer
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted.
10 
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  *
19  */
20 
21 
22 #include "xwidget.h"
23 #include "xwidget_private.h"
24 
25 
key_mapping(Display * dpy,XKeyEvent * xkey)26 int key_mapping(Display *dpy, XKeyEvent *xkey) {
27     if (xkey->keycode == XKeysymToKeycode(dpy,XK_Tab))
28         return (xkey->state == ShiftMask) ? 1 : 2;
29     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Up))
30         return 3;
31     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Right))
32         return 4;
33     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Down))
34         return 5;
35     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Left))
36         return 6;
37     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Home))
38         return 7;
39     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Insert))
40         return 8;
41     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_End))
42         return 9;
43     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_Return))
44         return 10;
45     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_BackSpace))
46         return 11;
47     // keypad
48     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Subtract))
49         return 1;
50     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Add))
51         return 2;
52     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Up))
53         return 3;
54     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Right))
55         return 4;
56     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Down))
57         return 5;
58     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Left))
59         return 6;
60     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Home))
61         return 7;
62     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Insert))
63         return 8;
64     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_End))
65         return 9;
66     else if (xkey->keycode == XKeysymToKeycode(dpy,XK_KP_Enter))
67         return 10;
68     else return 0;
69 }
70 
destroy_widget(Widget_t * w,Xputty * main)71 void destroy_widget(Widget_t * w, Xputty *main) {
72     int count = childlist_find_child(main->childlist, w);
73     if (count == 0 && main->run == true) {
74         quit(w);
75     } else if(childlist_find_child(main->childlist, w)>=0) {
76         if(w->flags & REUSE_IMAGE) {
77             w->image = NULL;
78         }
79         if(w->flags & HAS_MEM) {
80             w->func.mem_free_callback(w, NULL);
81         }
82         childlist_remove_child(main->childlist, w);
83         int ch = childlist_has_child(w->childlist);
84         if (ch) {
85             int i = ch;
86             for(;i>0;i--) {
87                 destroy_widget(w->childlist->childs[i-1],main);
88             }
89             destroy_widget(w,main);
90         }
91         if(w->flags & IS_WIDGET) {
92             Widget_t *p = (Widget_t *) w->parent;
93             childlist_remove_child(p->childlist, w);
94         }
95         delete_adjustment(w->adj_x);
96         delete_adjustment(w->adj_y);
97         childlist_destroy(w->childlist);
98         cairo_surface_destroy(w->image);
99         cairo_destroy(w->crb);
100         cairo_surface_destroy(w->buffer);
101         cairo_destroy(w->cr);
102         cairo_surface_destroy(w->surface);
103 
104         if (w->xic) XDestroyIC(w->xic);
105         if (w->xim) XCloseIM(w->xim);
106         XUnmapWindow(w->app->dpy, w->widget);
107         XDestroyWindow(w->app->dpy, w->widget);
108         free(w->childlist);
109         free(w);
110         w = NULL;
111     }
112 }
113 
configure_event(void * w_,void * user_data)114 void configure_event(void *w_, void* user_data) {
115     Widget_t *wid = (Widget_t*)w_;
116     XWindowAttributes attrs;
117     XGetWindowAttributes(wid->app->dpy, (Window)wid->widget, &attrs);
118     if (wid->width != attrs.width || wid->height != attrs.height) {
119         wid->scale.scale_x    = (float)wid->scale.init_width - attrs.width;
120         wid->scale.scale_y    = (float)wid->scale.init_height - attrs.height;
121         wid->scale.cscale_x   = (float)((float)wid->scale.init_width/(float)attrs.width);
122         wid->scale.cscale_y   = (float)((float)wid->scale.init_height/(float)attrs.height);
123         wid->scale.rcscale_x   = (float)((float)attrs.width/(float)wid->scale.init_width);
124         wid->scale.rcscale_y   = (float)((float)attrs.height/(float)wid->scale.init_height);
125         wid->scale.ascale     = wid->scale.cscale_x < wid->scale.cscale_y ?
126                                 wid->scale.cscale_y : wid->scale.cscale_x;
127 
128         _resize_surface(wid, attrs.width, attrs.height);
129 
130         debug_print("Widget_t configure callback width %i height %i\n", attrs.width, attrs.height);
131 
132         _resize_childs(wid);
133     }
134 }
135 
widget_reset_scale(Widget_t * w)136 void widget_reset_scale(Widget_t *w) {
137     cairo_scale(w->crb, w->scale.cscale_x,w->scale.cscale_y);
138 }
139 
widget_set_scale(Widget_t * w)140 void widget_set_scale(Widget_t *w) {
141     cairo_scale(w->crb, w->scale.rcscale_x,w->scale.rcscale_y);
142 }
143 
create_window(Xputty * app,Window win,int x,int y,int width,int height)144 Widget_t *create_window(Xputty *app, Window win,
145                           int x, int y, int width, int height) {
146 
147     Widget_t *w = (Widget_t*)malloc(sizeof(Widget_t));
148     assert(w != NULL);
149     debug_print("assert(w)\n");
150     XSetWindowAttributes attributes;
151     attributes.save_under = True;
152     attributes.override_redirect = 0;
153 
154     long event_mask = StructureNotifyMask|ExposureMask|KeyPressMask
155                     |EnterWindowMask|LeaveWindowMask|ButtonReleaseMask
156                     |ButtonPressMask|Button1MotionMask;
157 
158 
159 
160     w->widget = XCreateWindow(app->dpy, win , x, y, width, height, 0,
161                             CopyFromParent, InputOutput, CopyFromParent,
162                             CopyFromParent, &attributes);
163     debug_print("XCreateWindow\n");
164 
165     XSetLocaleModifiers("");
166     w->xim = XOpenIM(app->dpy, 0, 0, 0);
167     if(!w->xim){
168         XSetLocaleModifiers("@im=none");
169         w->xim = XOpenIM(app->dpy, 0, 0, 0);
170     }
171 
172     w->xic = XCreateIC(w->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
173                     XNClientWindow, w->widget, XNFocusWindow,  w->widget, NULL);
174 
175     XSetICFocus(w->xic);
176 
177     XSelectInput(app->dpy, w->widget, event_mask);
178 
179     XSizeHints* win_size_hints;
180     win_size_hints = XAllocSizeHints();
181     win_size_hints->flags =  PMinSize|PBaseSize|PWinGravity;
182     win_size_hints->min_width = width/2;
183     win_size_hints->min_height = height/2;
184     win_size_hints->base_width = width;
185     win_size_hints->base_height = height;
186     win_size_hints->win_gravity = CenterGravity;
187     XSetWMNormalHints(app->dpy, w->widget, win_size_hints);
188     XFree(win_size_hints);
189 
190     w->surface =  cairo_xlib_surface_create (app->dpy, w->widget,
191                   DefaultVisual(app->dpy, DefaultScreen(app->dpy)), width, height);
192 
193     assert(cairo_surface_status(w->surface) == CAIRO_STATUS_SUCCESS);
194     w->cr = cairo_create(w->surface);
195     cairo_select_font_face (w->cr, "Roboto", CAIRO_FONT_SLANT_NORMAL,
196                                CAIRO_FONT_WEIGHT_NORMAL);
197 
198     w->buffer = cairo_surface_create_similar (w->surface,
199                         CAIRO_CONTENT_COLOR_ALPHA, width, height);
200     assert(cairo_surface_status(w->buffer) == CAIRO_STATUS_SUCCESS);
201     w->crb = cairo_create (w->buffer);
202     cairo_select_font_face (w->crb, "Roboto", CAIRO_FONT_SLANT_NORMAL,
203                                CAIRO_FONT_WEIGHT_NORMAL);
204 
205     w->image = NULL;
206 
207     w->flags = IS_WINDOW;
208     w->flags &= ~NO_AUTOREPEAT;
209     w->flags &= ~FAST_REDRAW;
210     w->flags &= ~HIDE_ON_DELETE;
211     w->flags &= ~REUSE_IMAGE;
212     w->app = app;
213     w->parent = &win;
214     w->parent_struct = NULL;
215     w->label = NULL;
216     memset(w->input_label, 0, 32 * (sizeof w->input_label[0]));
217     w->state = 0;
218     w->data = 0;
219     w->x = x;
220     w->y = y;
221     w->width = width;
222     w->height = height;
223     w->scale.init_x = x;
224     w->scale.init_y = y;
225     w->scale.init_width = width;
226     w->scale.init_height = height;
227     w->scale.scale_x  = 0.0;
228     w->scale.scale_y  = 0.0;
229     w->scale.cscale_x = 1.0;
230     w->scale.cscale_y = 1.0;
231     w->scale.rcscale_x = 1.0;
232     w->scale.rcscale_y = 1.0;
233     w->scale.ascale   = 1.0;
234     w->scale.gravity  = CENTER;
235     w->adj_x = NULL;
236     w->adj_y = NULL;
237     w->adj   = NULL;
238     w->childlist = (Childlist_t*)malloc(sizeof(Childlist_t));
239     assert(w->childlist != NULL);
240     childlist_init(w->childlist);
241     w->event_callback = widget_event_loop;
242     w->func.expose_callback = _dummy_callback;
243     w->func.configure_callback = configure_event;
244     w->func.button_press_callback = _dummy1_callback;
245     w->func.button_release_callback = _dummy1_callback;
246     w->func.motion_callback = _dummy1_callback;
247     w->func.adj_callback = transparent_draw;
248     w->func.value_changed_callback = _dummy_callback;
249     w->func.key_press_callback = _dummy1_callback;
250     w->func.key_release_callback = _dummy1_callback;
251     w->func.enter_callback = _dummy_callback;
252     w->func.leave_callback = _dummy_callback;
253     w->func.user_callback = _dummy_callback;
254     w->func.mem_free_callback = _dummy_callback;
255     w->func.configure_notify_callback = _dummy_callback;
256     w->func.map_notify_callback = _dummy_callback;
257     w->func.unmap_notify_callback = _dummy_callback;
258     w->func.dialog_callback = _dummy_callback;
259 
260     childlist_add_child(app->childlist,w);
261     //XMapWindow(app->dpy, w->widget);
262     debug_print("size of Func_t = %lu\n", sizeof(w->func)/sizeof(void*));
263     return w;
264 }
265 
create_widget(Xputty * app,Widget_t * parent,int x,int y,int width,int height)266 Widget_t *create_widget(Xputty *app, Widget_t *parent,
267                           int x, int y, int width, int height) {
268 
269     Widget_t *w = (Widget_t*)malloc(sizeof(Widget_t));
270     assert(w != NULL);
271     debug_print("assert(w)\n");
272     XSetWindowAttributes attributes;
273     attributes.save_under = True;
274     attributes.override_redirect = True;
275 
276     long event_mask = StructureNotifyMask|ExposureMask|KeyPressMask
277                     |EnterWindowMask|LeaveWindowMask|ButtonReleaseMask
278                     |ButtonPressMask|Button1MotionMask;
279 
280 
281 
282     w->widget = XCreateWindow(app->dpy, parent->widget , x, y, width, height, 0,
283                             CopyFromParent, InputOutput, CopyFromParent,
284                             CopyFromParent|CWOverrideRedirect, &attributes);
285     debug_print("XCreateWindow\n");
286 
287     XSetLocaleModifiers("");
288     w->xim = XOpenIM(app->dpy, 0, 0, 0);
289     if(!w->xim){
290         XSetLocaleModifiers("@im=none");
291         w->xim = XOpenIM(app->dpy, 0, 0, 0);
292     }
293 
294     w->xic = XCreateIC(w->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
295                     XNClientWindow, w->widget, XNFocusWindow,  w->widget, NULL);
296 
297     XSetICFocus(w->xic);
298 
299     XSelectInput(app->dpy, w->widget, event_mask);
300 
301     w->surface =  cairo_xlib_surface_create (app->dpy, w->widget,
302                   DefaultVisual(app->dpy, DefaultScreen(app->dpy)), width, height);
303     assert(cairo_surface_status(w->surface) == CAIRO_STATUS_SUCCESS);
304     w->cr = cairo_create(w->surface);
305     cairo_select_font_face (w->cr, "Roboto", CAIRO_FONT_SLANT_NORMAL,
306                                CAIRO_FONT_WEIGHT_NORMAL);
307 
308     w->buffer = cairo_surface_create_similar (w->surface,
309                         CAIRO_CONTENT_COLOR_ALPHA, width, height);
310     assert(cairo_surface_status(w->buffer) == CAIRO_STATUS_SUCCESS);
311     w->crb = cairo_create (w->buffer);
312     cairo_select_font_face (w->crb, "Roboto", CAIRO_FONT_SLANT_NORMAL,
313                                CAIRO_FONT_WEIGHT_NORMAL);
314 
315     w->image = NULL;
316 
317     w->flags = IS_WIDGET | USE_TRANSPARENCY;
318     w->flags &= ~NO_AUTOREPEAT;
319     w->flags &= ~FAST_REDRAW;
320     w->flags &= ~HIDE_ON_DELETE;
321     w->flags &= ~REUSE_IMAGE;
322     w->app = app;
323     w->parent = parent;
324     w->parent_struct = NULL;
325     w->label = NULL;
326     memset(w->input_label, 0, 32 * (sizeof w->input_label[0]));
327     w->state = 0;
328     w->data = 0;
329     w->x = x;
330     w->y = y;
331     w->width = width;
332     w->height = height;
333     w->scale.gravity = CENTER;
334     w->scale.init_width = width;
335     w->scale.init_height = height;
336     w->scale.init_x   = x;
337     w->scale.init_y   = y;
338     w->scale.scale_x  = 0.0;
339     w->scale.scale_y  = 0.0;
340     w->scale.cscale_x = 1.0;
341     w->scale.cscale_y = 1.0;
342     w->scale.rcscale_x = 1.0;
343     w->scale.rcscale_y = 1.0;
344     w->scale.ascale   = 1.0;
345     w->adj_x = NULL;
346     w->adj_y = NULL;
347     w->adj   = NULL;
348     w->childlist = (Childlist_t*)malloc(sizeof(Childlist_t));
349     assert(w->childlist != NULL);
350     childlist_init(w->childlist);
351     childlist_add_child(parent->childlist, w);
352     w->event_callback = widget_event_loop;
353     w->func.expose_callback = _dummy_callback;
354     w->func.configure_callback = configure_event;
355     w->func.button_press_callback = _dummy1_callback;
356     w->func.button_release_callback = _dummy1_callback;
357     w->func.motion_callback = _dummy1_callback;
358     w->func.adj_callback = transparent_draw;
359     w->func.value_changed_callback = _dummy_callback;
360     w->func.key_press_callback = _dummy1_callback;
361     w->func.key_release_callback = _dummy1_callback;
362     w->func.enter_callback = _dummy_callback;
363     w->func.leave_callback = _dummy_callback;
364     w->func.user_callback = _dummy_callback;
365     w->func.mem_free_callback = _dummy_callback;
366     w->func.configure_notify_callback = _dummy_callback;
367     w->func.map_notify_callback = _dummy_callback;
368     w->func.unmap_notify_callback = _dummy_callback;
369     w->func.dialog_callback = _dummy_callback;
370 
371     childlist_add_child(app->childlist,w);
372     //XMapWindow(app->dpy, w->widget);
373     debug_print("size of Widget_t = %ld\n", sizeof(struct Widget_t));
374     return w;
375 }
376 
connect_func(void (** event)(),void (* handler)())377 void connect_func(void (**event)(), void (*handler)()) {
378     debug_print("address of a is: %p\n", (void*)event);
379     debug_print("address of b is: %p\n", (void*)handler);
380     *event = handler;
381     debug_print("address of a is: %p\n", (void*)(*event));
382 }
383 
widget_set_title(Widget_t * w,const char * title)384 void widget_set_title(Widget_t *w, const char *title) {
385     XStoreName(w->app->dpy, w->widget, title);
386 }
387 
widget_show(Widget_t * w)388 void widget_show(Widget_t *w) {
389     w->func.map_notify_callback(w, NULL);
390     XMapWindow(w->app->dpy, w->widget);
391 }
392 
widget_hide(Widget_t * w)393 void widget_hide(Widget_t *w) {
394     int i=0;
395     for(;i<w->childlist->elem;i++) {
396         widget_hide(w->childlist->childs[i]);
397     }
398     w->func.unmap_notify_callback(w, NULL);
399     XUnmapWindow(w->app->dpy, w->widget);
400 }
401 
widget_show_all(Widget_t * w)402 void widget_show_all(Widget_t *w) {
403     if (w->flags & IS_POPUP || w->flags & IS_TOOLTIP) {
404         return;
405     } else {
406         w->func.map_notify_callback(w, NULL);
407         XMapWindow(w->app->dpy, w->widget);
408         int i=0;
409         for(;i<w->childlist->elem;i++) {
410             widget_show_all(w->childlist->childs[i]);
411         }
412     }
413 }
414 
pop_widget_show_all(Widget_t * w)415 void pop_widget_show_all(Widget_t *w) {
416     w->func.map_notify_callback(w, NULL);
417     XMapWindow(w->app->dpy, w->widget);
418     int i=0;
419     for(;i<w->childlist->elem;i++) {
420         pop_widget_show_all(w->childlist->childs[i]);
421     }
422 }
423 
show_tooltip(Widget_t * wid)424 void show_tooltip(Widget_t *wid) {
425     int i = 0;
426     for(;i<wid->childlist->elem;i++) {
427         Widget_t *w = wid->childlist->childs[i];
428         if (w->flags & IS_TOOLTIP) {
429             unsigned int mask;
430             int x, y, rx, ry;
431             Window child, root;
432             XQueryPointer(wid->app->dpy, wid->widget, &root, &child, &rx, &ry, &x, &y, &mask);
433             int x1, y1;
434             XTranslateCoordinates( wid->app->dpy, wid->widget, DefaultRootWindow(wid->app->dpy),
435                                                                        x, y, &x1, &y1, &child );
436             XMoveWindow(w->app->dpy,w->widget,x1+10, y1-10);
437             widget_show(w);
438             break;
439         }
440     }
441 }
442 
hide_tooltip(Widget_t * wid)443 void hide_tooltip(Widget_t *wid) {
444     int i = 0;
445     for(;i<wid->childlist->elem;i++) {
446         Widget_t *w = wid->childlist->childs[i];
447         if (w->flags & IS_TOOLTIP) {
448             widget_hide(w);
449             break;
450         }
451     }
452 }
453 
get_toplevel_widget(Xputty * main)454 Widget_t *get_toplevel_widget(Xputty *main) {
455     return  main->childlist->childs[0];
456 }
457 
expose_widget(Widget_t * w)458 void expose_widget(Widget_t *w) {
459     XEvent exp;
460     memset(&exp, 0, sizeof(exp));
461     exp.type = Expose;
462     exp.xexpose.window = w->widget;
463     XSendEvent(w->app->dpy, w->widget, False, ExposureMask, (XEvent *)&exp);
464 }
465 
transparent_draw(void * w_,void * user_data)466 void transparent_draw(void * w_, void* user_data) {
467     Widget_t *wid = (Widget_t*)w_;
468 
469     cairo_push_group (wid->cr);
470 
471     if (wid->flags & USE_TRANSPARENCY) {
472         Widget_t *parent = (Widget_t*)wid->parent;
473         XWindowAttributes attrs;
474         XGetWindowAttributes(wid->app->dpy, wid->widget, &attrs);
475 
476         debug_print("Widget_t _transparency \n");
477         cairo_set_source_surface (wid->crb, parent->buffer, -attrs.x, -attrs.y);
478         cairo_paint (wid->crb);
479     }
480 
481     cairo_push_group (wid->crb);
482     wid->func.expose_callback(wid, user_data);
483     cairo_pop_group_to_source (wid->crb);
484     cairo_paint (wid->crb);
485 
486     cairo_set_source_surface (wid->cr, wid->buffer,0,0);
487     cairo_paint (wid->cr);
488 
489     cairo_pop_group_to_source (wid->cr);
490     cairo_paint (wid->cr);
491     _propagate_child_expose(wid);
492 }
493 
widget_event_loop(void * w_,void * event,Xputty * main,void * user_data)494 void widget_event_loop(void *w_, void* event, Xputty *main, void* user_data) {
495     Widget_t *wid = (Widget_t*)w_;
496     XEvent *xev = (XEvent*)event;
497     if (XFilterEvent(xev, wid->widget))
498         return;
499 
500     switch(xev->type) {
501         case ConfigureNotify:
502             wid->func.configure_callback(w_, user_data);
503             debug_print("Widget_t ConfigureNotify \n");
504         break;
505 
506         case Expose:
507             if (xev->xexpose.count == 0) {
508                 transparent_draw(w_, user_data);
509                 debug_print("Widget_t Expose \n");
510             }
511         break;
512 
513         case ButtonPress:
514             if (wid->state == 4) break;
515             if (wid->flags & HAS_TOOLTIP) hide_tooltip(wid);
516             _button_press(wid, &xev->xbutton, user_data);
517             debug_print("Widget_t  ButtonPress %i\n", xev->xbutton.button);
518         break;
519 
520         case ButtonRelease:
521             _check_grab(wid, &xev->xbutton, main);
522             if (wid->state == 4) break;
523             _has_pointer(wid, &xev->xbutton);
524             if(wid->flags & HAS_POINTER) wid->state = 1;
525             else wid->state = 0;
526             _check_enum(wid, &xev->xbutton);
527             wid->func.button_release_callback(w_, &xev->xbutton, user_data);
528             debug_print("Widget_t  ButtonRelease %i\n", xev->xbutton.button);
529         break;
530 
531         case KeyPress:
532             if (wid->state == 4) break;
533             _check_keymap(wid, xev->xkey);
534             wid->func.key_press_callback(w_, &xev->xkey, user_data);
535             debug_print("Widget_t KeyPress %u\n", xev->xkey.keycode);
536         break;
537 
538         case KeyRelease:
539             if (wid->state == 4) break;
540             {
541             unsigned short is_retriggered = 0;
542             if(wid->flags & NO_AUTOREPEAT) {
543                 if (XEventsQueued(main->dpy, QueuedAlready)) {
544                     XEvent nev;
545                     XPeekEvent(main->dpy, &nev);
546                     if (nev.type == KeyPress && nev.xkey.time == xev->xkey.time &&
547                         nev.xkey.keycode == xev->xkey.keycode &&
548                         (nev.xkey.keycode > 119 || nev.xkey.keycode < 110)) {
549                         XNextEvent (main->dpy, xev);
550                         is_retriggered = 1;
551                     }
552                 }
553             }
554             if (!is_retriggered) {
555                 wid->func.key_release_callback(w_, &xev->xkey, user_data);
556                 debug_print("Widget_t KeyRelease %u\n", xev->xkey.keycode);
557             }
558         }
559         break;
560 
561         case LeaveNotify:
562             wid->flags &= ~HAS_FOCUS;
563             if (wid->state == 4) break;
564             if(!(xev->xcrossing.state & Button1Mask)) {
565                 wid->state = 0;
566                 wid->func.leave_callback(w_, user_data);
567             }
568             if (wid->flags & HAS_TOOLTIP) hide_tooltip(wid);
569             debug_print("Widget_t LeaveNotify \n");
570         break;
571 
572         case EnterNotify:
573             wid->flags |= HAS_FOCUS;
574             if (wid->state == 4) break;
575             if(!(xev->xcrossing.state & Button1Mask)) {
576                 wid->state = 1;
577                 wid->func.enter_callback(w_, user_data);
578                 if (wid->flags & HAS_TOOLTIP) show_tooltip(wid);
579                 else _hide_all_tooltips(wid);
580             }
581             debug_print("Widget_t EnterNotify \n");
582         break;
583 
584         case MotionNotify:
585             if (wid->state == 4) break;
586             adj_set_motion_state(wid, xev->xmotion.x, xev->xmotion.y);
587             wid->func.motion_callback(w_,&xev->xmotion, user_data);
588             debug_print("Widget_t MotionNotify x = %i Y = %i \n",xev->xmotion.x,xev->xmotion.y );
589         break;
590 
591         case ClientMessage:
592             if (xev->xclient.message_type == XInternAtom(wid->app->dpy, "WIDGET_DESTROY", 1)) {
593                 int ch = childlist_has_child(wid->childlist);
594                 if (ch) {
595                     int i = ch;
596                     for(;i>0;i--) {
597                         quit_widget(wid->childlist->childs[i-1]);
598                     }
599                     quit_widget(wid);
600                 } else {
601                     destroy_widget(wid,main);
602                 }
603             }
604 
605         default:
606         break;
607     }
608     if (main->queue_event) {
609         main->queue_event = false;
610         transparent_draw(w_, user_data);
611     }
612 }
613 
send_configure_event(Widget_t * w,int x,int y,int width,int height)614 void send_configure_event(Widget_t *w,int x, int y, int width, int height) {
615     XConfigureEvent notify;
616     memset(&notify, 0, sizeof(notify));
617     notify.type = ConfigureNotify;
618     notify.display = w->app->dpy;
619     notify.send_event = True;
620     notify.event = w->widget;
621     notify.window = w->widget;
622     notify.x = x;
623     notify.y = y;
624     notify.width = width;
625     notify.height = height;
626     notify.border_width = 0;
627     notify.above = None;
628     notify.override_redirect = 1;
629     XSendEvent( w->app->dpy, w->widget, true, StructureNotifyMask, (XEvent*)&notify );
630 }
631 
send_button_press_event(Widget_t * w)632 void send_button_press_event(Widget_t *w) {
633     XEvent event;
634     memset(&event, 0, sizeof(XEvent));
635     XWindowAttributes attr;
636     XGetWindowAttributes(w->app->dpy, w->widget, &attr);
637     event.type = ButtonPress;
638     event.xbutton.same_screen = true;
639     event.xbutton.root = None;
640     event.xbutton.window = w->widget;
641     event.xbutton.subwindow = None;
642     event.xbutton.x = 1;
643     event.xbutton.y = 1;
644     event.xbutton.x_root = attr.x;
645     event.xbutton.y_root = attr.y;
646     event.xbutton.state = 0;
647     event.xbutton.button = Button1;
648     XSendEvent(w->app->dpy, PointerWindow, True, ButtonPressMask, &event);
649 }
650 
send_button_release_event(Widget_t * w)651 void send_button_release_event(Widget_t *w) {
652     XEvent event;
653     memset(&event, 0, sizeof(XEvent));
654     XWindowAttributes attr;
655     XGetWindowAttributes(w->app->dpy, w->widget, &attr);
656     event.type = ButtonRelease;
657     event.xbutton.same_screen = true;
658     event.xbutton.root = None;
659     event.xbutton.window = w->widget;
660     event.xbutton.subwindow = None;
661     event.xbutton.x = 1;
662     event.xbutton.y = 1;
663     event.xbutton.x_root = attr.x;
664     event.xbutton.y_root = attr.y;
665     event.xbutton.state = 0;
666     event.xbutton.button = Button1;
667     XSendEvent(w->app->dpy, PointerWindow, True, ButtonReleaseMask, &event);
668 }
669 
send_systray_message(Widget_t * w)670 void send_systray_message(Widget_t *w) {
671     XEvent event;
672     Screen *xscreen;
673     char buf[256];
674     buf[0]=0;
675 
676     xscreen=DefaultScreenOfDisplay(w->app->dpy);
677     sprintf(buf,"_NET_SYSTEM_TRAY_S%d",XScreenNumberOfScreen (xscreen));
678     Atom selection_atom = XInternAtom (w->app->dpy,buf,0);
679 
680     Window tray = XGetSelectionOwner (w->app->dpy,selection_atom);
681     Atom visualatom = XInternAtom(w->app->dpy, "_NET_SYSTEM_TRAY_VISUAL", False);
682     VisualID value = XVisualIDFromVisual(DefaultVisual(w->app->dpy, DefaultScreen(w->app->dpy)));
683     XChangeProperty(w->app->dpy, w->widget, visualatom, XA_VISUALID, 32,
684             PropModeReplace, (unsigned char*)&value, 1);
685 
686     if ( tray != None)
687         XSelectInput (w->app->dpy,tray,StructureNotifyMask);
688 
689     memset(&event, 0, sizeof(event));
690     event.xclient.type = ClientMessage;
691     event.xclient.window = tray;
692     event.xclient.message_type = XInternAtom (w->app->dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
693     event.xclient.format = 32;
694     event.xclient.data.l[0] = CurrentTime;
695     event.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
696     event.xclient.data.l[2] = w->widget;
697     event.xclient.data.l[3] = 0;
698     event.xclient.data.l[4] = 0;
699 
700     XSendEvent(w->app->dpy, tray, False, NoEventMask, &event);
701 }
702 
quit(Widget_t * w)703 void quit(Widget_t *w) {
704     Atom WM_DELETE_WINDOW = XInternAtom(w->app->dpy, "WM_DELETE_WINDOW", True);
705     XClientMessageEvent xevent;
706     xevent.type = ClientMessage;
707     xevent.message_type = WM_DELETE_WINDOW;
708     xevent.display = w->app->dpy;
709     xevent.window = get_toplevel_widget(w->app)->widget;
710     xevent.format = 16;
711     xevent.data.l[0] = WM_DELETE_WINDOW;
712     XSendEvent(w->app->dpy, w->widget, 0, 0, (XEvent *)&xevent);
713 }
714 
quit_widget(Widget_t * w)715 void quit_widget(Widget_t *w) {
716     Atom QUIT_WIDGET = XInternAtom(w->app->dpy, "WIDGET_DESTROY", False);
717     XClientMessageEvent xevent;
718     xevent.type = ClientMessage;
719     xevent.message_type = QUIT_WIDGET;
720     xevent.display = w->app->dpy;
721     xevent.window = w->widget;
722     xevent.format = 16;
723     xevent.data.l[0] = 1;
724     XSendEvent(w->app->dpy, w->widget, 0, 0, (XEvent *)&xevent);
725 }
726 
727