2 * ion/ioncore/clientwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
16 #include <libtu/objp.h>
17 #include <libtu/minmax.h>
18 #include <libextl/extl.h>
19 #include <libmainloop/defer.h>
20 #include <libmainloop/hooks.h>
27 #include "clientwin.h"
37 #include "fullscreen.h"
49 static void set_clientwin_state(WClientWin *cwin, int state);
50 static bool send_clientmsg(Window win, Atom a, Time stmp);
53 WHook *clientwin_do_manage_alt=NULL;
54 WHook *clientwin_mapped_hook=NULL;
55 WHook *clientwin_unmapped_hook=NULL;
56 WHook *clientwin_property_change_hook=NULL;
59 /*{{{ Get properties */
62 void clientwin_get_protocols(WClientWin *cwin)
64 Atom *protocols=NULL, *p;
67 cwin->flags&=~(CLIENTWIN_P_WM_DELETE|CLIENTWIN_P_WM_TAKE_FOCUS);
69 if(!XGetWMProtocols(ioncore_g.dpy, cwin->win, &protocols, &n))
72 for(p=protocols; n; n--, p++){
73 if(*p==ioncore_g.atom_wm_delete)
74 cwin->flags|=CLIENTWIN_P_WM_DELETE;
75 else if(*p==ioncore_g.atom_wm_take_focus)
76 cwin->flags|=CLIENTWIN_P_WM_TAKE_FOCUS;
80 XFree((char*)protocols);
84 static WSizePolicy get_sizepolicy_winprop(WClientWin *cwin,
90 if(extl_table_gets_s(cwin->proptab, propname, &szplcy)){
91 string2sizepolicy(szplcy, &value);
98 #define SIZEHINT_PROPS (CLIENTWIN_PROP_MAXSIZE| \
99 CLIENTWIN_PROP_MINSIZE| \
100 CLIENTWIN_PROP_ASPECT| \
101 CLIENTWIN_PROP_RSZINC| \
102 CLIENTWIN_PROP_I_MAXSIZE| \
103 CLIENTWIN_PROP_I_MINSIZE| \
104 CLIENTWIN_PROP_I_ASPECT| \
105 CLIENTWIN_PROP_I_RSZINC)
108 #define DO_SZH(NAME, FLAG, IFLAG, SZHFLAG, W, H, C) \
109 if(extl_table_is_bool_set(tab, "ignore_" NAME)){ \
110 cwin->flags|=IFLAG; \
111 }else if(extl_table_gets_t(tab, NAME, &tab2)){ \
112 if(extl_table_gets_i(tab2, "w", &i1) && \
113 extl_table_gets_i(tab2, "h", &i2)){ \
114 cwin->size_hints.W=i1; \
115 cwin->size_hints.H=i2; \
117 cwin->size_hints.flags|=SZHFLAG; \
120 extl_unref_table(tab2); \
124 static void clientwin_get_winprops(WClientWin *cwin)
129 tab=ioncore_get_winprop(cwin);
133 if(tab==extl_table_none())
136 if(extl_table_is_bool_set(tab, "transparent"))
137 cwin->flags|=CLIENTWIN_PROP_TRANSPARENT;
139 if(extl_table_is_bool_set(tab, "acrobatic"))
140 cwin->flags|=CLIENTWIN_PROP_ACROBATIC;
142 DO_SZH("max_size", CLIENTWIN_PROP_MAXSIZE, CLIENTWIN_PROP_I_MAXSIZE,
143 PMaxSize, max_width, max_height, );
145 DO_SZH("min_size", CLIENTWIN_PROP_MINSIZE, CLIENTWIN_PROP_I_MINSIZE,
146 PMinSize, min_width, min_height, );
148 DO_SZH("resizeinc", CLIENTWIN_PROP_RSZINC, CLIENTWIN_PROP_I_RSZINC,
149 PResizeInc, width_inc, height_inc, );
151 DO_SZH("aspect", CLIENTWIN_PROP_ASPECT, CLIENTWIN_PROP_I_ASPECT,
152 PAspect, min_aspect.x, min_aspect.y,
153 { cwin->size_hints.max_aspect.x=i1;
154 cwin->size_hints.max_aspect.y=i2;
157 if(extl_table_is_bool_set(tab, "ignore_cfgrq"))
158 cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ;
161 cwin->szplcy=get_sizepolicy_winprop(cwin, "sizepolicy",
163 cwin->transient_szplcy=get_sizepolicy_winprop(cwin,
164 "transient_sizepolicy",
170 void clientwin_get_size_hints(WClientWin *cwin)
172 XSizeHints tmp=cwin->size_hints;
174 xwindow_get_sizehints(cwin->win, &(cwin->size_hints));
176 if(cwin->flags&CLIENTWIN_PROP_I_MAXSIZE){
177 cwin->size_hints.flags&=~PMaxSize;
178 }else if(cwin->flags&CLIENTWIN_PROP_MAXSIZE){
179 cwin->size_hints.max_width=tmp.max_width;
180 cwin->size_hints.max_height=tmp.max_height;
181 cwin->size_hints.flags|=PMaxSize;
184 if(cwin->flags&CLIENTWIN_PROP_I_MINSIZE){
185 cwin->size_hints.flags&=~PMinSize;
186 }else if(cwin->flags&CLIENTWIN_PROP_MINSIZE){
187 cwin->size_hints.min_width=tmp.min_width;
188 cwin->size_hints.min_height=tmp.min_height;
189 cwin->size_hints.flags|=PMinSize;
192 if(cwin->flags&CLIENTWIN_PROP_I_ASPECT){
193 cwin->size_hints.flags&=~PAspect;
194 }else if(cwin->flags&CLIENTWIN_PROP_ASPECT){
195 cwin->size_hints.min_aspect=tmp.min_aspect;
196 cwin->size_hints.max_aspect=tmp.max_aspect;
197 cwin->size_hints.flags|=PAspect;
200 if(cwin->flags&CLIENTWIN_PROP_I_RSZINC){
201 cwin->size_hints.flags&=~PResizeInc;
202 }else if(cwin->flags&CLIENTWIN_PROP_RSZINC){
203 cwin->size_hints.width_inc=tmp.width_inc;
204 cwin->size_hints.height_inc=tmp.height_inc;
205 cwin->size_hints.flags|=PResizeInc;
210 void clientwin_get_set_name(WClientWin *cwin)
216 list=netwm_get_name(cwin);
219 list=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
221 cwin->flags|=CLIENTWIN_USE_NET_WM_NAME;
225 /* Special condition kludge: property exists, but couldn't
226 * be converted to a string list.
228 clientwin_set_name(cwin, (n==-1 ? "???" : NULL));
230 clientwin_set_name(cwin, *list);
231 XFreeStringList(list);
236 /* Some standard winprops */
239 bool clientwin_get_switchto(const WClientWin *cwin)
243 if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
246 if(extl_table_gets_b(cwin->proptab, "switchto", &b))
249 return ioncore_g.switchto_new;
253 int clientwin_get_transient_mode(const WClientWin *cwin)
256 int mode=TRANSIENT_MODE_NORMAL;
258 if(extl_table_gets_s(cwin->proptab, "transient_mode", &s)){
259 if(strcmp(s, "current")==0)
260 mode=TRANSIENT_MODE_CURRENT;
261 else if(strcmp(s, "off")==0)
262 mode=TRANSIENT_MODE_OFF;
272 /*{{{ Manage/create */
275 static void configure_cwin_bw(Window win, int bw)
278 ulong wcmask=CWBorderWidth;
281 XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc);
285 static void set_sane_gravity(Window win)
287 XSetWindowAttributes attr;
289 attr.win_gravity=NorthWestGravity;
291 XChangeWindowAttributes(ioncore_g.dpy, win,
292 CWWinGravity, &attr);
296 static bool clientwin_init(WClientWin *cwin, WWindow *par, Window win,
297 XWindowAttributes *attr)
303 cwin->state=WithdrawnState;
309 fp.mode=REGION_FIT_EXACT;
311 /* The idiot who invented special server-supported window borders that
312 * are not accounted for in the window size should be "taken behind a
315 cwin->orig_bw=attr->border_width;
316 configure_cwin_bw(cwin->win, 0);
317 if(cwin->orig_bw!=0 && cwin->size_hints.flags&PWinGravity){
318 fp.g.x+=xgravity_deltax(cwin->size_hints.win_gravity,
319 -cwin->orig_bw, -cwin->orig_bw);
320 fp.g.y+=xgravity_deltay(cwin->size_hints.win_gravity,
321 -cwin->orig_bw, -cwin->orig_bw);
324 set_sane_gravity(cwin->win);
326 cwin->transient_for=None;
329 cwin->cmap=attr->colormap;
333 cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN;
335 region_init(&(cwin->region), par, &fp);
337 cwin->region.flags|=REGION_GRAB_ON_PARENT;
338 region_add_bindmap(&cwin->region, ioncore_clientwin_bindmap);
340 XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
342 clientwin_register(cwin);
343 clientwin_get_set_name(cwin);
344 clientwin_get_colormaps(cwin);
345 clientwin_get_protocols(cwin);
346 clientwin_get_winprops(cwin);
347 clientwin_get_size_hints(cwin);
349 XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)cwin);
350 XAddToSaveSet(ioncore_g.dpy, win);
356 static WClientWin *create_clientwin(WWindow *par, Window win,
357 XWindowAttributes *attr)
359 CREATEOBJ_IMPL(WClientWin, clientwin, (p, par, win, attr));
364 WClientWin *clientwin_get_transient_for(const WClientWin *cwin)
367 WClientWin *tfor=NULL;
369 if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_NORMAL)
372 if(!XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin))
378 tfor=XWINDOW_REGION_OF_T(tforwin, WClientWin);
381 warn(TR("The transient_for hint for \"%s\" points to itself."),
382 region_name((WRegion*)cwin));
383 }else if(tfor==NULL){
384 if(xwindow_region_of(tforwin)!=NULL){
385 warn(TR("Client window \"%s\" has broken transient_for hint. "
386 "(\"Extended WM hints\" multi-parent brain damage?)"),
387 region_name((WRegion*)cwin));
389 }else if(!region_same_rootwin((WRegion*)cwin, (WRegion*)tfor)){
390 warn(TR("The transient_for window for \"%s\" is not on the same "
391 "screen."), region_name((WRegion*)cwin));
400 static bool postmanage_check(WClientWin *cwin, XWindowAttributes *attr)
402 /* Check that the window exists. The previous check and selectinput
403 * do not seem to catch all cases of window destroyal.
405 XSync(ioncore_g.dpy, False);
407 if(XGetWindowAttributes(ioncore_g.dpy, cwin->win, attr))
410 warn(TR("Window %#x disappeared."), cwin->win);
416 static bool do_manage_mrsh(bool (*fn)(WClientWin *cwin, WManageParams *pm),
419 return fn((WClientWin*)p[0], (WManageParams*)p[1]);
424 static bool do_manage_mrsh_extl(ExtlFn fn, void **p)
426 WClientWin *cwin=(WClientWin*)p[0];
427 WManageParams *mp=(WManageParams*)p[1];
428 ExtlTab t=manageparams_to_table(mp);
431 extl_call(fn, "ot", "b", cwin, t, &ret);
435 return (ret && REGION_MANAGER(cwin)!=NULL);
439 /* This is called when a window is mapped on the root window.
440 * We want to check if we should manage the window and how and
443 WClientWin* ioncore_manage_clientwin(Window win, bool maprq)
446 WClientWin *cwin=NULL;
447 XWindowAttributes attr;
449 int init_state=NormalState;
450 WManageParams param=MANAGEPARAMS_INIT;
456 /* Is the window already being managed? */
457 cwin=XWINDOW_REGION_OF_T(win, WClientWin);
461 /* Select for UnmapNotify and DestroyNotify as the
462 * window might get destroyed or unmapped in the meanwhile.
464 xwindow_unmanaged_selectinput(win, StructureNotifyMask);
469 hints=XGetWMHints(ioncore_g.dpy, win);
471 if(hints!=NULL && hints->flags&StateHint)
472 init_state=hints->initial_state;
474 if(!param.dockapp && init_state==WithdrawnState &&
475 hints->flags&IconWindowHint && hints->icon_window!=None){
476 /* The dockapp might be displaying its "main" window if no
477 * wm that understands dockapps has been managing it.
480 XUnmapWindow(ioncore_g.dpy, win);
482 xwindow_unmanaged_selectinput(win, 0);
484 win=hints->icon_window;
486 /* It is a dockapp, do everything again from the beginning, now
487 * with the icon window.
496 if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
498 warn(TR("Window %#x disappeared."), win);
502 attr.width=maxof(attr.width, 1);
503 attr.height=maxof(attr.height, 1);
505 /* Do we really want to manage it? */
506 if(!param.dockapp && (attr.override_redirect ||
507 (!maprq && attr.map_state!=IsViewable))){
511 /* Find root window */
512 FOR_ALL_ROOTWINS(rootwin){
513 if(WROOTWIN_ROOT(rootwin)==attr.root)
518 warn(TR("Unable to find a matching root window!"));
522 /* Allocate and initialize */
523 cwin=create_clientwin((WWindow*)rootwin, win, &attr);
530 param.geom=REGION_GEOM(cwin);
532 param.switchto=(init_state!=IconicState && clientwin_get_switchto(cwin));
533 param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
534 param.gravity=(cwin->size_hints.flags&PWinGravity
535 ? cwin->size_hints.win_gravity
537 param.tfor=clientwin_get_transient_for(cwin);
539 if(!extl_table_gets_b(cwin->proptab, "userpos", ¶m.userpos))
540 param.userpos=(cwin->size_hints.flags&USPosition);
542 if(cwin->flags&SIZEHINT_PROPS){
543 /* If size hints have been messed with, readjust requested geometry
544 * here. If programs themselves give incompatible geometries and
545 * things don't look good then, it's their fault.
547 region_size_hints_correct((WRegion*)cwin, ¶m.geom.w, ¶m.geom.h,
554 if(!hook_call_alt(clientwin_do_manage_alt, &mrshpm,
555 (WHookMarshall*)do_manage_mrsh,
556 (WHookMarshallExtl*)do_manage_mrsh_extl)){
557 warn(TR("Unable to manage client window %#x."), win);
561 if(ioncore_g.opmode==IONCORE_OPMODE_NORMAL &&
562 !region_is_fully_mapped((WRegion*)cwin) &&
563 !region_skip_focus((WRegion*)cwin)){
564 region_set_activity((WRegion*)cwin, SETPARAM_SET);
567 if(postmanage_check(cwin, &attr)){
568 if(param.jumpto && ioncore_g.focus_next==NULL)
569 region_goto((WRegion*)cwin);
570 hook_call_o(clientwin_mapped_hook, (Obj*)cwin);
575 clientwin_destroyed(cwin);
579 xwindow_unmanaged_selectinput(win, 0);
584 void clientwin_tfor_changed(WClientWin *cwin)
587 WManageParams param=MANAGEPARAMS_INIT;
588 bool succeeded=FALSE;
589 param.tfor=clientwin_get_transient_for(cwin);
593 region_rootpos((WRegion*)cwin, &(param.geom.x), &(param.geom.y));
594 param.geom.w=REGION_GEOM(cwin).w;
595 param.geom.h=REGION_GEOM(cwin).h;
598 param.switchto=region_may_control_focus((WRegion*)cwin);
599 param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
600 param.gravity=ForgetGravity;
602 CALL_ALT_B(succeeded, clientwin_do_manage_alt, (cwin, ¶m));
603 warn("WM_TRANSIENT_FOR changed for \"%s\".",
604 region_name((WRegion*)cwin));
606 warn(TR("Changes is WM_TRANSIENT_FOR property are unsupported."));
614 /*{{{ Unmanage/destroy */
617 static bool reparent_root(WClientWin *cwin)
619 XWindowAttributes attr;
624 if(!XGetWindowAttributes(ioncore_g.dpy, cwin->win, &attr))
627 par=REGION_PARENT(cwin);
630 x=REGION_GEOM(cwin).x;
631 y=REGION_GEOM(cwin).y;
633 int dr=REGION_GEOM(par).w-REGION_GEOM(cwin).w-REGION_GEOM(cwin).x;
634 int db=REGION_GEOM(par).h-REGION_GEOM(cwin).h-REGION_GEOM(cwin).y;
638 XTranslateCoordinates(ioncore_g.dpy, par->win, attr.root, 0, 0,
641 x-=xgravity_deltax(cwin->size_hints.win_gravity,
642 maxof(0, REGION_GEOM(cwin).x), dr);
643 y-=xgravity_deltay(cwin->size_hints.win_gravity,
644 maxof(0, REGION_GEOM(cwin).y), db);
647 XReparentWindow(ioncore_g.dpy, cwin->win, attr.root, x, y);
653 void clientwin_deinit(WClientWin *cwin)
658 region_pointer_focus_hack(&cwin->region);
660 xwindow_unmanaged_selectinput(cwin->win, 0);
661 XUnmapWindow(ioncore_g.dpy, cwin->win);
664 configure_cwin_bw(cwin->win, cwin->orig_bw);
666 if(reparent_root(cwin)){
667 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
668 XMapWindow(ioncore_g.dpy, cwin->win);
669 /* Make sure the topmost window has focus; it doesn't really
670 * matter which one has as long as some has.
672 xwindow_do_set_focus(cwin->win);
674 set_clientwin_state(cwin, WithdrawnState);
675 netwm_delete_state(cwin);
679 XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
680 XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
683 clientwin_clear_colormaps(cwin);
685 region_deinit((WRegion*)cwin);
690 static bool mrsh_u_c(WHookDummy *fn, void *param)
696 static bool mrsh_u_extl(ExtlFn fn, void *param)
698 double d=*(Window*)param;
699 extl_call(fn, "d", NULL, d);
703 static void clientwin_do_unmapped(WClientWin *cwin, Window win)
705 cwin->flags|=CLIENTWIN_UNMAP_RQ;
707 /* First try a graceful chain-dispose */
708 if(!region_rqdispose((WRegion*)cwin)){
709 /* But force dispose anyway */
710 region_dispose((WRegion*)cwin);
713 hook_call(clientwin_unmapped_hook, &win, mrsh_u_c, mrsh_u_extl);
716 /* Used when the window was unmapped */
717 void clientwin_unmapped(WClientWin *cwin)
719 clientwin_do_unmapped(cwin, cwin->win);
723 /* Used when the window was deastroyed */
724 void clientwin_destroyed(WClientWin *cwin)
726 Window win=cwin->win;
727 XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
728 XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
729 xwindow_unmanaged_selectinput(cwin->win, 0);
731 clientwin_do_unmapped(cwin, win);
741 static bool send_clientmsg(Window win, Atom a, Time stmp)
743 XClientMessageEvent ev;
745 ev.type=ClientMessage;
747 ev.message_type=ioncore_g.atom_wm_protocols;
752 return (XSendEvent(ioncore_g.dpy, win, False, 0L, (XEvent*)&ev)!=0);
757 * Attempt to kill (with XKillWindow) the client that owns the X
758 * window correspoding to \var{cwin}.
761 void clientwin_kill(WClientWin *cwin)
763 XKillClient(ioncore_g.dpy, cwin->win);
767 void clientwin_rqclose(WClientWin *cwin, bool relocate_ignored)
769 /* Ignore relocate parameter -- client windows can always be
770 * destroyed by the application in any case, so way may just as
771 * well assume relocate is always set.
774 if(cwin->flags&CLIENTWIN_P_WM_DELETE){
775 send_clientmsg(cwin->win, ioncore_g.atom_wm_delete,
776 ioncore_get_timestamp());
778 warn(TR("Client does not support the WM_DELETE protocol."));
786 /*{{{ State (hide/show) */
789 static void set_clientwin_state(WClientWin *cwin, int state)
791 if(cwin->state!=state){
793 xwindow_set_state_property(cwin->win, state);
798 static void hide_clientwin(WClientWin *cwin)
800 region_pointer_focus_hack(&cwin->region);
802 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
803 XMoveWindow(ioncore_g.dpy, cwin->win,
804 -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h);
808 set_clientwin_state(cwin, IconicState);
809 XSelectInput(ioncore_g.dpy, cwin->win,
810 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
811 XUnmapWindow(ioncore_g.dpy, cwin->win);
812 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
816 static void show_clientwin(WClientWin *cwin)
818 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
819 XMoveWindow(ioncore_g.dpy, cwin->win,
820 REGION_GEOM(cwin).x, REGION_GEOM(cwin).y);
821 if(cwin->state==NormalState)
825 XSelectInput(ioncore_g.dpy, cwin->win,
826 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
827 XMapWindow(ioncore_g.dpy, cwin->win);
828 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
829 set_clientwin_state(cwin, NormalState);
836 /*{{{ Resize/reparent/reconf helpers */
839 void clientwin_notify_rootpos(WClientWin *cwin, int rootx, int rooty)
849 ce.xconfigure.type=ConfigureNotify;
850 ce.xconfigure.event=win;
851 ce.xconfigure.window=win;
852 ce.xconfigure.x=rootx-cwin->orig_bw;
853 ce.xconfigure.y=rooty-cwin->orig_bw;
854 ce.xconfigure.width=REGION_GEOM(cwin).w;
855 ce.xconfigure.height=REGION_GEOM(cwin).h;
856 ce.xconfigure.border_width=cwin->orig_bw;
857 ce.xconfigure.above=None;
858 ce.xconfigure.override_redirect=False;
860 XSelectInput(ioncore_g.dpy, win, cwin->event_mask&~StructureNotifyMask);
861 XSendEvent(ioncore_g.dpy, win, False, StructureNotifyMask, &ce);
862 XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
866 static void sendconfig_clientwin(WClientWin *cwin)
870 region_rootpos(&cwin->region, &rootx, &rooty);
871 clientwin_notify_rootpos(cwin, rootx, rooty);
875 static void do_reparent_clientwin(WClientWin *cwin, Window win, int x, int y)
877 XSelectInput(ioncore_g.dpy, cwin->win,
878 cwin->event_mask&~StructureNotifyMask);
879 XReparentWindow(ioncore_g.dpy, cwin->win, win, x, y);
880 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
884 static void convert_geom(const WFitParams *fp,
885 WClientWin *cwin, WRectangle *geom)
887 WFitParams fptmp=*fp;
888 WSizePolicy szplcy=SIZEPOLICY_FULL_EXACT;
890 /*if(cwin->szplcy!=SIZEPOLICY_DEFAULT)
891 szplcy=cwin->szplcy;*/
893 sizepolicy(&szplcy, (WRegion*)cwin, NULL, REGION_RQGEOM_WEAK_ALL, &fptmp);
902 /*{{{ Region dynfuns */
905 static bool clientwin_fitrep(WClientWin *cwin, WWindow *np,
906 const WFitParams *fp)
912 if(np!=NULL && !region_same_rootwin((WRegion*)cwin, (WRegion*)np))
915 if(fp->mode®ION_FIT_WHATEVER){
918 geom.w=REGION_GEOM(cwin).w;
919 geom.h=REGION_GEOM(cwin).h;
924 changes=(REGION_GEOM(cwin).x!=geom.x ||
925 REGION_GEOM(cwin).y!=geom.y ||
926 REGION_GEOM(cwin).w!=geom.w ||
927 REGION_GEOM(cwin).h!=geom.h);
929 REGION_GEOM(cwin)=geom;
931 if(np==NULL && !changes)
935 region_unset_parent((WRegion*)cwin);
936 do_reparent_clientwin(cwin, np->win, geom.x, geom.y);
937 region_set_parent((WRegion*)cwin, np);
938 sendconfig_clientwin(cwin);
940 if(!REGION_IS_FULLSCREEN(cwin))
941 cwin->flags&=~CLIENTWIN_FS_RQ;
943 netwm_update_state(cwin);
949 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC && !REGION_IS_MAPPED(cwin)){
950 XMoveResizeWindow(ioncore_g.dpy, cwin->win,
951 -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h,
954 XMoveResizeWindow(ioncore_g.dpy, cwin->win, geom.x, geom.y, w, h);
957 cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
963 static void clientwin_map(WClientWin *cwin)
965 show_clientwin(cwin);
966 REGION_MARK_MAPPED(cwin);
970 static void clientwin_unmap(WClientWin *cwin)
972 hide_clientwin(cwin);
973 REGION_MARK_UNMAPPED(cwin);
977 static void clientwin_do_set_focus(WClientWin *cwin, bool warp)
979 if(cwin->flags&CLIENTWIN_P_WM_TAKE_FOCUS){
980 Time stmp=ioncore_get_timestamp();
981 send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp);
984 region_finalise_focusing((WRegion*)cwin, cwin->win, warp);
986 XSync(ioncore_g.dpy, 0);
990 void clientwin_restack(WClientWin *cwin, Window other, int mode)
992 xwindow_restack(cwin->win, other, mode);
996 void clientwin_stacking(WClientWin *cwin, Window *bottomret, Window *topret)
998 *bottomret=cwin->win;
1003 static Window clientwin_x_window(WClientWin *cwin)
1009 static void clientwin_activated(WClientWin *cwin)
1011 clientwin_install_colormap(cwin);
1015 static void clientwin_size_hints(WClientWin *cwin, WSizeHints *hints_ret)
1017 if(cwin->flags&CLIENTWIN_FS_RQ){
1018 /* Do not use size hints, when full screen mode has been
1019 * requested by the client window itself.
1021 sizehints_clear(hints_ret);
1023 xsizehints_to_sizehints(&cwin->size_hints, hints_ret);
1031 /*{{{ Identity & lookup */
1035 * Returns a table containing the properties \code{WM_CLASS} (table entries
1036 * \var{instance} and \var{class}) and \code{WM_WINDOW_ROLE} (\var{role})
1037 * properties for \var{cwin}. If a property is not set, the corresponding
1038 * field(s) are unset in the table.
1042 ExtlTab clientwin_get_ident(WClientWin *cwin)
1044 char **p=NULL, *wrole=NULL;
1045 int n=0, n2=0, n3=0, tmp=0;
1048 p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
1049 wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, &n2);
1051 tab=extl_create_table();
1052 if(n>=2 && p[1]!=NULL)
1053 extl_table_sets_s(tab, "class", p[1]);
1054 if(n>=1 && p[0]!=NULL)
1055 extl_table_sets_s(tab, "instance", p[0]);
1057 extl_table_sets_s(tab, "role", wrole);
1071 /*{{{ ConfigureRequest */
1074 static bool check_fs_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev)
1076 /* check full screen request */
1077 if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){
1078 WRegion *grp=region_groupleader_of((WRegion*)cwin);
1079 WScreen *scr=clientwin_fullscreen_chkrq(cwin, ev->width, ev->height);
1081 if(scr!=NULL && REGION_MANAGER(grp)!=(WRegion*)scr){
1082 bool sw=clientwin_fullscreen_may_switchto(cwin);
1084 cwin->flags|=CLIENTWIN_FS_RQ;
1086 if(!region_fullscreen_scr(grp, scr, sw))
1087 cwin->flags&=~CLIENTWIN_FS_RQ;
1097 static bool check_normal_cfgrq(WClientWin *cwin, XConfigureRequestEvent *ev)
1099 if(ev->value_mask&(CWX|CWY|CWWidth|CWHeight)){
1100 WRQGeomParams rq=RQGEOMPARAMS_INIT;
1103 rq.flags=REGION_RQGEOM_WEAK_ALL|REGION_RQGEOM_ABSOLUTE;
1105 if(cwin->size_hints.flags&PWinGravity){
1106 rq.flags|=REGION_RQGEOM_GRAVITY;
1107 rq.gravity=cwin->size_hints.win_gravity;
1110 /* Do I need to insert another disparaging comment on the person who
1111 * invented special server-supported window borders that are not
1112 * accounted for in the window size? Keep it simple, stupid!
1114 if(cwin->size_hints.flags&PWinGravity){
1115 gdx=xgravity_deltax(cwin->size_hints.win_gravity,
1116 -cwin->orig_bw, -cwin->orig_bw);
1117 gdy=xgravity_deltay(cwin->size_hints.win_gravity,
1118 -cwin->orig_bw, -cwin->orig_bw);
1121 region_rootpos((WRegion*)cwin, &(rq.geom.x), &(rq.geom.y));
1122 rq.geom.w=REGION_GEOM(cwin).w;
1123 rq.geom.h=REGION_GEOM(cwin).h;
1125 if(ev->value_mask&CWWidth){
1126 /* If x was not changed, keep reference point where it was */
1127 if(cwin->size_hints.flags&PWinGravity){
1128 rq.geom.x+=xgravity_deltax(cwin->size_hints.win_gravity, 0,
1129 ev->width-rq.geom.w);
1131 rq.geom.w=maxof(ev->width, 1);
1132 rq.flags&=~REGION_RQGEOM_WEAK_W;
1134 if(ev->value_mask&CWHeight){
1135 /* If y was not changed, keep reference point where it was */
1136 if(cwin->size_hints.flags&PWinGravity){
1137 rq.geom.y+=xgravity_deltay(cwin->size_hints.win_gravity, 0,
1138 ev->height-rq.geom.h);
1140 rq.geom.h=maxof(ev->height, 1);
1141 rq.flags&=~REGION_RQGEOM_WEAK_H;
1143 if(ev->value_mask&CWX){
1144 rq.geom.x=ev->x+gdx;
1145 rq.flags&=~REGION_RQGEOM_WEAK_X;
1147 if(ev->value_mask&CWY){
1148 rq.geom.y=ev->y+gdy;
1149 rq.flags&=~REGION_RQGEOM_WEAK_Y;
1152 region_rqgeom((WRegion*)cwin, &rq, NULL);
1161 void clientwin_handle_configure_request(WClientWin *cwin,
1162 XConfigureRequestEvent *ev)
1164 if(ev->value_mask&CWBorderWidth)
1165 cwin->orig_bw=ev->border_width;
1167 cwin->flags|=CLIENTWIN_NEED_CFGNTFY;
1169 if(!(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ)){
1170 if(!check_fs_cfgrq(cwin, ev))
1171 check_normal_cfgrq(cwin, ev);
1174 if(cwin->flags&CLIENTWIN_NEED_CFGNTFY){
1175 sendconfig_clientwin(cwin);
1176 cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
1188 * Attempts to fix window size problems with non-ICCCM compliant
1192 void clientwin_nudge(WClientWin *cwin)
1194 XResizeWindow(ioncore_g.dpy, cwin->win,
1195 2*REGION_GEOM(cwin).w, 2*REGION_GEOM(cwin).h);
1196 XFlush(ioncore_g.dpy);
1197 XResizeWindow(ioncore_g.dpy, cwin->win,
1198 REGION_GEOM(cwin).w, REGION_GEOM(cwin).h);
1209 * Return the X window id for the client window.
1213 double clientwin_xid(WClientWin *cwin)
1225 static int last_checkcode=1;
1228 static ExtlTab clientwin_get_configuration(WClientWin *cwin)
1232 SMCfgCallback *cfg_cb;
1233 SMAddCallback *add_cb;
1235 tab=region_get_base_configuration((WRegion*)cwin);
1237 extl_table_sets_d(tab, "windowid", (double)(cwin->win));
1239 if(last_checkcode!=0){
1240 chkc=last_checkcode++;
1241 xwindow_set_integer_property(cwin->win, ioncore_g.atom_checkcode,
1243 extl_table_sets_i(tab, "checkcode", chkc);
1246 ioncore_get_sm_callbacks(&add_cb, &cfg_cb);
1255 WRegion *clientwin_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1259 int chkc=0, real_chkc=0;
1260 WClientWin *cwin=NULL;
1261 XWindowAttributes attr;
1265 if(!extl_table_gets_d(tab, "windowid", &wind) ||
1266 !extl_table_gets_i(tab, "checkcode", &chkc)){
1272 if(XWINDOW_REGION_OF(win)!=NULL){
1273 warn("Client window %x already managed.", win);
1277 got_chkc=xwindow_get_integer_property(win, ioncore_g.atom_checkcode,
1280 if(!got_chkc || real_chkc!=chkc){
1281 ioncore_clientwin_load_missing();
1287 if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
1288 warn(TR("Window %#x disappeared."), win);
1292 if(attr.override_redirect ||
1293 (ioncore_g.opmode==IONCORE_OPMODE_INIT && attr.map_state!=IsViewable)){
1294 warn(TR("Saved client window does not want to be managed."));
1302 attr.height=fp->g.h;
1305 cwin=create_clientwin(par, win, &attr);
1310 /* Reparent and resize taking limits set by size hints into account */
1311 convert_geom(fp, cwin, &rg);
1312 REGION_GEOM(cwin)=rg;
1313 do_reparent_clientwin(cwin, par->win, rg.x, rg.y);
1314 XResizeWindow(ioncore_g.dpy, win, maxof(1, rg.w), maxof(1, rg.h));
1316 if(!postmanage_check(cwin, &attr)){
1317 clientwin_destroyed(cwin);
1321 return (WRegion*)cwin;
1328 /*{{{ Dynfuntab and class info */
1331 static DynFunTab clientwin_dynfuntab[]={
1332 {(DynFun*)region_fitrep,
1333 (DynFun*)clientwin_fitrep},
1341 {region_do_set_focus,
1342 clientwin_do_set_focus},
1344 {region_notify_rootpos,
1345 clientwin_notify_rootpos},
1351 clientwin_stacking},
1353 {(DynFun*)region_xwindow,
1354 (DynFun*)clientwin_x_window},
1357 clientwin_activated},
1360 clientwin_size_hints},
1362 {(DynFun*)region_rqclose,
1363 (DynFun*)clientwin_rqclose},
1365 {(DynFun*)region_get_configuration,
1366 (DynFun*)clientwin_get_configuration},
1373 IMPLCLASS(WClientWin, WRegion, clientwin_deinit, clientwin_dynfuntab);