Imported Upstream version 1.7.8
[platform/upstream/ecore.git] / src / lib / ecore_wayland / ecore_wl_window.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #ifdef STDC_HEADERS
6 # include <stdlib.h>
7 # include <stddef.h>
8 #else
9 # ifdef HAVE_STDLIB_H
10 #  include <stdlib.h>
11 # endif
12 #endif
13 #ifdef HAVE_ALLOCA_H
14 # include <alloca.h>
15 #elif !defined alloca
16 # ifdef __GNUC__
17 #  define alloca __builtin_alloca
18 # elif defined _AIX
19 #  define alloca __alloca
20 # elif defined _MSC_VER
21 #  include <malloc.h>
22 #  define alloca _alloca
23 # elif !defined HAVE_ALLOCA
24 #  ifdef  __cplusplus
25 extern "C"
26 #  endif
27 void *alloca (size_t);
28 # endif
29 #endif
30
31 #include "ecore_wl_private.h"
32
33 /* local function prototypes */
34 static void _ecore_wl_window_cb_ping(void *data __UNUSED__, struct wl_shell_surface *shell_surface, unsigned int serial);
35 static void _ecore_wl_window_cb_configure(void *data, struct wl_shell_surface *shell_surface __UNUSED__, unsigned int edges, int w, int h);
36 static void _ecore_wl_window_cb_popup_done(void *data, struct wl_shell_surface *shell_surface __UNUSED__);
37 static void _ecore_wl_window_cb_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output __UNUSED__);
38 static void _ecore_wl_window_cb_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output __UNUSED__);
39 static void _ecore_wl_window_configure_send(Ecore_Wl_Window *win, int w, int h);
40 static char *_ecore_wl_window_id_str_get(unsigned int win_id);
41
42 /* local variables */
43 static Eina_Hash *_windows = NULL;
44
45 /* wayland listeners */
46 static const struct wl_surface_listener _ecore_wl_surface_listener = 
47 {
48    _ecore_wl_window_cb_surface_enter,
49    _ecore_wl_window_cb_surface_leave
50 };
51
52 static const struct wl_shell_surface_listener _ecore_wl_shell_surface_listener = 
53 {
54    _ecore_wl_window_cb_ping,
55    _ecore_wl_window_cb_configure,
56    _ecore_wl_window_cb_popup_done
57 };
58
59 /* internal functions */
60 void 
61 _ecore_wl_window_init(void)
62 {
63    if (!_windows) 
64      _windows = eina_hash_string_superfast_new(NULL);
65 }
66
67 void 
68 _ecore_wl_window_shutdown(void)
69 {
70    eina_hash_free(_windows);
71    _windows = NULL;
72 }
73
74 /**
75  * @defgroup Ecore_Wl_Window_Group Wayland Library Init and Shutdown Functions
76  * 
77  * Functions that can be used to create a Wayland window.
78  */
79
80 /**
81  * Creates a new window
82  * 
83  * @param parent The parent window to use. If @p parent is @c 0, the root window 
84  *               of the default display is used.
85  * @param x      X Position
86  * @param y      Y position
87  * @param w      Width
88  * @param h      Height
89  * @param buffer_type The type of the buffer to be used to create a new Ecore_Wl_Window.
90  * 
91  * @return The new window
92  * 
93  * @ingroup Ecore_Wl_Window_Group
94  * @since 1.2
95  */
96 EAPI Ecore_Wl_Window *
97 ecore_wl_window_new(Ecore_Wl_Window *parent, int x, int y, int w, int h, int buffer_type)
98 {
99    Ecore_Wl_Window *win;
100    static int _win_id = 1;
101
102    LOGFN(__FILE__, __LINE__, __FUNCTION__);
103
104    if (!(win = malloc(sizeof(Ecore_Wl_Window))))
105      {
106         ERR("Failed to allocate an Ecore Wayland Window");
107         return NULL;
108      }
109
110    memset(win, 0, sizeof(Ecore_Wl_Window));
111
112    win->display = _ecore_wl_disp;
113    win->parent = parent;
114    win->allocation.x = x;
115    win->allocation.y = y;
116    win->allocation.w = w;
117    win->allocation.h = h;
118    win->saved_allocation = win->allocation;
119    win->transparent = EINA_FALSE;
120    win->type = ECORE_WL_WINDOW_TYPE_TOPLEVEL;
121    win->buffer_type = buffer_type;
122    win->id = _win_id++;
123
124    eina_hash_add(_windows, _ecore_wl_window_id_str_get(win->id), win);
125    return win;
126 }
127
128 /**
129  * Deletes the given window
130  * 
131  * @param win The given window
132  * 
133  * @ingroup Ecore_Wl_Window_Group
134  * @since 1.2
135  */
136 EAPI void 
137 ecore_wl_window_free(Ecore_Wl_Window *win)
138 {
139    Ecore_Wl_Input *input;
140
141    LOGFN(__FILE__, __LINE__, __FUNCTION__);
142
143    if (!win) return;
144
145    eina_hash_del(_windows, _ecore_wl_window_id_str_get(win->id), win);
146
147    wl_list_for_each(input, &_ecore_wl_disp->inputs, link)
148      {
149         if ((input->pointer_focus) && (input->pointer_focus == win))
150           input->pointer_focus = NULL;
151         if ((input->keyboard_focus) && (input->keyboard_focus == win))
152           input->keyboard_focus = NULL;
153      }
154
155    if (win->region.input) wl_region_destroy(win->region.input);
156    win->region.input = NULL;
157    if (win->region.opaque) wl_region_destroy(win->region.opaque);
158    win->region.opaque = NULL;
159    if (win->shell_surface) wl_shell_surface_destroy(win->shell_surface);
160    win->shell_surface = NULL;
161
162    if (win->surface) wl_surface_destroy(win->surface);
163    win->surface = NULL;
164
165    /* HMMM, why was this disabled ? */
166    free(win);
167 }
168
169 /**
170  * Signals for Wayland to initiate a window move.
171  * 
172  * The position requested (@p x, @p y) is not honored by Wayland because 
173  * Wayland does not allow specific window placement to be set.
174  * 
175  * @param win The window to move.
176  * @param x   X Position
177  * @param y   Y Position
178  * 
179  * @ingroup Ecore_Wl_Window_Group
180  * @since 1.2
181  */
182 EAPI void 
183 ecore_wl_window_move(Ecore_Wl_Window *win, int x, int y)
184 {
185    LOGFN(__FILE__, __LINE__, __FUNCTION__);
186
187    if (!win) return;
188
189    win->allocation.x = x;
190    win->allocation.y = y;
191
192    if (win->shell_surface)
193      {
194         Ecore_Wl_Input *input;
195
196         if (!(input = win->keyboard_device))
197           {
198              if (win->parent)
199                {
200                   if (!(input = win->parent->keyboard_device))
201                     input = win->parent->pointer_device;
202                }
203           }
204
205         if ((!input) || (!input->seat)) return;
206
207         wl_shell_surface_move(win->shell_surface, input->seat,
208                               input->display->serial);
209      }
210 }
211
212 /**
213  * Signals for Wayland to initiate a window resize.
214  * 
215  * The size requested (@p w, @p h) is not honored by Wayland because 
216  * Wayland does not allow specific window sizes to be set.
217  * 
218  * @param win      The window to resize.
219  * @param w        Width
220  * @param h        Height
221  * @param location The edge of the window from where the resize should start.
222  * 
223  * @ingroup Ecore_Wl_Window_Group
224  * @since 1.2
225  */
226 EAPI void 
227 ecore_wl_window_resize(Ecore_Wl_Window *win, int w, int h, int location)
228 {
229    LOGFN(__FILE__, __LINE__, __FUNCTION__);
230
231    if (!win) return;
232
233    if (win->type != ECORE_WL_WINDOW_TYPE_FULLSCREEN)
234      {
235         win->allocation.w = w;
236         win->allocation.h = h;
237
238         win->region.input = 
239           wl_compositor_create_region(_ecore_wl_disp->wl.compositor);
240         wl_region_add(win->region.input, win->allocation.x, win->allocation.y, 
241                       win->allocation.w, win->allocation.h);
242      }
243
244    if (!win->transparent)
245      {
246         win->region.opaque = 
247           wl_compositor_create_region(_ecore_wl_disp->wl.compositor);
248         wl_region_add(win->region.opaque, win->allocation.x, win->allocation.y, 
249                       win->allocation.w, win->allocation.h);
250      }
251
252    if (win->shell_surface)
253      {
254         Ecore_Wl_Input *input;
255
256         if (!(input = win->keyboard_device))
257           {
258              if (win->parent)
259                {
260                   if (!(input = win->parent->keyboard_device))
261                     input = win->parent->pointer_device;
262                }
263           }
264
265         if ((!input) || (!input->seat)) return;
266
267         wl_shell_surface_resize(win->shell_surface, input->seat, 
268                                 input->display->serial, location);
269      }
270 }
271
272 EAPI void
273 ecore_wl_window_damage(Ecore_Wl_Window *win, int x, int y, int w, int h)
274 {
275    LOGFN(__FILE__, __LINE__, __FUNCTION__);
276
277    if (!win) return;
278    if (win->surface)
279      wl_surface_damage(win->surface, x, y, w, h);
280 }
281
282 EAPI void
283 ecore_wl_window_commit(Ecore_Wl_Window *win)
284 {
285    LOGFN(__FILE__, __LINE__, __FUNCTION__);
286
287    if (!win) return;
288    if (win->surface)
289      wl_surface_commit(win->surface);
290 }
291
292 EAPI void 
293 ecore_wl_window_buffer_attach(Ecore_Wl_Window *win, struct wl_buffer *buffer, int x, int y)
294 {
295    LOGFN(__FILE__, __LINE__, __FUNCTION__);
296
297    if (!win) return;
298
299    switch (win->buffer_type)
300      {
301       case ECORE_WL_WINDOW_BUFFER_TYPE_EGL_WINDOW:
302         win->server_allocation = win->allocation;
303         break;
304       case ECORE_WL_WINDOW_BUFFER_TYPE_EGL_IMAGE:
305       case ECORE_WL_WINDOW_BUFFER_TYPE_SHM:
306         if (win->surface)
307           {
308              if (win->edges & 4) //  resizing from the left
309                x = win->server_allocation.w - win->allocation.w;
310              else
311                x = 0;
312
313              if (win->edges & 1) // resizing from the top
314                y = win->server_allocation.h - win->allocation.h;
315              else
316                y = 0;
317
318              win->edges = 0;
319
320              /* if (buffer) */
321              wl_surface_attach(win->surface, buffer, x, y);
322              wl_surface_damage(win->surface, 0, 0, 
323                                win->allocation.w, win->allocation.h);
324              wl_surface_commit(win->surface);
325
326              win->server_allocation = win->allocation;
327           }
328         break;
329       default:
330         return;
331      }
332
333    if (win->region.input)
334      {
335         wl_surface_set_input_region(win->surface, win->region.input);
336         wl_region_destroy(win->region.input);
337         win->region.input = NULL;
338      }
339
340    if (win->region.opaque)
341      {
342         wl_surface_set_opaque_region(win->surface, win->region.opaque);
343         wl_region_destroy(win->region.opaque);
344         win->region.opaque = NULL;
345      }
346 }
347
348 /**
349  * Shows a window
350  * 
351  * Synonymous to "mapping" a window in Wayland System terminology.
352  * 
353  * @param win The window to show.
354  * 
355  * @ingroup Ecore_Wl_Window_Group
356  * @since 1.2
357  */
358 EAPI void 
359 ecore_wl_window_show(Ecore_Wl_Window *win)
360 {
361    LOGFN(__FILE__, __LINE__, __FUNCTION__);
362
363    if (!win) return;
364    if (win->surface) return;
365
366    win->surface = wl_compositor_create_surface(_ecore_wl_disp->wl.compositor);
367    wl_surface_set_user_data(win->surface, win);
368    /* wl_surface_add_listener(win->surface, &_ecore_wl_surface_listener, win); */
369
370    if (win->type != ECORE_WL_WINDOW_TYPE_NONE)
371      {
372         win->shell_surface =
373            wl_shell_get_shell_surface(_ecore_wl_disp->wl.shell, win->surface);
374            wl_shell_surface_add_listener(win->shell_surface,
375                                          &_ecore_wl_shell_surface_listener, win);
376      }
377
378    switch (win->type)
379      {
380       case ECORE_WL_WINDOW_TYPE_FULLSCREEN:
381         wl_shell_surface_set_fullscreen(win->shell_surface, 
382                                         WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
383                                         0, NULL);
384         break;
385       case ECORE_WL_WINDOW_TYPE_MAXIMIZED:
386         wl_shell_surface_set_maximized(win->shell_surface, NULL);
387         break;
388       case ECORE_WL_WINDOW_TYPE_TRANSIENT:
389         wl_shell_surface_set_transient(win->shell_surface, 
390                                        win->parent->surface, 
391                                        win->allocation.x, win->allocation.y, 0);
392         break;
393       case ECORE_WL_WINDOW_TYPE_MENU:
394         wl_shell_surface_set_popup(win->shell_surface, 
395                                    _ecore_wl_disp->input->seat,
396                                    _ecore_wl_disp->serial,
397                                    win->parent->surface, 
398                                    win->allocation.x, win->allocation.y, 0);
399         break;
400       case ECORE_WL_WINDOW_TYPE_TOPLEVEL:
401         wl_shell_surface_set_toplevel(win->shell_surface);
402         break;
403       default:
404         break;
405      }
406
407    /* if (win->type != ECORE_WL_WINDOW_TYPE_FULLSCREEN) */
408    /*   { */
409    /*      win->region.input =  */
410    /*        wl_compositor_create_region(_ecore_wl_disp->wl.compositor); */
411    /*      wl_region_add(win->region.input, win->allocation.x, win->allocation.y,  */
412    /*                    win->allocation.w, win->allocation.h); */
413    /*   } */
414
415    /* if (!win->transparent) */
416    /*   { */
417    /*      win->region.opaque =  */
418    /*        wl_compositor_create_region(_ecore_wl_disp->wl.compositor); */
419    /*      wl_region_add(win->region.opaque, win->allocation.x, win->allocation.y,  */
420    /*                    win->allocation.w, win->allocation.h); */
421    /*   } */
422 }
423
424 /**
425  * Hides a window
426  * 
427  * Synonymous to "unmapping" a window in Wayland System terminology.
428  * 
429  * @param win The window to hide.
430  * 
431  * @ingroup Ecore_Wl_Window_Group
432  * @since 1.2
433  */
434 EAPI void 
435 ecore_wl_window_hide(Ecore_Wl_Window *win)
436 {
437    LOGFN(__FILE__, __LINE__, __FUNCTION__);
438
439    if (!win) return;
440    if (win->shell_surface) wl_shell_surface_destroy(win->shell_surface);
441    win->shell_surface = NULL;
442    if (win->surface) wl_surface_destroy(win->surface);
443    win->surface = NULL;
444 }
445
446 /**
447  * Raises a window
448  * 
449  * @param win The window to raise.
450  * 
451  * @ingroup Ecore_Wl_Window_Group
452  * @since 1.2
453  */
454 EAPI void 
455 ecore_wl_window_raise(Ecore_Wl_Window *win)
456 {
457    LOGFN(__FILE__, __LINE__, __FUNCTION__);
458
459    if (!win) return;
460    if (win->shell_surface) 
461      wl_shell_surface_set_toplevel(win->shell_surface);
462 }
463
464 EAPI void 
465 ecore_wl_window_maximized_set(Ecore_Wl_Window *win, Eina_Bool maximized)
466 {
467    LOGFN(__FILE__, __LINE__, __FUNCTION__);
468
469    if (!win) return;
470
471    if ((win->type == ECORE_WL_WINDOW_TYPE_MAXIMIZED) == maximized) return;
472    if (win->type == ECORE_WL_WINDOW_TYPE_TOPLEVEL)
473      {
474         win->saved_allocation = win->allocation;
475         if (win->shell_surface) 
476           wl_shell_surface_set_maximized(win->shell_surface, NULL);
477         win->type = ECORE_WL_WINDOW_TYPE_MAXIMIZED;
478      }
479    else if (win->type == ECORE_WL_WINDOW_TYPE_MAXIMIZED)
480      {
481         if (win->shell_surface) 
482           wl_shell_surface_set_toplevel(win->shell_surface);
483         win->type = ECORE_WL_WINDOW_TYPE_TOPLEVEL;
484         _ecore_wl_window_configure_send(win, win->saved_allocation.w, 
485                                         win->saved_allocation.h);
486      }
487    win->edges = 0;
488 }
489
490 EAPI void 
491 ecore_wl_window_fullscreen_set(Ecore_Wl_Window *win, Eina_Bool fullscreen)
492 {
493    LOGFN(__FILE__, __LINE__, __FUNCTION__);
494
495    if (!win) return;
496    if ((win->type == ECORE_WL_WINDOW_TYPE_FULLSCREEN) == fullscreen) return;
497    if (fullscreen)
498      {
499         win->type = ECORE_WL_WINDOW_TYPE_FULLSCREEN;
500         win->saved_allocation = win->allocation;
501         if (win->shell_surface)
502           wl_shell_surface_set_fullscreen(win->shell_surface, 
503                                           WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
504                                           0, NULL);
505      }
506    else 
507      {
508         if (win->shell_surface)
509           wl_shell_surface_set_toplevel(win->shell_surface);
510         win->type = ECORE_WL_WINDOW_TYPE_TOPLEVEL;
511         _ecore_wl_window_configure_send(win, win->saved_allocation.w, 
512                                         win->saved_allocation.h);
513      }
514    win->edges = 0;
515 }
516
517 EAPI void 
518 ecore_wl_window_transparent_set(Ecore_Wl_Window *win, Eina_Bool transparent)
519 {
520    LOGFN(__FILE__, __LINE__, __FUNCTION__);
521
522    if (!win) return;
523    win->transparent = transparent;
524    if (win->region.opaque) wl_region_destroy(win->region.opaque);
525    win->region.opaque = NULL;
526    if (!win->transparent)
527      {
528         win->region.opaque = 
529           wl_compositor_create_region(_ecore_wl_disp->wl.compositor);
530         wl_region_add(win->region.opaque, win->allocation.x, win->allocation.y, 
531                       win->allocation.w, win->allocation.h);
532      }
533 }
534
535 EAPI void 
536 ecore_wl_window_update_size(Ecore_Wl_Window *win, int w, int h)
537 {
538    LOGFN(__FILE__, __LINE__, __FUNCTION__);
539
540    if (!win) return;
541    win->allocation.w = w;
542    win->allocation.h = h;
543 }
544
545 EAPI void 
546 ecore_wl_window_update_location(Ecore_Wl_Window *win, int x, int y)
547 {
548    LOGFN(__FILE__, __LINE__, __FUNCTION__);
549
550    if (!win) return;
551    win->allocation.x = x;
552    win->allocation.y = y;
553 }
554
555 EAPI struct wl_surface *
556 ecore_wl_window_surface_get(Ecore_Wl_Window *win)
557 {
558    LOGFN(__FILE__, __LINE__, __FUNCTION__);
559
560    if (!win) return NULL;
561    return win->surface;
562 }
563
564 /* @since 1.2 */
565 EAPI struct wl_shell_surface *
566 ecore_wl_window_shell_surface_get(Ecore_Wl_Window *win)
567 {
568    LOGFN(__FILE__, __LINE__, __FUNCTION__);
569
570    if (!win) return NULL;
571    return win->shell_surface;
572 }
573
574 EAPI Ecore_Wl_Window *
575 ecore_wl_window_find(unsigned int id)
576 {
577    Ecore_Wl_Window *win = NULL;
578
579    win = eina_hash_find(_windows, _ecore_wl_window_id_str_get(id));
580    return win;
581 }
582
583 EAPI void 
584 ecore_wl_window_type_set(Ecore_Wl_Window *win, Ecore_Wl_Window_Type type)
585 {
586    LOGFN(__FILE__, __LINE__, __FUNCTION__);
587
588    if (!win) return;
589    win->type = type;
590 }
591
592 EAPI void 
593 ecore_wl_window_pointer_set(Ecore_Wl_Window *win, struct wl_surface *surface, int hot_x, int hot_y)
594 {
595    Ecore_Wl_Input *input;
596
597    LOGFN(__FILE__, __LINE__, __FUNCTION__);
598
599    if (!win) return;
600
601    if ((input = win->pointer_device))
602      ecore_wl_input_pointer_set(input, surface, hot_x, hot_y);
603 }
604
605 EAPI void
606 ecore_wl_window_cursor_from_name_set(Ecore_Wl_Window *win, const char *cursor_name)
607 {
608    Ecore_Wl_Input *input;
609
610    LOGFN(__FILE__, __LINE__, __FUNCTION__);
611
612    if (!win) return;
613
614    if ((input = win->pointer_device))
615      ecore_wl_input_cursor_from_name_set(input, cursor_name);
616 }
617
618 EAPI void
619 ecore_wl_window_cursor_default_restore(Ecore_Wl_Window *win)
620 {
621    Ecore_Wl_Input *input;
622
623    LOGFN(__FILE__, __LINE__, __FUNCTION__);
624
625    if (!win) return;
626
627    if ((input = win->pointer_device))
628      ecore_wl_input_cursor_default_restore(input);
629 }
630
631 /* @since 1.2 */
632 EAPI void 
633 ecore_wl_window_parent_set(Ecore_Wl_Window *win, Ecore_Wl_Window *parent)
634 {
635    LOGFN(__FILE__, __LINE__, __FUNCTION__);
636
637    win->parent = parent;
638 }
639
640 /* local functions */
641 static void 
642 _ecore_wl_window_cb_ping(void *data __UNUSED__, struct wl_shell_surface *shell_surface, unsigned int serial)
643 {
644    if (!shell_surface) return;
645    wl_shell_surface_pong(shell_surface, serial);
646 }
647
648 static void 
649 _ecore_wl_window_cb_configure(void *data, struct wl_shell_surface *shell_surface __UNUSED__, unsigned int edges, int w, int h)
650 {
651    Ecore_Wl_Window *win;
652
653    LOGFN(__FILE__, __LINE__, __FUNCTION__);
654
655    if (!(win = data)) return;
656
657    if ((w <= 0) || (h <= 0)) return;
658
659    if ((win->allocation.w != w) || (win->allocation.h != h))
660      {
661         if (win->type == ECORE_WL_WINDOW_TYPE_TOPLEVEL)
662           win->edges = edges;
663         if (win->region.input) wl_region_destroy(win->region.input);
664         win->region.input = NULL;
665         if (win->region.opaque) wl_region_destroy(win->region.opaque);
666         win->region.opaque = NULL;
667
668         _ecore_wl_window_configure_send(win, w, h);
669      }
670 }
671
672 static void 
673 _ecore_wl_window_cb_popup_done(void *data, struct wl_shell_surface *shell_surface __UNUSED__)
674 {
675    Ecore_Wl_Window *win;
676
677    LOGFN(__FILE__, __LINE__, __FUNCTION__);
678
679    if (!shell_surface) return;
680    if (!(win = data)) return;
681    ecore_wl_input_ungrab(win->pointer_device);
682 }
683
684 static void 
685 _ecore_wl_window_cb_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output __UNUSED__)
686 {
687    Ecore_Wl_Window *win;
688
689    LOGFN(__FILE__, __LINE__, __FUNCTION__);
690
691    if (!surface) return;
692    if (!(win = data)) return;
693 }
694
695 static void 
696 _ecore_wl_window_cb_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output __UNUSED__)
697 {
698    Ecore_Wl_Window *win;
699
700    LOGFN(__FILE__, __LINE__, __FUNCTION__);
701
702    if (!surface) return;
703    if (!(win = data)) return;
704 }
705
706 static void 
707 _ecore_wl_window_configure_send(Ecore_Wl_Window *win, int w, int h)
708 {
709    Ecore_Wl_Event_Window_Configure *ev;
710
711    LOGFN(__FILE__, __LINE__, __FUNCTION__);
712
713    if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Window_Configure)))) return;
714    ev->win = win->id;
715    ev->event_win = win->id;
716    ev->x = win->allocation.x;
717    ev->y = win->allocation.y;
718    ev->w = w;
719    ev->h = h;
720    ecore_event_add(ECORE_WL_EVENT_WINDOW_CONFIGURE, ev, NULL, NULL);
721 }
722
723 static char *
724 _ecore_wl_window_id_str_get(unsigned int win_id)
725 {
726    const char *vals = "qWeRtYuIoP5$&<~";
727    static char id[9];
728    unsigned int val;
729
730    val = win_id;
731    id[0] = vals[(val >> 28) & 0xf];
732    id[1] = vals[(val >> 24) & 0xf];
733    id[2] = vals[(val >> 20) & 0xf];
734    id[3] = vals[(val >> 16) & 0xf];
735    id[4] = vals[(val >> 12) & 0xf];
736    id[5] = vals[(val >> 8) & 0xf];
737    id[6] = vals[(val >> 4) & 0xf];
738    id[7] = vals[(val) & 0xf];
739    id[8] = 0;
740
741    return id;
742 }