]> git.decadent.org.uk Git - ion3.git/blob - ioncore/clientwin.c
Imported Upstream version 20090110
[ion3.git] / ioncore / clientwin.c
1 /*
2  * ion/ioncore/clientwin.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10 #include <limits.h>
11 #include <ctype.h>
12
13 #include <libtu/objp.h>
14 #include <libtu/minmax.h>
15 #include <libextl/extl.h>
16 #include <libmainloop/defer.h>
17 #include <libmainloop/hooks.h>
18 #include "common.h"
19 #include "global.h"
20 #include "property.h"
21 #include "focus.h"
22 #include "sizehint.h"
23 #include "event.h"
24 #include "clientwin.h"
25 #include "colormap.h"
26 #include "resize.h"
27 #include "attach.h"
28 #include "regbind.h"
29 #include "bindmaps.h"
30 #include "names.h"
31 #include "saveload.h"
32 #include "manage.h"
33 #include "extlconv.h"
34 #include "fullscreen.h"
35 #include "event.h"
36 #include "rootwin.h"
37 #include "activity.h"
38 #include "netwm.h"
39 #include "xwindow.h"
40 #include "bindmaps.h"
41 #include "return.h"
42 #include "conf.h"
43 #include "group.h"
44
45
46 static void set_clientwin_state(WClientWin *cwin, int state);
47 static bool send_clientmsg(Window win, Atom a, Time stmp);
48
49
50 WHook *clientwin_do_manage_alt=NULL;
51 WHook *clientwin_mapped_hook=NULL;
52 WHook *clientwin_unmapped_hook=NULL;
53 WHook *clientwin_property_change_hook=NULL;
54
55
56 /*{{{ Get properties */
57
58
59 void clientwin_get_protocols(WClientWin *cwin)
60 {
61     Atom *protocols=NULL, *p;
62     int n;
63     
64     cwin->flags&=~(CLIENTWIN_P_WM_DELETE|CLIENTWIN_P_WM_TAKE_FOCUS);
65     
66     if(!XGetWMProtocols(ioncore_g.dpy, cwin->win, &protocols, &n))
67         return;
68     
69     for(p=protocols; n; n--, p++){
70         if(*p==ioncore_g.atom_wm_delete)
71             cwin->flags|=CLIENTWIN_P_WM_DELETE;
72         else if(*p==ioncore_g.atom_wm_take_focus)
73             cwin->flags|=CLIENTWIN_P_WM_TAKE_FOCUS;
74     }
75     
76     if(protocols!=NULL)
77         XFree((char*)protocols);
78 }
79
80
81 static WSizePolicy get_sizepolicy_winprop(WClientWin *cwin,
82                                           const char *propname,
83                                           WSizePolicy value)
84 {
85     char *szplcy;
86
87     if(extl_table_gets_s(cwin->proptab, propname, &szplcy)){
88         string2sizepolicy(szplcy, &value);
89         free(szplcy);
90     }
91     return value;
92 }
93
94
95 #define SIZEHINT_PROPS (CLIENTWIN_PROP_MAXSIZE|   \
96                         CLIENTWIN_PROP_MINSIZE|   \
97                         CLIENTWIN_PROP_ASPECT|    \
98                         CLIENTWIN_PROP_RSZINC|    \
99                         CLIENTWIN_PROP_I_MAXSIZE| \
100                         CLIENTWIN_PROP_I_MINSIZE| \
101                         CLIENTWIN_PROP_I_ASPECT|  \
102                         CLIENTWIN_PROP_I_RSZINC)
103
104
105 #define DO_SZH(NAME, FLAG, IFLAG, SZHFLAG, W, H, C)  \
106     if(extl_table_is_bool_set(tab, "ignore_" NAME)){ \
107         cwin->flags|=IFLAG;                          \
108     }else if(extl_table_gets_t(tab, NAME, &tab2)){   \
109         if(extl_table_gets_i(tab2, "w", &i1) &&      \
110            extl_table_gets_i(tab2, "h", &i2)){       \
111             cwin->size_hints.W=i1;                   \
112             cwin->size_hints.H=i2;                   \
113             C                                        \
114             cwin->size_hints.flags|=SZHFLAG;         \
115             cwin->flags|=FLAG;                       \
116         }                                            \
117         extl_unref_table(tab2);                      \
118     }
119
120
121 static void clientwin_get_winprops(WClientWin *cwin)
122 {
123     ExtlTab tab, tab2;
124     char *s;
125     int i1, i2;
126     
127     tab=ioncore_get_winprop(cwin);
128     
129     cwin->proptab=tab;
130     
131     if(tab==extl_table_none())
132         return;
133
134     if(extl_table_is_bool_set(tab, "transparent"))
135         cwin->flags|=CLIENTWIN_PROP_TRANSPARENT;
136
137     if(extl_table_is_bool_set(tab, "acrobatic"))
138         cwin->flags|=CLIENTWIN_PROP_ACROBATIC;
139     
140     DO_SZH("max_size", CLIENTWIN_PROP_MAXSIZE, CLIENTWIN_PROP_I_MAXSIZE,
141            PMaxSize, max_width, max_height, );
142            
143     DO_SZH("min_size", CLIENTWIN_PROP_MINSIZE, CLIENTWIN_PROP_I_MINSIZE,
144            PMinSize, min_width, min_height, );
145            
146     DO_SZH("resizeinc", CLIENTWIN_PROP_RSZINC, CLIENTWIN_PROP_I_RSZINC,
147            PResizeInc, width_inc, height_inc, );
148
149     DO_SZH("aspect", CLIENTWIN_PROP_ASPECT, CLIENTWIN_PROP_I_ASPECT,
150            PAspect, min_aspect.x, min_aspect.y, 
151            { cwin->size_hints.max_aspect.x=i1;
152              cwin->size_hints.max_aspect.y=i2;
153            });
154            
155     if(extl_table_is_bool_set(tab, "ignore_cfgrq"))
156         cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ;
157
158     if(extl_table_gets_s(tab, "orientation", &s)){
159         if(strcmp(s, "vertical")==0)
160             cwin->flags|=CLIENTWIN_PROP_O_VERT;
161         else if(strcmp(s, "horizontal")==0)
162             cwin->flags|=CLIENTWIN_PROP_O_HORIZ;
163         free(s);
164     }
165 }
166
167
168 void clientwin_get_size_hints(WClientWin *cwin)
169 {
170     XSizeHints tmp=cwin->size_hints;
171     
172     xwindow_get_sizehints(cwin->win, &(cwin->size_hints));
173     
174     if(cwin->flags&CLIENTWIN_PROP_I_MAXSIZE){
175         cwin->size_hints.flags&=~PMaxSize;
176     }else if(cwin->flags&CLIENTWIN_PROP_MAXSIZE){
177         cwin->size_hints.max_width=tmp.max_width;
178         cwin->size_hints.max_height=tmp.max_height;
179         cwin->size_hints.flags|=PMaxSize;
180     }
181     
182     if(cwin->flags&CLIENTWIN_PROP_I_MINSIZE){
183         cwin->size_hints.flags&=~PMinSize;
184     }else if(cwin->flags&CLIENTWIN_PROP_MINSIZE){
185         cwin->size_hints.min_width=tmp.min_width;
186         cwin->size_hints.min_height=tmp.min_height;
187         cwin->size_hints.flags|=PMinSize;
188     }
189     
190     if(cwin->flags&CLIENTWIN_PROP_I_ASPECT){
191         cwin->size_hints.flags&=~PAspect;
192     }else if(cwin->flags&CLIENTWIN_PROP_ASPECT){
193         cwin->size_hints.min_aspect=tmp.min_aspect;
194         cwin->size_hints.max_aspect=tmp.max_aspect;
195         cwin->size_hints.flags|=PAspect;
196     }
197     
198     if(cwin->flags&CLIENTWIN_PROP_I_RSZINC){
199         cwin->size_hints.flags&=~PResizeInc;
200     }else if(cwin->flags&CLIENTWIN_PROP_RSZINC){
201         cwin->size_hints.width_inc=tmp.width_inc;
202         cwin->size_hints.height_inc=tmp.height_inc;
203         cwin->size_hints.flags|=PResizeInc;
204     }
205 }
206
207
208 void clientwin_get_set_name(WClientWin *cwin)
209 {
210     char **list=NULL;
211     int n=0;
212     
213     if(ioncore_g.use_mb)
214         list=netwm_get_name(cwin);
215
216     if(list==NULL){
217         list=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
218     }else{
219         cwin->flags|=CLIENTWIN_USE_NET_WM_NAME;
220     }
221
222     if(list==NULL){
223         /* Special condition kludge: property exists, but couldn't
224          * be converted to a string list.
225          */
226         clientwin_set_name(cwin, (n==-1 ? "???" : NULL));
227     }else{
228         clientwin_set_name(cwin, *list);
229         XFreeStringList(list);
230     }
231 }
232
233
234 /* Some standard winprops */
235
236
237 bool clientwin_get_switchto(const WClientWin *cwin)
238 {
239     bool b;
240     
241     if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
242         return FALSE;
243     
244     if(extl_table_gets_b(cwin->proptab, "switchto", &b))
245         return b;
246     
247     return ioncore_g.switchto_new;
248 }
249
250
251 int clientwin_get_transient_mode(const WClientWin *cwin)
252 {
253     char *s;
254     int mode=TRANSIENT_MODE_NORMAL;
255     
256     if(extl_table_gets_s(cwin->proptab, "transient_mode", &s)){
257         if(strcmp(s, "current")==0)
258             mode=TRANSIENT_MODE_CURRENT;
259         else if(strcmp(s, "off")==0)
260             mode=TRANSIENT_MODE_OFF;
261         free(s);
262     }
263     return mode;
264 }
265
266
267 /*}}}*/
268
269
270 /*{{{ Manage/create */
271
272
273 static void configure_cwin_bw(Window win, int bw)
274 {
275     XWindowChanges wc;
276     ulong wcmask=CWBorderWidth;
277     
278     wc.border_width=bw;
279     XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc);
280 }
281
282
283 static void set_sane_gravity(Window win)
284 {
285     XSetWindowAttributes attr;
286     
287     attr.win_gravity=NorthWestGravity;
288     
289     XChangeWindowAttributes(ioncore_g.dpy, win,
290                             CWWinGravity, &attr);
291 }
292
293
294 static bool clientwin_init(WClientWin *cwin, WWindow *par, Window win,
295                            XWindowAttributes *attr)
296 {
297     WFitParams fp;
298
299     cwin->flags=0;
300     cwin->win=win;
301     cwin->state=WithdrawnState;
302     
303     fp.g.x=attr->x;
304     fp.g.y=attr->y;
305     fp.g.w=attr->width;
306     fp.g.h=attr->height;
307     fp.mode=REGION_FIT_EXACT;
308     
309     /* The idiot who invented special server-supported window borders that
310      * are not accounted for in the window size should be "taken behind a
311      * sauna".
312      */
313     cwin->orig_bw=attr->border_width;
314     configure_cwin_bw(cwin->win, 0);
315     if(cwin->orig_bw!=0 && cwin->size_hints.flags&PWinGravity){
316         fp.g.x+=xgravity_deltax(cwin->size_hints.win_gravity, 
317                                -cwin->orig_bw, -cwin->orig_bw);
318         fp.g.y+=xgravity_deltay(cwin->size_hints.win_gravity, 
319                                -cwin->orig_bw, -cwin->orig_bw);
320     }
321     
322     set_sane_gravity(cwin->win);
323
324     cwin->n_cmapwins=0;
325     cwin->cmap=attr->colormap;
326     cwin->cmaps=NULL;
327     cwin->cmapwins=NULL;
328     cwin->n_cmapwins=0;
329     cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN;
330
331     region_init(&(cwin->region), par, &fp);
332
333     cwin->region.flags|=REGION_GRAB_ON_PARENT;
334     region_add_bindmap(&cwin->region, ioncore_clientwin_bindmap);
335         
336     XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
337
338     clientwin_register(cwin);
339     clientwin_get_set_name(cwin);
340     clientwin_get_colormaps(cwin);
341     clientwin_get_protocols(cwin);
342     clientwin_get_winprops(cwin);
343     clientwin_get_size_hints(cwin);
344     
345     XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)cwin);
346     XAddToSaveSet(ioncore_g.dpy, win);
347
348     return TRUE;
349 }
350
351
352 static WClientWin *create_clientwin(WWindow *par, Window win,
353                                     XWindowAttributes *attr)
354 {
355     CREATEOBJ_IMPL(WClientWin, clientwin, (p, par, win, attr));
356 }
357
358
359
360 WClientWin *clientwin_get_transient_for(const WClientWin *cwin)
361 {
362     Window tforwin;
363     WClientWin *tfor=NULL;
364     
365     if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_NORMAL)
366         return NULL;
367
368     if(!XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin))
369         return NULL;
370     
371     if(tforwin==None)
372         return NULL;
373     
374     tfor=XWINDOW_REGION_OF_T(tforwin, WClientWin);
375     
376     if(tfor==cwin){
377         warn(TR("The transient_for hint for \"%s\" points to itself."),
378              region_name((WRegion*)cwin));
379     }else if(tfor==NULL){
380         if(xwindow_region_of(tforwin)!=NULL){
381             warn(TR("Client window \"%s\" has broken transient_for hint. "
382                     "(\"Extended WM hints\" multi-parent brain damage?)"),
383                  region_name((WRegion*)cwin));
384         }
385     }else if(!region_same_rootwin((WRegion*)cwin, (WRegion*)tfor)){
386         warn(TR("The transient_for window for \"%s\" is not on the same "
387                 "screen."), region_name((WRegion*)cwin));
388     }else{
389         return tfor;
390     }
391     
392     return NULL;
393 }
394
395
396 static bool postmanage_check(WClientWin *cwin, XWindowAttributes *attr)
397 {
398     /* Check that the window exists. The previous check and selectinput
399      * do not seem to catch all cases of window destroyal.
400      */
401     XSync(ioncore_g.dpy, False);
402     
403     if(XGetWindowAttributes(ioncore_g.dpy, cwin->win, attr))
404         return TRUE;
405     
406     warn(TR("Window %#x disappeared."), cwin->win);
407     
408     return FALSE;
409 }
410
411
412 static bool do_manage_mrsh(bool (*fn)(WClientWin *cwin, WManageParams *pm),
413                            void **p)
414 {
415     return fn((WClientWin*)p[0], (WManageParams*)p[1]);
416 }
417
418
419
420 static bool do_manage_mrsh_extl(ExtlFn fn, void **p)
421 {
422     WClientWin *cwin=(WClientWin*)p[0];
423     WManageParams *mp=(WManageParams*)p[1];
424     ExtlTab t=manageparams_to_table(mp);
425     bool ret=FALSE;
426     
427     extl_call(fn, "ot", "b", cwin, t, &ret);
428     
429     extl_unref_table(t);
430     
431     return (ret && REGION_MANAGER(cwin)!=NULL);
432 }
433
434
435 /* This is called when a window is mapped on the root window.
436  * We want to check if we should manage the window and how and
437  * act appropriately.
438  */
439 WClientWin* ioncore_manage_clientwin(Window win, bool maprq)
440 {
441     WRootWin *rootwin;
442     WClientWin *cwin=NULL;
443     XWindowAttributes attr;
444     XWMHints *hints;
445     int init_state=NormalState;
446     WManageParams param=MANAGEPARAMS_INIT;
447     void *mrshpm[2];
448
449     param.dockapp=FALSE;
450     
451     /* Is the window already being managed? */
452     cwin=XWINDOW_REGION_OF_T(win, WClientWin);
453     if(cwin!=NULL)
454         return cwin;
455     
456     /* Select for UnmapNotify and DestroyNotify as the
457      * window might get destroyed or unmapped in the meanwhile.
458      */
459     xwindow_unmanaged_selectinput(win, StructureNotifyMask);
460
461     if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
462         if(maprq)
463             warn(TR("Window %#x disappeared."), win);
464         goto fail2;
465     }
466     
467     /* Is it a dockapp?
468      */
469     hints=XGetWMHints(ioncore_g.dpy, win);
470     
471     if(hints!=NULL){
472         if(hints->flags&StateHint)
473             init_state=hints->initial_state;
474     
475         if(!param.dockapp && init_state==WithdrawnState && 
476            hints->flags&IconWindowHint && hints->icon_window!=None){
477             Window icon_win=hints->icon_window;
478             XWindowAttributes icon_attr;
479             
480             if(!XGetWindowAttributes(ioncore_g.dpy, icon_win, &icon_attr)){
481                 if(maprq)
482                     warn(TR("Window %#x disappeared."), win);
483                 XFree((void*)hints);
484                 goto fail2;
485             }
486              
487             if(!maprq){
488                 if(attr.map_state==IsViewable){
489                     /* The dockapp might be displaying its "main" window if no
490                      * wm that understands dockapps has been managing it.
491                      */
492                     XUnmapWindow(ioncore_g.dpy, win);
493                     param.dockapp=TRUE;
494                 }else{
495                     /* Main window is unmapped on initial scan, but icon window
496                      * is mapped. Let's hope it's a dockapp left by e.g. us.
497                      */
498                     if(icon_attr.map_state==IsViewable)
499                         param.dockapp=TRUE;
500                 }
501             }else{
502                 param.dockapp=TRUE;
503             }
504
505             if(param.dockapp){
506                 char **p=NULL;
507                 int n=0;
508                 
509                 xwindow_unmanaged_selectinput(win, 0);
510                 xwindow_unmanaged_selectinput(icon_win, StructureNotifyMask);
511                 
512                 /* Copy WM_CLASS as _ION_DOCKAPP_HACK */
513
514                 p=xwindow_get_text_property(win, XA_WM_CLASS, &n);
515                 
516                 if(p!=NULL){
517                     xwindow_set_text_property(icon_win, ioncore_g.atom_dockapp_hack,
518                                               (const char **)p, n);
519                     XFreeStringList(p);
520                 }else{
521                     const char *pdummy[2]={"unknowndockapp", "UnknownDockapp"};
522                     xwindow_set_text_property(icon_win, ioncore_g.atom_dockapp_hack,
523                                               pdummy, 2);
524                 }
525                 
526                 win=icon_win;
527                 attr=icon_attr;
528             }
529         }
530         
531         XFree((void*)hints);
532     }
533     
534     /* Do we really want to manage it? */
535     if(!param.dockapp && (attr.override_redirect || 
536         (!maprq && attr.map_state!=IsViewable))){
537         goto fail2;
538     }
539
540     attr.width=maxof(attr.width, 1);
541     attr.height=maxof(attr.height, 1);
542
543     /* Find root window */
544     FOR_ALL_ROOTWINS(rootwin){
545         if(WROOTWIN_ROOT(rootwin)==attr.root)
546             break;
547     }
548
549     if(rootwin==NULL){
550         warn(TR("Unable to find a matching root window!"));
551         goto fail2;
552     }
553
554     /* Allocate and initialize */
555     cwin=create_clientwin((WWindow*)rootwin, win, &attr);
556     
557     if(cwin==NULL){
558         warn_err();
559         goto fail2;
560     }
561
562     param.geom=REGION_GEOM(cwin);
563     param.maprq=maprq;
564     param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
565     param.switchto=(init_state!=IconicState && 
566                     (param.jumpto || clientwin_get_switchto(cwin)));
567     param.gravity=(cwin->size_hints.flags&PWinGravity
568                    ? cwin->size_hints.win_gravity
569                    : ForgetGravity);
570     param.tfor=clientwin_get_transient_for(cwin);
571     
572     if(!extl_table_gets_b(cwin->proptab, "userpos", &param.userpos))
573         param.userpos=(cwin->size_hints.flags&USPosition);
574     
575     if(cwin->flags&SIZEHINT_PROPS){
576         /* If size hints have been messed with, readjust requested geometry
577          * here. If programs themselves give incompatible geometries and
578          * things don't look good then, it's their fault.
579          */
580         region_size_hints_correct((WRegion*)cwin, &param.geom.w, &param.geom.h,
581                                   FALSE);
582     }
583
584     mrshpm[0]=cwin;
585     mrshpm[1]=&param;
586         
587     if(!hook_call_alt(clientwin_do_manage_alt, &mrshpm, 
588                       (WHookMarshall*)do_manage_mrsh,
589                       (WHookMarshallExtl*)do_manage_mrsh_extl)){
590         warn(TR("Unable to manage client window %#x."), win);
591         goto failure;
592     }
593     
594     if(ioncore_g.opmode==IONCORE_OPMODE_NORMAL &&
595        !region_is_fully_mapped((WRegion*)cwin) && 
596        !region_skip_focus((WRegion*)cwin)){
597         region_set_activity((WRegion*)cwin, SETPARAM_SET);
598     }
599     
600     if(postmanage_check(cwin, &attr)){
601         /* Check for focus_next==NULL does not play nicely with
602          * pointer_focus_hack.
603          */
604         /*if(param.jumpto && ioncore_g.focus_next==NULL)*/
605         if(param.jumpto && !region_manager_is_focusnext((WRegion*)cwin))
606             region_goto((WRegion*)cwin);
607         hook_call_o(clientwin_mapped_hook, (Obj*)cwin);
608         return cwin;
609     }
610
611 failure:
612     clientwin_destroyed(cwin);
613     return NULL;
614
615 fail2:
616     xwindow_unmanaged_selectinput(win, 0);
617     return NULL;
618 }
619
620
621 void clientwin_tfor_changed(WClientWin *cwin)
622 {
623 #if 0
624     WManageParams param=MANAGEPARAMS_INIT;
625     bool succeeded=FALSE;
626     param.tfor=clientwin_get_transient_for(cwin);
627     if(param.tfor==NULL)
628         return;
629     
630     region_rootpos((WRegion*)cwin, &(param.geom.x), &(param.geom.y));
631     param.geom.w=REGION_GEOM(cwin).w;
632     param.geom.h=REGION_GEOM(cwin).h;
633     param.maprq=FALSE;
634     param.userpos=FALSE;
635     param.switchto=region_may_control_focus((WRegion*)cwin);
636     param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
637     param.gravity=ForgetGravity;
638     
639     CALL_ALT_B(succeeded, clientwin_do_manage_alt, (cwin, &param));
640     warn("WM_TRANSIENT_FOR changed for \"%s\".",
641          region_name((WRegion*)cwin));
642 #else
643     warn(TR("Changes is WM_TRANSIENT_FOR property are unsupported."));
644 #endif        
645 }
646
647
648 /*}}}*/
649
650
651 /*{{{ Unmanage/destroy */
652
653
654 static bool reparent_root(WClientWin *cwin)
655 {
656     XWindowAttributes attr;
657     WWindow *par;
658     Window dummy;
659     int x=0, y=0;
660     
661     if(!XGetWindowAttributes(ioncore_g.dpy, cwin->win, &attr))
662         return FALSE;
663     
664     par=REGION_PARENT(cwin);
665     
666     if(par==NULL){
667         x=REGION_GEOM(cwin).x;
668         y=REGION_GEOM(cwin).y;
669     }else{
670         int dr=REGION_GEOM(par).w-REGION_GEOM(cwin).w-REGION_GEOM(cwin).x;
671         int db=REGION_GEOM(par).h-REGION_GEOM(cwin).h-REGION_GEOM(cwin).y;
672         dr=maxof(dr, 0);
673         db=maxof(db, 0);
674         
675         XTranslateCoordinates(ioncore_g.dpy, par->win, attr.root, 0, 0, 
676                               &x, &y, &dummy);
677
678         x-=xgravity_deltax(cwin->size_hints.win_gravity, 
679                            maxof(0, REGION_GEOM(cwin).x), dr);
680         y-=xgravity_deltay(cwin->size_hints.win_gravity, 
681                            maxof(0, REGION_GEOM(cwin).y), db);
682     }
683     
684     XReparentWindow(ioncore_g.dpy, cwin->win, attr.root, x, y);
685     
686     return TRUE;
687 }
688
689
690 void clientwin_deinit(WClientWin *cwin)
691 {
692     WRegion *reg;
693     
694     if(cwin->win!=None){
695         region_pointer_focus_hack(&cwin->region);
696
697         xwindow_unmanaged_selectinput(cwin->win, 0);
698         XUnmapWindow(ioncore_g.dpy, cwin->win);
699         
700         if(cwin->orig_bw!=0)
701             configure_cwin_bw(cwin->win, cwin->orig_bw);
702         
703         if(reparent_root(cwin)){
704             if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
705                 XMapWindow(ioncore_g.dpy, cwin->win);
706                 /* Make sure the topmost window has focus; it doesn't really
707                  * matter which one has as long as some has.
708                  */
709                 xwindow_do_set_focus(cwin->win);
710             }else{
711                 set_clientwin_state(cwin, WithdrawnState);
712                 netwm_delete_state(cwin);
713             }
714         }
715         
716         XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
717         XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
718     }
719     
720     clientwin_clear_colormaps(cwin);
721     
722     region_deinit((WRegion*)cwin);
723 }
724
725
726
727 static bool mrsh_u_c(WHookDummy *fn, void *param)
728 {
729     fn(*(Window*)param);
730     return TRUE;
731 }
732
733 static bool mrsh_u_extl(ExtlFn fn, void *param)
734 {
735     double d=*(Window*)param;
736     extl_call(fn, "d", NULL, d);
737     return TRUE;
738 }
739
740 static void clientwin_do_unmapped(WClientWin *cwin, Window win)
741 {
742     cwin->flags|=CLIENTWIN_UNMAP_RQ;
743     
744     /* First try a graceful chain-dispose */
745     if(!region_rqdispose((WRegion*)cwin)){
746         /* But force dispose anyway */
747         region_dispose((WRegion*)cwin);
748     }
749     
750     hook_call(clientwin_unmapped_hook, &win, mrsh_u_c, mrsh_u_extl);
751 }
752
753 /* Used when the window was unmapped */
754 void clientwin_unmapped(WClientWin *cwin)
755 {
756     clientwin_do_unmapped(cwin, cwin->win);
757 }
758
759
760 /* Used when the window was deastroyed */
761 void clientwin_destroyed(WClientWin *cwin)
762 {
763     Window win=cwin->win;
764     XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
765     XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
766     xwindow_unmanaged_selectinput(cwin->win, 0);
767     cwin->win=None;
768     clientwin_do_unmapped(cwin, win);
769 }
770
771
772 /*}}}*/
773
774
775 /*{{{ Kill/close */
776
777
778 static bool send_clientmsg(Window win, Atom a, Time stmp)
779 {
780     XClientMessageEvent ev;
781     
782     ev.type=ClientMessage;
783     ev.window=win;
784     ev.message_type=ioncore_g.atom_wm_protocols;
785     ev.format=32;
786     ev.data.l[0]=a;
787     ev.data.l[1]=stmp;
788     
789     return (XSendEvent(ioncore_g.dpy, win, False, 0L, (XEvent*)&ev)!=0);
790 }
791
792
793 /*EXTL_DOC
794  * Attempt to kill (with \code{XKillWindow}) the client that owns 
795  * the X window correspoding to \var{cwin}.
796  */
797 EXTL_EXPORT_MEMBER
798 void clientwin_kill(WClientWin *cwin)
799 {
800     XKillClient(ioncore_g.dpy, cwin->win);
801 }
802
803
804 void clientwin_rqclose(WClientWin *cwin, bool relocate_ignored)
805 {
806     /* Ignore relocate parameter -- client windows can always be 
807      * destroyed by the application in any case, so way may just as
808      * well assume relocate is always set.
809      */
810     
811     if(cwin->flags&CLIENTWIN_P_WM_DELETE){
812         send_clientmsg(cwin->win, ioncore_g.atom_wm_delete, 
813                        ioncore_get_timestamp());
814     }else{
815         warn(TR("Client does not support the WM_DELETE protocol."));
816     }
817 }
818
819
820 /*}}}*/
821
822
823 /*{{{ State (hide/show) */
824
825
826 static void set_clientwin_state(WClientWin *cwin, int state)
827 {
828     if(cwin->state!=state){
829         cwin->state=state;
830         xwindow_set_state_property(cwin->win, state);
831     }
832 }
833
834
835 static void hide_clientwin(WClientWin *cwin)
836 {
837     region_pointer_focus_hack(&cwin->region);
838
839     if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
840         XMoveWindow(ioncore_g.dpy, cwin->win,
841                     -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h);
842         return;
843     }
844     
845     set_clientwin_state(cwin, IconicState);
846     XSelectInput(ioncore_g.dpy, cwin->win,
847                  cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
848     XUnmapWindow(ioncore_g.dpy, cwin->win);
849     XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
850 }
851
852
853 static void show_clientwin(WClientWin *cwin)
854 {
855     if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
856         XMoveWindow(ioncore_g.dpy, cwin->win,
857                     REGION_GEOM(cwin).x, REGION_GEOM(cwin).y);
858         if(cwin->state==NormalState)
859             return;
860     }
861     
862     XSelectInput(ioncore_g.dpy, cwin->win,
863                  cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
864     XMapWindow(ioncore_g.dpy, cwin->win);
865     XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
866     set_clientwin_state(cwin, NormalState);
867 }
868
869
870 /*}}}*/
871
872
873 /*{{{ Resize/reparent/reconf helpers */
874
875
876 void clientwin_notify_rootpos(WClientWin *cwin, int rootx, int rooty)
877 {
878     XEvent ce;
879     Window win;
880     
881     if(cwin==NULL)
882         return;
883     
884     win=cwin->win;
885     
886     ce.xconfigure.type=ConfigureNotify;
887     ce.xconfigure.event=win;
888     ce.xconfigure.window=win;
889     ce.xconfigure.x=rootx-cwin->orig_bw;
890     ce.xconfigure.y=rooty-cwin->orig_bw;
891     ce.xconfigure.width=REGION_GEOM(cwin).w;
892     ce.xconfigure.height=REGION_GEOM(cwin).h;
893     ce.xconfigure.border_width=cwin->orig_bw;
894     ce.xconfigure.above=None;
895     ce.xconfigure.override_redirect=False;
896     
897     XSelectInput(ioncore_g.dpy, win, cwin->event_mask&~StructureNotifyMask);
898     XSendEvent(ioncore_g.dpy, win, False, StructureNotifyMask, &ce);
899     XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
900 }
901
902
903 static void sendconfig_clientwin(WClientWin *cwin)
904 {
905     int rootx, rooty;
906     
907     region_rootpos(&cwin->region, &rootx, &rooty);
908     clientwin_notify_rootpos(cwin, rootx, rooty);
909 }
910
911
912 static void do_reparent_clientwin(WClientWin *cwin, Window win, int x, int y)
913 {
914     XSelectInput(ioncore_g.dpy, cwin->win,
915                  cwin->event_mask&~StructureNotifyMask);
916     XReparentWindow(ioncore_g.dpy, cwin->win, win, x, y);
917     XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
918 }
919
920
921 static void convert_geom(const WFitParams *fp, 
922                          WClientWin *cwin, WRectangle *geom)
923 {
924     WFitParams fptmp=*fp;
925     WSizePolicy szplcy=SIZEPOLICY_FULL_EXACT;
926     
927     /*if(cwin->szplcy!=SIZEPOLICY_DEFAULT)
928         szplcy=cwin->szplcy;*/
929     
930     sizepolicy(&szplcy, (WRegion*)cwin, NULL, REGION_RQGEOM_WEAK_ALL, &fptmp);
931     
932     *geom=fptmp.g;
933 }
934
935
936 /*}}}*/
937
938
939 /*{{{ Region dynfuns */
940
941
942 static bool clientwin_fitrep(WClientWin *cwin, WWindow *np, 
943                              const WFitParams *fp)
944 {
945     WRectangle geom;
946     bool changes;
947     int w, h;
948
949     if(np!=NULL && !region_same_rootwin((WRegion*)cwin, (WRegion*)np))
950         return FALSE;
951     
952     if(fp->mode&REGION_FIT_WHATEVER){
953         geom.x=fp->g.x;
954         geom.y=fp->g.y;
955         geom.w=REGION_GEOM(cwin).w;
956         geom.h=REGION_GEOM(cwin).h;
957     }else{
958         geom=fp->g;
959     }
960     
961     changes=(REGION_GEOM(cwin).x!=geom.x ||
962              REGION_GEOM(cwin).y!=geom.y ||
963              REGION_GEOM(cwin).w!=geom.w ||
964              REGION_GEOM(cwin).h!=geom.h);
965     
966     REGION_GEOM(cwin)=geom;
967     
968     if(np==NULL && !changes)
969         return TRUE;
970     
971     if(np!=NULL){
972         region_unset_parent((WRegion*)cwin);
973         do_reparent_clientwin(cwin, np->win, geom.x, geom.y);
974         region_set_parent((WRegion*)cwin, np);
975         sendconfig_clientwin(cwin);
976         
977         if(!REGION_IS_FULLSCREEN(cwin))
978             cwin->flags&=~CLIENTWIN_FS_RQ;
979
980         netwm_update_state(cwin);
981     }
982     
983     w=maxof(1, geom.w);
984     h=maxof(1, geom.h);
985     
986     if(cwin->flags&CLIENTWIN_PROP_ACROBATIC && !REGION_IS_MAPPED(cwin)){
987         XMoveResizeWindow(ioncore_g.dpy, cwin->win,
988                           -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h, 
989                           w, h);
990     }else{
991         XMoveResizeWindow(ioncore_g.dpy, cwin->win, geom.x, geom.y, w, h);
992     }
993     
994     cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
995     
996     return TRUE;
997 }
998
999
1000 static void clientwin_map(WClientWin *cwin)
1001 {
1002     show_clientwin(cwin);
1003     REGION_MARK_MAPPED(cwin);
1004 }
1005
1006
1007 static void clientwin_unmap(WClientWin *cwin)
1008 {
1009     hide_clientwin(cwin);
1010     REGION_MARK_UNMAPPED(cwin);
1011 }
1012
1013
1014 static void clientwin_do_set_focus(WClientWin *cwin, bool warp)
1015 {
1016     if(cwin->flags&CLIENTWIN_P_WM_TAKE_FOCUS){
1017         Time stmp=ioncore_get_timestamp();
1018         send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp);
1019     }
1020
1021     region_finalise_focusing((WRegion*)cwin, cwin->win, warp);
1022     
1023     XSync(ioncore_g.dpy, 0);
1024 }
1025
1026
1027 void clientwin_restack(WClientWin *cwin, Window other, int mode)
1028 {
1029     xwindow_restack(cwin->win, other, mode);
1030 }
1031        
1032
1033 void clientwin_stacking(WClientWin *cwin, Window *bottomret, Window *topret)
1034 {
1035     *bottomret=cwin->win;
1036     *topret=cwin->win;
1037 }
1038
1039
1040 static Window clientwin_x_window(WClientWin *cwin)
1041 {
1042     return cwin->win;
1043 }
1044
1045
1046 static void clientwin_activated(WClientWin *cwin)
1047 {
1048     clientwin_install_colormap(cwin);
1049 }
1050
1051
1052 static void clientwin_size_hints(WClientWin *cwin, WSizeHints *hints_ret)
1053 {
1054     if(cwin->flags&CLIENTWIN_FS_RQ){
1055         /* Do not use size hints, when full screen mode has been
1056          * requested by the client window itself.
1057          */
1058         sizehints_clear(hints_ret);
1059     }else{
1060         xsizehints_to_sizehints(&cwin->size_hints, hints_ret);
1061     }
1062 }
1063
1064
1065 static int clientwin_orientation(WClientWin *cwin)
1066 {
1067     return (cwin->flags&CLIENTWIN_PROP_O_VERT
1068             ? REGION_ORIENTATION_VERTICAL
1069             : (cwin->flags&CLIENTWIN_PROP_O_HORIZ
1070                ? REGION_ORIENTATION_HORIZONTAL
1071                : REGION_ORIENTATION_NONE));
1072 }
1073
1074
1075 /*}}}*/
1076
1077
1078 /*{{{ Identity & lookup */
1079
1080
1081 /*EXTL_DOC
1082  * Returns a table containing the properties \code{WM_CLASS} (table entries
1083  * \var{instance} and \var{class}) and  \code{WM_WINDOW_ROLE} (\var{role})
1084  * properties for \var{cwin}. If a property is not set, the corresponding 
1085  * field(s) are unset in the  table.
1086  */
1087 EXTL_SAFE
1088 EXTL_EXPORT_MEMBER
1089 ExtlTab clientwin_get_ident(WClientWin *cwin)
1090 {
1091     char **p=NULL, **p2=NULL, *wrole=NULL;
1092     int n=0, n2=0, n3=0, tmp=0;
1093     Window tforwin=None;
1094     ExtlTab tab;
1095     bool dockapp_hack=FALSE;
1096     
1097     p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
1098     
1099     p2=xwindow_get_text_property(cwin->win, ioncore_g.atom_dockapp_hack, &n2);
1100     
1101     dockapp_hack=(n2>0);
1102     
1103     if(p==NULL){
1104         /* Some dockapps do actually have WM_CLASS, so use it. */
1105         p=p2;
1106         n=n2;
1107         p2=NULL;
1108     }
1109     
1110     wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, 
1111                                       &n3);
1112     
1113     tab=extl_create_table();
1114     if(n>=2 && p[1]!=NULL)
1115         extl_table_sets_s(tab, "class", p[1]);
1116     if(n>=1 && p[0]!=NULL)
1117         extl_table_sets_s(tab, "instance", p[0]);
1118     if(wrole!=NULL)
1119         extl_table_sets_s(tab, "role", wrole);
1120     
1121     if(XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin) 
1122        && tforwin!=None){
1123         extl_table_sets_b(tab, "is_transient", TRUE);
1124     }
1125     
1126     if(dockapp_hack)
1127         extl_table_sets_b(tab, "is_dockapp", TRUE);
1128     
1129     if(p!=NULL)
1130         XFreeStringList(p);
1131     if(p2!=NULL)
1132         XFreeStringList(p2);
1133     if(wrole!=NULL)
1134         free(wrole);
1135     
1136     return tab;
1137 }
1138
1139
1140 /*}}}*/
1141
1142
1143 /*{{{ ConfigureRequest */
1144
1145
1146 static bool check_fs_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev)
1147 {
1148     /* check full screen request */
1149     if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){
1150         WRegion *grp=region_groupleader_of((WRegion*)cwin);
1151         WScreen *scr=clientwin_fullscreen_chkrq(cwin, ev->width, ev->height);
1152         
1153         if(scr!=NULL && REGION_MANAGER(grp)!=(WRegion*)scr){
1154             bool sw=clientwin_fullscreen_may_switchto(cwin);
1155             
1156             cwin->flags|=CLIENTWIN_FS_RQ;
1157             
1158             if(!region_fullscreen_scr(grp, scr, sw))
1159                 cwin->flags&=~CLIENTWIN_FS_RQ;
1160                 
1161             return TRUE;
1162         }
1163     }
1164
1165     return FALSE;
1166 }
1167
1168
1169 static bool check_normal_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev)
1170 {
1171     if(ev->value_mask&(CWX|CWY|CWWidth|CWHeight)){
1172         WRQGeomParams rq=RQGEOMPARAMS_INIT;
1173         int gdx=0, gdy=0;
1174
1175         rq.flags=REGION_RQGEOM_WEAK_ALL|REGION_RQGEOM_ABSOLUTE;
1176         
1177         if(cwin->size_hints.flags&PWinGravity){
1178             rq.flags|=REGION_RQGEOM_GRAVITY;
1179             rq.gravity=cwin->size_hints.win_gravity;
1180         }
1181         
1182         /* Do I need to insert another disparaging comment on the person who
1183          * invented special server-supported window borders that are not 
1184          * accounted for in the window size? Keep it simple, stupid!
1185          */
1186         if(cwin->size_hints.flags&PWinGravity){
1187             gdx=xgravity_deltax(cwin->size_hints.win_gravity, 
1188                                -cwin->orig_bw, -cwin->orig_bw);
1189             gdy=xgravity_deltay(cwin->size_hints.win_gravity, 
1190                                -cwin->orig_bw, -cwin->orig_bw);
1191         }
1192         
1193         region_rootpos((WRegion*)cwin, &(rq.geom.x), &(rq.geom.y));
1194         rq.geom.w=REGION_GEOM(cwin).w;
1195         rq.geom.h=REGION_GEOM(cwin).h;
1196         
1197         if(ev->value_mask&CWWidth){
1198             /* If x was not changed, keep reference point where it was */
1199             if(cwin->size_hints.flags&PWinGravity){
1200                 rq.geom.x+=xgravity_deltax(cwin->size_hints.win_gravity, 0,
1201                                            ev->width-rq.geom.w);
1202             }
1203             rq.geom.w=maxof(ev->width, 1);
1204             rq.flags&=~REGION_RQGEOM_WEAK_W;
1205         }
1206         if(ev->value_mask&CWHeight){
1207             /* If y was not changed, keep reference point where it was */
1208             if(cwin->size_hints.flags&PWinGravity){
1209                 rq.geom.y+=xgravity_deltay(cwin->size_hints.win_gravity, 0,
1210                                            ev->height-rq.geom.h);
1211             }
1212             rq.geom.h=maxof(ev->height, 1);
1213             rq.flags&=~REGION_RQGEOM_WEAK_H;
1214         }
1215         if(ev->value_mask&CWX){
1216             rq.geom.x=ev->x+gdx;
1217             rq.flags&=~REGION_RQGEOM_WEAK_X;
1218         }
1219         if(ev->value_mask&CWY){
1220             rq.geom.y=ev->y+gdy;
1221             rq.flags&=~REGION_RQGEOM_WEAK_Y;
1222         }
1223         
1224         region_rqgeom((WRegion*)cwin, &rq, NULL);
1225         
1226         return TRUE;
1227     }
1228     
1229     return FALSE;
1230 }
1231
1232
1233 void clientwin_handle_configure_request(WClientWin *cwin,
1234                                         XConfigureRequestEvent *ev)
1235 {
1236     if(ev->value_mask&CWBorderWidth)
1237         cwin->orig_bw=ev->border_width;
1238     
1239     cwin->flags|=CLIENTWIN_NEED_CFGNTFY;
1240
1241     if(!(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ)){
1242         if(!check_fs_cfgrq(cwin, ev))
1243             check_normal_cfgrq(cwin, ev);
1244     }
1245
1246     if(cwin->flags&CLIENTWIN_NEED_CFGNTFY){
1247         sendconfig_clientwin(cwin);
1248         cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
1249     }
1250 }
1251
1252
1253 /*}}}*/
1254
1255
1256 /*{{{ Kludges */
1257
1258
1259 /*EXTL_DOC
1260  * Attempts to fix window size problems with non-ICCCM compliant
1261  * programs.
1262  */
1263 EXTL_EXPORT_MEMBER
1264 void clientwin_nudge(WClientWin *cwin)
1265 {
1266     XResizeWindow(ioncore_g.dpy, cwin->win, 
1267                   2*REGION_GEOM(cwin).w, 2*REGION_GEOM(cwin).h);
1268     XFlush(ioncore_g.dpy);
1269     XResizeWindow(ioncore_g.dpy, cwin->win, 
1270                   REGION_GEOM(cwin).w, REGION_GEOM(cwin).h);
1271 }
1272
1273
1274 /*}}}*/
1275
1276
1277 /*{{{ Misc. */
1278
1279
1280 /*EXTL_DOC
1281  * Return the X window id for the client window.
1282  */
1283 EXTL_SAFE
1284 EXTL_EXPORT_MEMBER
1285 double clientwin_xid(WClientWin *cwin)
1286 {
1287     return cwin->win;
1288 }
1289
1290
1291 /*}}}*/
1292
1293
1294 /*{{{ Save/load */
1295
1296
1297 static int last_checkcode=1;
1298
1299
1300 static ExtlTab clientwin_get_configuration(WClientWin *cwin)
1301 {
1302     int chkc=0;
1303     ExtlTab tab;
1304     SMCfgCallback *cfg_cb;
1305     SMAddCallback *add_cb;
1306     
1307     tab=region_get_base_configuration((WRegion*)cwin);
1308
1309     extl_table_sets_d(tab, "windowid", (double)(cwin->win));
1310     
1311     if(last_checkcode!=0){
1312         chkc=last_checkcode++;
1313         xwindow_set_integer_property(cwin->win, ioncore_g.atom_checkcode, 
1314                                      chkc);
1315         extl_table_sets_i(tab, "checkcode", chkc);
1316     }
1317
1318     ioncore_get_sm_callbacks(&add_cb, &cfg_cb);
1319     
1320     if(cfg_cb!=NULL)
1321         cfg_cb(cwin, tab);
1322     
1323     return tab;
1324 }
1325
1326
1327 static void do_sm(ExtlTab tab)
1328 {
1329     SMAddCallback *add_cb;
1330     SMCfgCallback *cfg_cb;
1331     WPHolder *ph;
1332     
1333     ioncore_get_sm_callbacks(&add_cb, &cfg_cb);
1334     
1335     if(add_cb!=NULL){
1336         ph=ioncore_get_load_pholder();
1337     
1338         if(ph!=NULL){
1339             if(!add_cb(ph, tab))
1340                 destroy_obj((Obj*)ph);
1341         }
1342     }
1343 }
1344
1345     
1346 WRegion *clientwin_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1347 {
1348     double wind=0;
1349     Window win=None;
1350     int chkc=0, real_chkc=0;
1351     WClientWin *cwin=NULL;
1352     XWindowAttributes attr;
1353     WRectangle rg;
1354     bool got_chkc=FALSE;
1355     
1356     if(!extl_table_gets_d(tab, "windowid", &wind) ||
1357        !extl_table_gets_i(tab, "checkcode", &chkc)){
1358         return NULL;
1359     }
1360     
1361     win=(Window)wind;
1362
1363     if(XWINDOW_REGION_OF(win)!=NULL){
1364         warn("Client window %x already managed.", win);
1365         return NULL;
1366     }
1367
1368     got_chkc=xwindow_get_integer_property(win, ioncore_g.atom_checkcode, 
1369                                           &real_chkc);
1370     
1371     if(!got_chkc || real_chkc!=chkc){
1372         do_sm(tab);
1373         return NULL;
1374     }
1375
1376     /* Found it! */
1377     
1378     if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
1379         warn(TR("Window %#x disappeared."), win);
1380         return NULL;
1381     }
1382     
1383     if(attr.root!=region_root_of((WRegion*)par))
1384         return NULL;
1385         
1386     if(attr.override_redirect || 
1387        (ioncore_g.opmode==IONCORE_OPMODE_INIT && attr.map_state!=IsViewable)){
1388         warn(TR("Saved client window does not want to be managed."));
1389         return NULL;
1390     }
1391     
1392     cwin=create_clientwin(par, win, &attr);
1393     
1394     if(cwin==NULL)
1395         return FALSE;
1396     
1397     /* Reparent and resize taking limits set by size hints into account */
1398     convert_geom(fp, cwin, &rg);
1399     REGION_GEOM(cwin)=rg;
1400     do_reparent_clientwin(cwin, par->win, rg.x, rg.y);
1401     XResizeWindow(ioncore_g.dpy, win, maxof(1, rg.w), maxof(1, rg.h));
1402     
1403     if(!postmanage_check(cwin, &attr)){
1404         clientwin_destroyed(cwin);
1405         return NULL;
1406     }
1407     
1408     return (WRegion*)cwin;
1409 }
1410
1411
1412 /*}}}*/
1413
1414
1415 /*{{{ Dynfuntab and class info */
1416
1417
1418 static DynFunTab clientwin_dynfuntab[]={
1419     {(DynFun*)region_fitrep,
1420      (DynFun*)clientwin_fitrep},
1421
1422     {region_map,
1423      clientwin_map},
1424     
1425     {region_unmap,
1426      clientwin_unmap},
1427     
1428     {region_do_set_focus, 
1429      clientwin_do_set_focus},
1430     
1431     {region_notify_rootpos, 
1432      clientwin_notify_rootpos},
1433     
1434     {region_restack, 
1435      clientwin_restack},
1436
1437     {region_stacking, 
1438      clientwin_stacking},
1439     
1440     {(DynFun*)region_xwindow, 
1441      (DynFun*)clientwin_x_window},
1442     
1443     {region_activated, 
1444      clientwin_activated},
1445     
1446     {region_size_hints, 
1447      clientwin_size_hints},
1448      
1449     {(DynFun*)region_orientation, 
1450      (DynFun*)clientwin_orientation},
1451     
1452     {(DynFun*)region_rqclose, 
1453      (DynFun*)clientwin_rqclose},
1454     
1455     {(DynFun*)region_get_configuration,
1456      (DynFun*)clientwin_get_configuration},
1457
1458     END_DYNFUNTAB
1459 };
1460
1461
1462 EXTL_EXPORT
1463 IMPLCLASS(WClientWin, WRegion, clientwin_deinit, clientwin_dynfuntab);
1464
1465
1466 /*}}}*/