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 XDestroyIC(w->xic);
105 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 wid->func.configure_notify_callback(wid,NULL);
135 }
136
resize_childs(Widget_t * w)137 void resize_childs(Widget_t *w) {
138 _resize_childs(w);
139 }
140
widget_reset_scale(Widget_t * w)141 void widget_reset_scale(Widget_t *w) {
142 cairo_scale(w->crb, w->scale.cscale_x,w->scale.cscale_y);
143 }
144
widget_set_scale(Widget_t * w)145 void widget_set_scale(Widget_t *w) {
146 cairo_scale(w->crb, w->scale.rcscale_x,w->scale.rcscale_y);
147 }
148
create_window(Xputty * app,Window win,int x,int y,int width,int height)149 Widget_t *create_window(Xputty *app, Window win,
150 int x, int y, int width, int height) {
151
152 Widget_t *w = (Widget_t*)malloc(sizeof(Widget_t));
153 assert(w != NULL);
154 debug_print("assert(w)\n");
155 XSetWindowAttributes attributes;
156 attributes.save_under = True;
157 attributes.override_redirect = 0;
158
159 long event_mask = StructureNotifyMask|ExposureMask|KeyPressMask
160 |EnterWindowMask|LeaveWindowMask|ButtonReleaseMask
161 |ButtonPressMask|Button1MotionMask;
162
163
164
165 w->widget = XCreateWindow(app->dpy, win , x, y, width, height, 0,
166 CopyFromParent, InputOutput, CopyFromParent,
167 CopyFromParent, &attributes);
168 debug_print("XCreateWindow\n");
169
170 XSetLocaleModifiers("");
171 w->xim = XOpenIM(app->dpy, 0, 0, 0);
172 if(!w->xim){
173 XSetLocaleModifiers("@im=none");
174 w->xim = XOpenIM(app->dpy, 0, 0, 0);
175 }
176
177 w->xic = XCreateIC(w->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
178 XNClientWindow, w->widget, XNFocusWindow, w->widget, NULL);
179
180 XSetICFocus(w->xic);
181
182 XSelectInput(app->dpy, w->widget, event_mask);
183
184 XSizeHints* win_size_hints;
185 win_size_hints = XAllocSizeHints();
186 win_size_hints->flags = PMinSize|PBaseSize|PWinGravity;
187 win_size_hints->min_width = width/2;
188 win_size_hints->min_height = height/2;
189 win_size_hints->base_width = width;
190 win_size_hints->base_height = height;
191 win_size_hints->win_gravity = CenterGravity;
192 XSetWMNormalHints(app->dpy, w->widget, win_size_hints);
193 XFree(win_size_hints);
194
195 w->surface = cairo_xlib_surface_create (app->dpy, w->widget,
196 DefaultVisual(app->dpy, DefaultScreen(app->dpy)), width, height);
197
198 assert(cairo_surface_status(w->surface) == CAIRO_STATUS_SUCCESS);
199 w->cr = cairo_create(w->surface);
200 cairo_select_font_face (w->cr, "Roboto", CAIRO_FONT_SLANT_NORMAL,
201 CAIRO_FONT_WEIGHT_NORMAL);
202
203 w->buffer = cairo_surface_create_similar (w->surface,
204 CAIRO_CONTENT_COLOR_ALPHA, width, height);
205 assert(cairo_surface_status(w->buffer) == CAIRO_STATUS_SUCCESS);
206 w->crb = cairo_create (w->buffer);
207 cairo_select_font_face (w->crb, "Roboto", CAIRO_FONT_SLANT_NORMAL,
208 CAIRO_FONT_WEIGHT_NORMAL);
209
210 w->image = NULL;
211
212 w->flags = IS_WINDOW;
213 w->flags &= ~NO_AUTOREPEAT;
214 w->flags &= ~FAST_REDRAW;
215 w->flags &= ~HIDE_ON_DELETE;
216 w->flags &= ~REUSE_IMAGE;
217 w->flags &= ~NO_PROPAGATE;
218 w->flags &= ~IS_SUBMENU;
219 w->app = app;
220 w->parent = &win;
221 w->parent_struct = NULL;
222 w->label = NULL;
223 memset(w->input_label, 0, 32 * (sizeof w->input_label[0]));
224 w->state = 0;
225 w->double_click = 0;
226 w->data = 0;
227 w->x = x;
228 w->y = y;
229 w->width = width;
230 w->height = height;
231 w->scale.init_x = x;
232 w->scale.init_y = y;
233 w->scale.init_width = width;
234 w->scale.init_height = height;
235 w->scale.scale_x = 0.0;
236 w->scale.scale_y = 0.0;
237 w->scale.cscale_x = 1.0;
238 w->scale.cscale_y = 1.0;
239 w->scale.rcscale_x = 1.0;
240 w->scale.rcscale_y = 1.0;
241 w->scale.ascale = 1.0;
242 w->scale.gravity = CENTER;
243 w->adj_x = NULL;
244 w->adj_y = NULL;
245 w->adj = NULL;
246 w->childlist = (Childlist_t*)malloc(sizeof(Childlist_t));
247 assert(w->childlist != NULL);
248 childlist_init(w->childlist);
249 w->event_callback = widget_event_loop;
250 w->func.expose_callback = _dummy_callback;
251 w->func.configure_callback = configure_event;
252 w->func.button_press_callback = _dummy1_callback;
253 w->func.button_release_callback = _dummy1_callback;
254 w->func.double_click_callback = _dummy1_callback;
255 w->func.motion_callback = _dummy1_callback;
256 w->func.adj_callback = transparent_draw;
257 w->func.value_changed_callback = _dummy_callback;
258 w->func.key_press_callback = _dummy1_callback;
259 w->func.key_release_callback = _dummy1_callback;
260 w->func.enter_callback = _dummy_callback;
261 w->func.leave_callback = _dummy_callback;
262 w->func.user_callback = _dummy_callback;
263 w->func.mem_free_callback = _dummy_callback;
264 w->func.configure_notify_callback = _dummy_callback;
265 w->func.map_notify_callback = _dummy_callback;
266 w->func.unmap_notify_callback = _dummy_callback;
267 w->func.dialog_callback = _dummy_callback;
268 w->func.dnd_notify_callback = _dummy_callback;
269 w->xpaste_callback = _dummy_callback;
270
271 childlist_add_child(app->childlist,w);
272 //XMapWindow(app->dpy, w->widget);
273 debug_print("size of Func_t = %lu\n", sizeof(w->func)/sizeof(void*));
274 return w;
275 }
276
create_widget(Xputty * app,Widget_t * parent,int x,int y,int width,int height)277 Widget_t *create_widget(Xputty *app, Widget_t *parent,
278 int x, int y, int width, int height) {
279
280 Widget_t *w = (Widget_t*)malloc(sizeof(Widget_t));
281 assert(w != NULL);
282 debug_print("assert(w)\n");
283 XSetWindowAttributes attributes;
284 attributes.save_under = True;
285 attributes.override_redirect = True;
286
287 long event_mask = StructureNotifyMask|ExposureMask|KeyPressMask
288 |EnterWindowMask|LeaveWindowMask|ButtonReleaseMask
289 |ButtonPressMask|Button1MotionMask;
290
291
292
293 w->widget = XCreateWindow(app->dpy, parent->widget , x, y, width, height, 0,
294 CopyFromParent, InputOutput, CopyFromParent,
295 CopyFromParent|CWOverrideRedirect, &attributes);
296 debug_print("XCreateWindow\n");
297
298 XSetLocaleModifiers("");
299 w->xim = XOpenIM(app->dpy, 0, 0, 0);
300 if(!w->xim){
301 XSetLocaleModifiers("@im=none");
302 w->xim = XOpenIM(app->dpy, 0, 0, 0);
303 }
304
305 w->xic = XCreateIC(w->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
306 XNClientWindow, w->widget, XNFocusWindow, w->widget, NULL);
307
308 XSetICFocus(w->xic);
309
310 XSelectInput(app->dpy, w->widget, event_mask);
311
312 w->surface = cairo_xlib_surface_create (app->dpy, w->widget,
313 DefaultVisual(app->dpy, DefaultScreen(app->dpy)), width, height);
314 assert(cairo_surface_status(w->surface) == CAIRO_STATUS_SUCCESS);
315 w->cr = cairo_create(w->surface);
316 cairo_select_font_face (w->cr, "Roboto", CAIRO_FONT_SLANT_NORMAL,
317 CAIRO_FONT_WEIGHT_NORMAL);
318
319 w->buffer = cairo_surface_create_similar (w->surface,
320 CAIRO_CONTENT_COLOR_ALPHA, width, height);
321 assert(cairo_surface_status(w->buffer) == CAIRO_STATUS_SUCCESS);
322 w->crb = cairo_create (w->buffer);
323 cairo_select_font_face (w->crb, "Roboto", CAIRO_FONT_SLANT_NORMAL,
324 CAIRO_FONT_WEIGHT_NORMAL);
325
326 w->image = NULL;
327
328 w->flags = IS_WIDGET | USE_TRANSPARENCY;
329 w->flags &= ~NO_AUTOREPEAT;
330 w->flags &= ~FAST_REDRAW;
331 w->flags &= ~HIDE_ON_DELETE;
332 w->flags &= ~REUSE_IMAGE;
333 w->flags &= ~NO_PROPAGATE;
334 w->flags &= ~IS_SUBMENU;
335 w->app = app;
336 w->parent = parent;
337 w->parent_struct = NULL;
338 w->private_struct = NULL;
339 w->label = NULL;
340 memset(w->input_label, 0, 32 * (sizeof w->input_label[0]));
341 w->state = 0;
342 w->double_click = 0;
343 w->data = 0;
344 w->x = x;
345 w->y = y;
346 w->width = width;
347 w->height = height;
348 w->scale.gravity = CENTER;
349 w->scale.init_width = width;
350 w->scale.init_height = height;
351 w->scale.init_x = x;
352 w->scale.init_y = y;
353 w->scale.scale_x = 0.0;
354 w->scale.scale_y = 0.0;
355 w->scale.cscale_x = 1.0;
356 w->scale.cscale_y = 1.0;
357 w->scale.rcscale_x = 1.0;
358 w->scale.rcscale_y = 1.0;
359 w->scale.ascale = 1.0;
360 w->adj_x = NULL;
361 w->adj_y = NULL;
362 w->adj = NULL;
363 w->childlist = (Childlist_t*)malloc(sizeof(Childlist_t));
364 assert(w->childlist != NULL);
365 childlist_init(w->childlist);
366 childlist_add_child(parent->childlist, w);
367 w->event_callback = widget_event_loop;
368 w->func.expose_callback = _dummy_callback;
369 w->func.configure_callback = configure_event;
370 w->func.button_press_callback = _dummy1_callback;
371 w->func.button_release_callback = _dummy1_callback;
372 w->func.double_click_callback = _dummy1_callback;
373 w->func.motion_callback = _dummy1_callback;
374 w->func.adj_callback = transparent_draw;
375 w->func.value_changed_callback = _dummy_callback;
376 w->func.key_press_callback = _dummy1_callback;
377 w->func.key_release_callback = _dummy1_callback;
378 w->func.enter_callback = _dummy_callback;
379 w->func.leave_callback = _dummy_callback;
380 w->func.user_callback = _dummy_callback;
381 w->func.mem_free_callback = _dummy_callback;
382 w->func.configure_notify_callback = _dummy_callback;
383 w->func.map_notify_callback = _dummy_callback;
384 w->func.unmap_notify_callback = _dummy_callback;
385 w->func.dialog_callback = _dummy_callback;
386 w->func.dnd_notify_callback = _dummy_callback;
387 w->xpaste_callback = _dummy_callback;
388
389 childlist_add_child(app->childlist,w);
390 //XMapWindow(app->dpy, w->widget);
391 debug_print("size of Widget_t = %ld\n", sizeof(struct Widget_t));
392 return w;
393 }
394
connect_func(void (** event)(),void (* handler)())395 void connect_func(void (**event)(), void (*handler)()) {
396 debug_print("address of a is: %p\n", (void*)event);
397 debug_print("address of b is: %p\n", (void*)handler);
398 *event = handler;
399 debug_print("address of a is: %p\n", (void*)(*event));
400 }
401
widget_set_title(Widget_t * w,const char * title)402 void widget_set_title(Widget_t *w, const char *title) {
403 XStoreName(w->app->dpy, w->widget, title);
404 XChangeProperty(w->app->dpy, w->widget,
405 XInternAtom(w->app->dpy, "_NET_WM_NAME", False),
406 XInternAtom(w->app->dpy, "UTF8_STRING", False),
407 8, PropModeReplace, (unsigned char *) title,
408 strlen(title));
409 }
410
widget_show(Widget_t * w)411 void widget_show(Widget_t *w) {
412 w->func.map_notify_callback(w, NULL);
413 XMapWindow(w->app->dpy, w->widget);
414 }
415
widget_hide(Widget_t * w)416 void widget_hide(Widget_t *w) {
417 int i=0;
418 for(;i<w->childlist->elem;i++) {
419 widget_hide(w->childlist->childs[i]);
420 }
421 w->func.unmap_notify_callback(w, NULL);
422 XUnmapWindow(w->app->dpy, w->widget);
423 }
424
widget_show_all(Widget_t * w)425 void widget_show_all(Widget_t *w) {
426 if (w->flags & IS_POPUP || w->flags & IS_TOOLTIP ||
427 w->flags & IS_SUBMENU) {
428 return;
429 } else {
430 w->func.map_notify_callback(w, NULL);
431 XMapWindow(w->app->dpy, w->widget);
432 int i=0;
433 for(;i<w->childlist->elem;i++) {
434 widget_show_all(w->childlist->childs[i]);
435 }
436 }
437 }
438
pop_widget_show_all(Widget_t * w)439 void pop_widget_show_all(Widget_t *w) {
440 if (w->flags & IS_SUBMENU) return;
441 w->func.map_notify_callback(w, NULL);
442 XMapWindow(w->app->dpy, w->widget);
443 int i=0;
444 for(;i<w->childlist->elem;i++) {
445 pop_widget_show_all(w->childlist->childs[i]);
446 }
447 }
448
submenu_widget_show_all(Widget_t * w)449 void submenu_widget_show_all(Widget_t *w) {
450 w->func.map_notify_callback(w, NULL);
451 XMapWindow(w->app->dpy, w->widget);
452 int i=0;
453 for(;i<w->childlist->elem;i++) {
454 submenu_widget_show_all(w->childlist->childs[i]);
455 }
456 }
457
show_tooltip(Widget_t * wid)458 void show_tooltip(Widget_t *wid) {
459 int i = 0;
460 for(;i<wid->childlist->elem;i++) {
461 Widget_t *w = wid->childlist->childs[i];
462 if (w->flags & IS_TOOLTIP) {
463 XWindowAttributes attrs;
464 XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
465 int width_t = attrs.width;
466 unsigned int mask;
467 int x, y, rx, ry;
468 Window child, root;
469 XQueryPointer(wid->app->dpy, wid->widget, &root, &child, &rx, &ry, &x, &y, &mask);
470 int x1, y1;
471 XTranslateCoordinates( wid->app->dpy, wid->widget, DefaultRootWindow(wid->app->dpy),
472 x, y, &x1, &y1, &child );
473 int snum = DefaultScreen(wid->app->dpy);
474 int screen_width = DisplayWidth(wid->app->dpy, snum);
475 if (x1+10+width_t > screen_width) x1 = x1-width_t-10;
476
477 XMoveWindow(w->app->dpy,w->widget,x1+10, y1-10);
478 widget_show(w);
479 break;
480 }
481 }
482 }
483
hide_tooltip(Widget_t * wid)484 void hide_tooltip(Widget_t *wid) {
485 int i = 0;
486 for(;i<wid->childlist->elem;i++) {
487 Widget_t *w = wid->childlist->childs[i];
488 if (w->flags & IS_TOOLTIP) {
489 widget_hide(w);
490 break;
491 }
492 }
493 }
494
get_toplevel_widget(Xputty * main)495 Widget_t *get_toplevel_widget(Xputty *main) {
496 return main->childlist->childs[0];
497 }
498
expose_widget(Widget_t * w)499 void expose_widget(Widget_t *w) {
500 XEvent exp;
501 memset(&exp, 0, sizeof(exp));
502 exp.type = Expose;
503 exp.xexpose.window = w->widget;
504 XSendEvent(w->app->dpy, w->widget, False, ExposureMask, (XEvent *)&exp);
505 }
506
transparent_draw(void * w_,void * user_data)507 void transparent_draw(void * w_, void* user_data) {
508 Widget_t *wid = (Widget_t*)w_;
509
510 cairo_push_group (wid->cr);
511
512 if (wid->flags & USE_TRANSPARENCY) {
513 Widget_t *parent = (Widget_t*)wid->parent;
514 XWindowAttributes attrs;
515 XGetWindowAttributes(wid->app->dpy, wid->widget, &attrs);
516
517 debug_print("Widget_t _transparency \n");
518 cairo_set_source_surface (wid->crb, parent->buffer, -attrs.x, -attrs.y);
519 cairo_paint (wid->crb);
520 }
521
522 cairo_push_group (wid->crb);
523 wid->func.expose_callback(wid, user_data);
524 cairo_pop_group_to_source (wid->crb);
525 cairo_paint (wid->crb);
526
527 cairo_set_source_surface (wid->cr, wid->buffer,0,0);
528 cairo_paint (wid->cr);
529
530 cairo_pop_group_to_source (wid->cr);
531 cairo_paint (wid->cr);
532 _propagate_child_expose(wid);
533 }
534
widget_event_loop(void * w_,void * event,Xputty * main,void * user_data)535 void widget_event_loop(void *w_, void* event, Xputty *main, void* user_data) {
536 Widget_t *wid = (Widget_t*)w_;
537 XEvent *xev = (XEvent*)event;
538
539 switch(xev->type) {
540 case ConfigureNotify:
541 wid->func.configure_callback(w_, user_data);
542 //transparent_draw(w_, user_data);
543 debug_print("Widget_t ConfigureNotify \n");
544 break;
545
546 case Expose:
547 if (xev->xexpose.count == 0) {
548 transparent_draw(w_, user_data);
549 debug_print("Widget_t Expose \n");
550 }
551 break;
552
553 case ButtonPress:
554 if (wid->state == 4) break;
555 if (wid->flags & HAS_TOOLTIP) hide_tooltip(wid);
556 _button_press(wid, &xev->xbutton, user_data);
557 debug_print("Widget_t ButtonPress %i\n", xev->xbutton.button);
558 break;
559
560 case ButtonRelease:
561 {
562 XButtonEvent *xbutton = &xev->xbutton;
563 _check_grab(wid, xbutton, main);
564 _check_submenu(wid, xbutton, main);
565 if (wid->state == 4) break;
566 if (xbutton->button == Button1) {
567 if (xbutton->time < wid->double_click+300) {
568 wid->func.double_click_callback(wid, xbutton, user_data);
569 break;
570 }
571 wid->double_click = xbutton->time;
572 }
573 _has_pointer(wid, &xev->xbutton);
574 if(wid->flags & HAS_POINTER) wid->state = 1;
575 else wid->state = 0;
576 _check_enum(wid, xbutton);
577 wid->func.button_release_callback(w_, xbutton, user_data);
578 debug_print("Widget_t ButtonRelease %i\n", xev->xbutton.button);
579 }
580 break;
581
582 case KeyPress:
583 if (wid->state == 4) break;
584 _check_keymap(wid, xev->xkey);
585 wid->func.key_press_callback(w_, &xev->xkey, user_data);
586 debug_print("Widget_t KeyPress %u\n", xev->xkey.keycode);
587 break;
588
589 case KeyRelease:
590 if (wid->state == 4) break;
591 {
592 unsigned short is_retriggered = 0;
593 if(wid->flags & NO_AUTOREPEAT) {
594 if (XEventsQueued(main->dpy, QueuedAfterReading)) {
595 XEvent nev;
596 XPeekEvent(main->dpy, &nev);
597 if (nev.type == KeyPress && nev.xkey.time == xev->xkey.time &&
598 nev.xkey.keycode == xev->xkey.keycode &&
599 (nev.xkey.keycode > 119 || nev.xkey.keycode < 110)) {
600 XNextEvent (main->dpy, xev);
601 is_retriggered = 1;
602 }
603 }
604 }
605 if (!is_retriggered) {
606 wid->func.key_release_callback(w_, &xev->xkey, user_data);
607 debug_print("Widget_t KeyRelease %u\n", xev->xkey.keycode);
608 }
609 }
610 break;
611
612 case LeaveNotify:
613 wid->flags &= ~HAS_FOCUS;
614 if (wid->state == 4) break;
615 if(!(xev->xcrossing.state & Button1Mask) &&
616 !(xev->xcrossing.state & Button2Mask) &&
617 !(xev->xcrossing.state & Button3Mask)) {
618 wid->state = 0;
619 wid->func.leave_callback(w_, user_data);
620 }
621 if (wid->flags & HAS_TOOLTIP) hide_tooltip(wid);
622 debug_print("Widget_t LeaveNotify \n");
623 break;
624
625 case EnterNotify:
626 wid->flags |= HAS_FOCUS;
627 if (wid->state == 4) break;
628 if(!(xev->xcrossing.state & Button1Mask) &&
629 !(xev->xcrossing.state & Button2Mask) &&
630 !(xev->xcrossing.state & Button3Mask)) {
631 wid->state = 1;
632 wid->func.enter_callback(w_, user_data);
633 if (wid->flags & HAS_TOOLTIP) show_tooltip(wid);
634 else _hide_all_tooltips(wid);
635 }
636 debug_print("Widget_t EnterNotify \n");
637 break;
638
639 case MotionNotify:
640 if (wid->state == 4) break;
641 if (xev->xmotion.state) {
642 adj_set_motion_state(wid, xev->xmotion.x, xev->xmotion.y);
643 }
644 wid->func.motion_callback(w_,&xev->xmotion, user_data);
645 debug_print("Widget_t MotionNotify x = %i Y = %i \n",xev->xmotion.x,xev->xmotion.y );
646 break;
647
648 case SelectionRequest:
649 if (xev->xselectionrequest.selection != main->selection) break;
650 send_to_clipboard(wid, xev);
651 break;
652 case SelectionClear:
653 break;
654 case SelectionNotify:
655 if (xev->xselection.property == None) {
656 wid->xpaste_callback(wid, NULL);
657 break;
658 }
659 if (xev->xselection.selection == main->selection) {
660 receive_paste_from_clipboard(wid, xev);
661 break;
662 }
663 debug_print("Widget_t SelectionNotify\n");
664 handle_drag_data(wid, xev);
665 break;
666
667 case ClientMessage:
668 if (xev->xclient.message_type == main->XdndPosition) {
669 debug_print( "XdndPosition\n");
670 send_dnd_status_event(wid, xev);
671 } else if (xev->xclient.message_type == main->XdndEnter) {
672 debug_print( "XdndEnter\n");
673 handle_dnd_enter(main, xev);
674 } else if (xev->xclient.message_type == main->XdndLeave) {
675 debug_print( "XdndLeave\n");
676 main->dnd_type = None;
677 main->dnd_source_window = 0;
678 main->dnd_version = 0;
679 } else if (xev->xclient.message_type == main->XdndDrop) {
680 if ((DND_SOURCE_WIN(xev) != main->dnd_source_window) ||
681 main->dnd_type == None || main->dnd_source_window == 0) {
682 break;
683 }
684 XConvertSelection(main->dpy, main->XdndSelection,
685 main->dnd_type, main->XdndSelection, wid->widget, CurrentTime);
686
687 send_dnd_finished_event(wid, xev);
688 } else if (xev->xclient.message_type == XInternAtom(wid->app->dpy, "WIDGET_DESTROY", 1)) {
689 int ch = childlist_has_child(wid->childlist);
690 if (ch) {
691 int i = ch;
692 for(;i>0;i--) {
693 quit_widget(wid->childlist->childs[i-1]);
694 }
695 quit_widget(wid);
696 } else {
697 destroy_widget(wid,main);
698 }
699 }
700 break;
701 default:
702 break;
703 }
704 }
705
widget_set_dnd_aware(Widget_t * w)706 void widget_set_dnd_aware(Widget_t *w) {
707 Atom dnd_version = 5;
708 XChangeProperty (w->app->dpy, w->widget, w->app->XdndAware, XA_ATOM,
709 32, PropModeReplace, (unsigned char*)&dnd_version, 1);
710 }
711
widget_set_dnd_unaware(Widget_t * w)712 void widget_set_dnd_unaware(Widget_t *w) {
713 XDeleteProperty(w->app->dpy, w->widget, w->app->XdndAware);
714 }
715
strremove(char * str,const char * sub)716 void strremove(char *str, const char *sub) {
717 char *p, *q, *r;
718 if ((q = r = strstr(str, sub)) != NULL) {
719 size_t len = strlen(sub);
720 while ((r = strstr(p = r + len, sub)) != NULL) {
721 while (p < r)
722 *q++ = *p++;
723 }
724 while ((*q++ = *p++) != '\0')
725 continue;
726 }
727 }
728
strdecode(char * target,const char * needle,const char * replacement)729 void strdecode(char *target, const char *needle, const char *replacement) {
730 char buffer[1024] = { 0 };
731 char *insert_point = &buffer[0];
732 const char *tmp = target;
733 size_t needle_len = strlen(needle);
734 size_t repl_len = strlen(replacement);
735
736 while (1) {
737 const char *p = strstr(tmp, needle);
738 if (p == NULL) {
739 strcpy(insert_point, tmp);
740 break;
741 }
742 memcpy(insert_point, tmp, p - tmp);
743 insert_point += p - tmp;
744 memcpy(insert_point, replacement, repl_len);
745 insert_point += repl_len;
746 tmp = p + needle_len;
747 }
748 strcpy(target, buffer);
749 }
750
751
handle_drag_data(Widget_t * w,XEvent * event)752 void handle_drag_data(Widget_t *w, XEvent* event) {
753 if (event->xselection.property != w->app->XdndSelection) return;
754
755 Atom type;
756 int format;
757 unsigned long count = 0, remaining;
758 unsigned char* data = 0;
759
760 XGetWindowProperty(w->app->dpy,w->widget, event->xselection.property,
761 0, 65536, True, w->app->dnd_type, &type, &format,
762 &count, &remaining, &data);
763
764 send_dnd_finished_event (w, event);
765
766 if (!data || count == 0) {
767 return;
768 }
769 char* dndfile = (char*)data;
770 strdecode(dndfile, "%20", " ");
771 strremove(dndfile, "file://");
772 w->func.dnd_notify_callback(w, (void*)&dndfile);
773 w->app->dnd_type = None;
774 w->app->dnd_source_window = 0;
775 free(data);
776 }
777
handle_dnd_enter(Xputty * main,XEvent * event)778 void handle_dnd_enter(Xputty *main, XEvent* event) {
779 main->dnd_source_window = DND_SOURCE_WIN(event);
780 main->dnd_version = 0;
781 if (DND_STATUS_ACCEPT(event)) {
782 main->dnd_version = DND_VERSION(event);
783 if (main->dnd_version > 5) return;
784 Atom type = 0;
785 int format;
786 unsigned long count, remaining;
787 unsigned char *data = 0;
788
789 XGetWindowProperty (main->dpy, main->dnd_source_window, main->XdndTypeList,
790 0, 0x8000000L, False, XA_ATOM, &type, &format, &count, &remaining, &data);
791 if (!data || type != XA_ATOM || format != 32) {
792 if (data) {
793 XFree (data);
794 }
795 return;
796 }
797 Atom* types = (Atom*)data;
798 for (unsigned long l = 1; l < count; l++) {
799 if ((types[l] == main->dnd_type_uri) ||
800 (types[l] == main->dnd_type_text) ||
801 (types[l] == main->dnd_type_utf8)) {
802 main->dnd_type = types[l];
803 break;
804 }
805 }
806 if (data) {
807 XFree (data);
808 }
809 } else {
810 for (int i = 2; i < 5; ++i) {
811 if ((event->xclient.data.l[i] == main->dnd_type_uri) ||
812 (event->xclient.data.l[i] == main->dnd_type_text) ||
813 (event->xclient.data.l[i] == main->dnd_type_utf8)) {
814 main->dnd_type = event->xclient.data.l[i];
815 break;
816 }
817 }
818 }
819 }
820
send_dnd_status_event(Widget_t * w,XEvent * event)821 void send_dnd_status_event(Widget_t *w, XEvent* event) {
822 XEvent xev;
823 memset (&xev, 0, sizeof (XEvent));
824 xev.xany.type = ClientMessage;
825 xev.xany.display = w->app->dpy;
826 xev.xclient.window = w->app->dnd_source_window;
827 xev.xclient.message_type = w->app->XdndStatus;
828 xev.xclient.format = 32;
829 xev.xclient.data.l[0] = event->xany.window;
830 xev.xclient.data.l[1] = (w->app->dnd_type != None) ? 1 : 0;
831 xev.xclient.data.l[2] = DND_DROP_TIME(event);
832 xev.xclient.data.l[3] = 0;
833 xev.xclient.data.l[4] = w->app->XdndActionCopy;
834 XSendEvent (w->app->dpy, w->app->dnd_source_window, False, NoEventMask, &xev);
835 }
836
send_dnd_finished_event(Widget_t * w,XEvent * event)837 void send_dnd_finished_event(Widget_t *w, XEvent* event) {
838 if (w->app->dnd_version < 2) {
839 return;
840 }
841 XEvent xev;
842 memset (&xev, 0, sizeof (XEvent));
843 xev.xany.type = ClientMessage;
844 xev.xany.display = w->app->dpy;
845 xev.xclient.window = w->app->dnd_source_window;
846 xev.xclient.message_type = w->app->XdndFinished;
847 xev.xclient.format = 32;
848 xev.xclient.data.l[0] = event->xany.window;
849 xev.xclient.data.l[1] = 1;
850 xev.xclient.data.l[2] = w->app->XdndActionCopy;
851 XSendEvent (w->app->dpy, w->app->dnd_source_window, False, NoEventMask, &xev);
852 }
853
have_paste(Widget_t * w)854 int have_paste(Widget_t *w) {
855 return XGetSelectionOwner(w->app->dpy,w->app->selection);
856 }
857
request_paste_from_clipboard(Widget_t * w)858 void request_paste_from_clipboard(Widget_t *w) {
859 Atom XSEL_DATA = XInternAtom(w->app->dpy, "XSEL_DATA", 0);
860 XConvertSelection(w->app->dpy, w->app->selection, w->app->UTF8, XSEL_DATA, w->widget, CurrentTime);
861 }
862
receive_paste_from_clipboard(Widget_t * w,XEvent * event)863 void receive_paste_from_clipboard(Widget_t *w, XEvent* event) {
864 if(event->xselection.property) {
865 Atom target;
866 char * data = NULL;
867 int format;
868 unsigned long N, size;
869 XGetWindowProperty(event->xselection.display, event->xselection.requestor,
870 event->xselection.property, 0L,(~0L), 0, AnyPropertyType, &target,
871 &format, &size, &N,(unsigned char**)&data);
872 if(target == w->app->UTF8 || target == XA_STRING) {
873 free(w->app->ctext);
874 w->app->ctext = NULL;
875 w->app->ctext = (unsigned char*)strndup(data, size);
876 XFree(data);
877 }
878 XDeleteProperty(event->xselection.display, event->xselection.requestor, event->xselection.property);
879 w->xpaste_callback(w, (void*)&w->app->ctext);
880 }
881 }
882
copy_to_clipboard(Widget_t * w,char * text,int size)883 void copy_to_clipboard(Widget_t *w, char* text, int size) {
884 XSetSelectionOwner (w->app->dpy, w->app->selection, w->widget, 0);
885 if (XGetSelectionOwner (w->app->dpy, w->app->selection) != w->widget) return;
886 free(w->app->ctext);
887 w->app->ctext = NULL;
888 w->app->ctext = (unsigned char*)strndup(text, size);
889 w->app->csize = size;
890 }
891
send_to_clipboard(Widget_t * w,XEvent * event)892 void send_to_clipboard(Widget_t *w, XEvent* event) {
893 XSelectionRequestEvent * xsr = &event->xselectionrequest;
894 XSelectionEvent xev;
895 memset (&xev, 0, sizeof (XSelectionEvent));
896 int R = 0;
897 xev.type = SelectionNotify;
898 xev.display = xsr->display;
899 xev.requestor = xsr->requestor;
900 xev.selection = xsr->selection;
901 xev.time = xsr->time;
902 xev.target = xsr->target;
903 xev.property = xsr->property;
904 if (xev.target == w->app->targets_atom) {
905 R = XChangeProperty (xev.display, xev.requestor, xev.property, XA_ATOM, 32,
906 PropModeReplace, (unsigned char*)&w->app->UTF8, 1);
907 } else if (xev.target == XA_STRING || xev.target == w->app->text_atom) {
908 R = XChangeProperty(xev.display, xev.requestor, xev.property, XA_STRING, 8,
909 PropModeReplace, w->app->ctext, w->app->csize);
910 } else if (xev.target == w->app->UTF8) {
911 R = XChangeProperty(xev.display, xev.requestor, xev.property, w->app->UTF8, 8,
912 PropModeReplace, w->app->ctext, w->app->csize);
913 } else {
914 xev.property = None;
915 }
916 if ((R & 2) == 0) XSendEvent (w->app->dpy, xev.requestor, 0, 0, (XEvent *)&xev);
917 debug_print("send to clipboard %s\n", w->app->ctext);
918 }
919
send_configure_event(Widget_t * w,int x,int y,int width,int height)920 void send_configure_event(Widget_t *w,int x, int y, int width, int height) {
921 XConfigureEvent notify;
922 memset(¬ify, 0, sizeof(notify));
923 notify.type = ConfigureNotify;
924 notify.display = w->app->dpy;
925 notify.send_event = True;
926 notify.event = w->widget;
927 notify.window = w->widget;
928 notify.x = x;
929 notify.y = y;
930 notify.width = width;
931 notify.height = height;
932 notify.border_width = 0;
933 notify.above = None;
934 notify.override_redirect = 1;
935 XSendEvent( w->app->dpy, w->widget, true, StructureNotifyMask, (XEvent*)¬ify );
936 }
937
send_button_press_event(Widget_t * w)938 void send_button_press_event(Widget_t *w) {
939 XEvent event;
940 memset(&event, 0, sizeof(XEvent));
941 XWindowAttributes attr;
942 XGetWindowAttributes(w->app->dpy, w->widget, &attr);
943 event.type = ButtonPress;
944 event.xbutton.same_screen = true;
945 event.xbutton.root = None;
946 event.xbutton.window = w->widget;
947 event.xbutton.subwindow = None;
948 event.xbutton.x = 1;
949 event.xbutton.y = 1;
950 event.xbutton.x_root = attr.x;
951 event.xbutton.y_root = attr.y;
952 event.xbutton.state = 0;
953 event.xbutton.button = Button1;
954 XSendEvent(w->app->dpy, PointerWindow, True, ButtonPressMask, &event);
955 }
956
send_button_release_event(Widget_t * w)957 void send_button_release_event(Widget_t *w) {
958 XEvent event;
959 memset(&event, 0, sizeof(XEvent));
960 XWindowAttributes attr;
961 XGetWindowAttributes(w->app->dpy, w->widget, &attr);
962 event.type = ButtonRelease;
963 event.xbutton.same_screen = true;
964 event.xbutton.root = None;
965 event.xbutton.window = w->widget;
966 event.xbutton.subwindow = None;
967 event.xbutton.x = 1;
968 event.xbutton.y = 1;
969 event.xbutton.x_root = attr.x;
970 event.xbutton.y_root = attr.y;
971 event.xbutton.state = 0;
972 event.xbutton.button = Button1;
973 XSendEvent(w->app->dpy, PointerWindow, True, ButtonReleaseMask, &event);
974 }
975
send_systray_message(Widget_t * w)976 void send_systray_message(Widget_t *w) {
977 XEvent event;
978 Screen *xscreen;
979 char buf[256];
980 buf[0]=0;
981
982 xscreen=DefaultScreenOfDisplay(w->app->dpy);
983 sprintf(buf,"_NET_SYSTEM_TRAY_S%d",XScreenNumberOfScreen (xscreen));
984 Atom selection_atom = XInternAtom (w->app->dpy,buf,0);
985
986 Window tray = XGetSelectionOwner (w->app->dpy,selection_atom);
987 Atom visualatom = XInternAtom(w->app->dpy, "_NET_SYSTEM_TRAY_VISUAL", False);
988 VisualID value = XVisualIDFromVisual(DefaultVisual(w->app->dpy, DefaultScreen(w->app->dpy)));
989 XChangeProperty(w->app->dpy, w->widget, visualatom, XA_VISUALID, 32,
990 PropModeReplace, (unsigned char*)&value, 1);
991
992 if ( tray != None)
993 XSelectInput (w->app->dpy,tray,StructureNotifyMask);
994
995 memset(&event, 0, sizeof(event));
996 event.xclient.type = ClientMessage;
997 event.xclient.window = tray;
998 event.xclient.message_type = XInternAtom (w->app->dpy, "_NET_SYSTEM_TRAY_OPCODE", False );
999 event.xclient.format = 32;
1000 event.xclient.data.l[0] = CurrentTime;
1001 event.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
1002 event.xclient.data.l[2] = w->widget;
1003 event.xclient.data.l[3] = 0;
1004 event.xclient.data.l[4] = 0;
1005
1006 XSendEvent(w->app->dpy, tray, False, NoEventMask, &event);
1007 }
1008
quit(Widget_t * w)1009 void quit(Widget_t *w) {
1010 Atom WM_DELETE_WINDOW = XInternAtom(w->app->dpy, "WM_DELETE_WINDOW", True);
1011 XClientMessageEvent xevent;
1012 xevent.type = ClientMessage;
1013 xevent.message_type = WM_DELETE_WINDOW;
1014 xevent.display = w->app->dpy;
1015 xevent.window = get_toplevel_widget(w->app)->widget;
1016 xevent.format = 16;
1017 xevent.data.l[0] = WM_DELETE_WINDOW;
1018 XSendEvent(w->app->dpy, w->widget, 0, 0, (XEvent *)&xevent);
1019 }
1020
quit_widget(Widget_t * w)1021 void quit_widget(Widget_t *w) {
1022 Atom QUIT_WIDGET = XInternAtom(w->app->dpy, "WIDGET_DESTROY", False);
1023 XClientMessageEvent xevent;
1024 xevent.type = ClientMessage;
1025 xevent.message_type = QUIT_WIDGET;
1026 xevent.display = w->app->dpy;
1027 xevent.window = w->widget;
1028 xevent.format = 16;
1029 xevent.data.l[0] = 1;
1030 XSendEvent(w->app->dpy, w->widget, 0, 0, (XEvent *)&xevent);
1031 }
1032
1033