2 * ion/ioncore/clientwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2006.
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"
46 static void set_clientwin_state(WClientWin *cwin, int state);
47 static bool send_clientmsg(Window win, Atom a, Time stmp);
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;
56 /*{{{ Get properties */
59 void clientwin_get_protocols(WClientWin *cwin)
61 Atom *protocols=NULL, *p;
64 cwin->flags&=~(CLIENTWIN_P_WM_DELETE|CLIENTWIN_P_WM_TAKE_FOCUS);
66 if(!XGetWMProtocols(ioncore_g.dpy, cwin->win, &protocols, &n))
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;
77 XFree((char*)protocols);
81 static bool get_winprop_fn_set=FALSE;
82 static ExtlFn get_winprop_fn;
85 * Set function used to look up winprops.
88 void ioncore_set_get_winprop_fn(ExtlFn fn)
90 if(get_winprop_fn_set)
91 extl_unref_fn(get_winprop_fn);
92 get_winprop_fn=extl_ref_fn(fn);
93 get_winprop_fn_set=TRUE;
97 static WSizePolicy get_sizepolicy_winprop(WClientWin *cwin,
103 if(extl_table_gets_s(cwin->proptab, propname, &szplcy)){
104 string2sizepolicy(szplcy, &value);
111 #define SIZEHINT_PROPS (CLIENTWIN_PROP_MAXSIZE| \
112 CLIENTWIN_PROP_MINSIZE| \
113 CLIENTWIN_PROP_ASPECT| \
114 CLIENTWIN_PROP_IGNORE_RSZINC)
117 static void clientwin_get_winprops(WClientWin *cwin)
123 if(!get_winprop_fn_set)
127 ret=extl_call(get_winprop_fn, "o", "t", cwin, &tab);
128 extl_unprotect(NULL);
135 if(tab==extl_table_none())
138 if(extl_table_is_bool_set(tab, "transparent"))
139 cwin->flags|=CLIENTWIN_PROP_TRANSPARENT;
141 if(extl_table_is_bool_set(tab, "acrobatic"))
142 cwin->flags|=CLIENTWIN_PROP_ACROBATIC;
144 if(extl_table_gets_t(tab, "max_size", &tab2)){
145 if(extl_table_gets_i(tab2, "w", &i1) &&
146 extl_table_gets_i(tab2, "h", &i2)){
147 cwin->size_hints.max_width=i1;
148 cwin->size_hints.max_height=i2;
149 cwin->size_hints.flags|=PMaxSize;
150 cwin->flags|=CLIENTWIN_PROP_MAXSIZE;
152 extl_unref_table(tab2);
155 if(extl_table_gets_t(tab, "min_size", &tab2)){
156 if(extl_table_gets_i(tab2, "w", &i1) &&
157 extl_table_gets_i(tab2, "h", &i2)){
158 cwin->size_hints.min_width=i1;
159 cwin->size_hints.min_height=i2;
160 cwin->size_hints.flags|=PMinSize;
161 cwin->flags|=CLIENTWIN_PROP_MINSIZE;
163 extl_unref_table(tab2);
166 if(extl_table_gets_t(tab, "aspect", &tab2)){
167 if(extl_table_gets_i(tab2, "w", &i1) &&
168 extl_table_gets_i(tab2, "h", &i2)){
169 cwin->size_hints.min_aspect.x=i1;
170 cwin->size_hints.max_aspect.x=i1;
171 cwin->size_hints.min_aspect.y=i2;
172 cwin->size_hints.max_aspect.y=i2;
173 cwin->size_hints.flags|=PAspect;
174 cwin->flags|=CLIENTWIN_PROP_ASPECT;
176 extl_unref_table(tab2);
179 if(extl_table_is_bool_set(tab, "ignore_resizeinc"))
180 cwin->flags|=CLIENTWIN_PROP_IGNORE_RSZINC;
182 if(extl_table_is_bool_set(tab, "ignore_cfgrq"))
183 cwin->flags|=CLIENTWIN_PROP_IGNORE_CFGRQ;
186 cwin->szplcy=get_sizepolicy_winprop(cwin, "sizepolicy",
188 cwin->transient_szplcy=get_sizepolicy_winprop(cwin,
189 "transient_sizepolicy",
195 void clientwin_get_size_hints(WClientWin *cwin)
197 XSizeHints tmp=cwin->size_hints;
199 xwindow_get_sizehints(cwin->win, &(cwin->size_hints));
201 if(cwin->flags&CLIENTWIN_PROP_MAXSIZE){
202 cwin->size_hints.max_width=tmp.max_width;
203 cwin->size_hints.max_height=tmp.max_height;
204 cwin->size_hints.flags|=PMaxSize;
207 if(cwin->flags&CLIENTWIN_PROP_MINSIZE){
208 cwin->size_hints.min_width=tmp.min_width;
209 cwin->size_hints.min_height=tmp.min_height;
210 cwin->size_hints.flags|=PMinSize;
213 if(cwin->flags&CLIENTWIN_PROP_ASPECT){
214 cwin->size_hints.min_aspect=tmp.min_aspect;
215 cwin->size_hints.max_aspect=tmp.max_aspect;
216 cwin->size_hints.flags|=PAspect;
219 if(cwin->flags&CLIENTWIN_PROP_IGNORE_RSZINC)
220 cwin->size_hints.flags&=~PResizeInc;
224 void clientwin_get_set_name(WClientWin *cwin)
230 list=netwm_get_name(cwin);
233 list=xwindow_get_text_property(cwin->win, XA_WM_NAME, &n);
235 cwin->flags|=CLIENTWIN_USE_NET_WM_NAME;
239 /* Special condition kludge: property exists, but couldn't
240 * be converted to a string list.
242 clientwin_set_name(cwin, (n==-1 ? "???" : NULL));
244 clientwin_set_name(cwin, *list);
245 XFreeStringList(list);
250 /* Some standard winprops */
253 bool clientwin_get_switchto(const WClientWin *cwin)
257 if(ioncore_g.opmode==IONCORE_OPMODE_INIT)
260 if(extl_table_gets_b(cwin->proptab, "switchto", &b))
263 return ioncore_g.switchto_new;
267 int clientwin_get_transient_mode(const WClientWin *cwin)
270 int mode=TRANSIENT_MODE_NORMAL;
272 if(extl_table_gets_s(cwin->proptab, "transient_mode", &s)){
273 if(strcmp(s, "current")==0)
274 mode=TRANSIENT_MODE_CURRENT;
275 else if(strcmp(s, "off")==0)
276 mode=TRANSIENT_MODE_OFF;
286 /*{{{ Manage/create */
289 static void configure_cwin_bw(Window win, int bw)
292 ulong wcmask=CWBorderWidth;
295 XConfigureWindow(ioncore_g.dpy, win, wcmask, &wc);
299 static void set_sane_gravity(Window win)
301 XSetWindowAttributes attr;
303 attr.win_gravity=NorthWestGravity;
305 XChangeWindowAttributes(ioncore_g.dpy, win,
306 CWWinGravity, &attr);
310 static bool clientwin_init(WClientWin *cwin, WWindow *par, Window win,
311 XWindowAttributes *attr)
317 cwin->state=WithdrawnState;
323 fp.mode=REGION_FIT_EXACT;
325 /* The idiot who invented special server-supported window borders that
326 * are not accounted for in the window size should be "taken behind a
329 cwin->orig_bw=attr->border_width;
330 configure_cwin_bw(cwin->win, 0);
331 if(cwin->orig_bw!=0 && cwin->size_hints.flags&PWinGravity){
332 fp.g.x+=xgravity_deltax(cwin->size_hints.win_gravity,
333 -cwin->orig_bw, -cwin->orig_bw);
334 fp.g.y+=xgravity_deltay(cwin->size_hints.win_gravity,
335 -cwin->orig_bw, -cwin->orig_bw);
338 set_sane_gravity(cwin->win);
340 cwin->transient_for=None;
343 cwin->cmap=attr->colormap;
347 cwin->event_mask=IONCORE_EVENTMASK_CLIENTWIN;
349 cwin->fs_pholder=NULL;
351 region_init(&(cwin->region), par, &fp);
353 cwin->region.flags|=REGION_GRAB_ON_PARENT;
354 region_add_bindmap(&cwin->region, ioncore_clientwin_bindmap);
356 XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
358 clientwin_register(cwin);
359 clientwin_get_set_name(cwin);
360 clientwin_get_colormaps(cwin);
361 clientwin_get_protocols(cwin);
362 clientwin_get_winprops(cwin);
363 clientwin_get_size_hints(cwin);
365 XSaveContext(ioncore_g.dpy, win, ioncore_g.win_context, (XPointer)cwin);
366 XAddToSaveSet(ioncore_g.dpy, win);
372 static WClientWin *create_clientwin(WWindow *par, Window win,
373 XWindowAttributes *attr)
375 CREATEOBJ_IMPL(WClientWin, clientwin, (p, par, win, attr));
379 static bool handle_target_prop(WClientWin *cwin, const WManageParams *param)
382 char *target_name=NULL;
384 if(extl_table_gets_s(cwin->proptab, "target", &target_name)){
385 r=ioncore_lookup_region(target_name, NULL);
390 if(region_manage_clientwin(r, cwin, param,
391 MANAGE_REDIR_PREFER_NO))
400 WClientWin *clientwin_get_transient_for(const WClientWin *cwin)
403 WClientWin *tfor=NULL;
405 if(clientwin_get_transient_mode(cwin)!=TRANSIENT_MODE_NORMAL)
408 if(!XGetTransientForHint(ioncore_g.dpy, cwin->win, &tforwin))
414 tfor=XWINDOW_REGION_OF_T(tforwin, WClientWin);
417 warn(TR("The transient_for hint for \"%s\" points to itself."),
418 region_name((WRegion*)cwin));
419 }else if(tfor==NULL){
420 if(xwindow_region_of(tforwin)!=NULL){
421 warn(TR("Client window \"%s\" has broken transient_for hint. "
422 "(\"Extended WM hints\" multi-parent brain damage?)"),
423 region_name((WRegion*)cwin));
425 }else if(!region_same_rootwin((WRegion*)cwin, (WRegion*)tfor)){
426 warn(TR("The transient_for window for \"%s\" is not on the same "
427 "screen."), region_name((WRegion*)cwin));
436 static bool postmanage_check(WClientWin *cwin, XWindowAttributes *attr)
438 /* Check that the window exists. The previous check and selectinput
439 * do not seem to catch all cases of window destroyal.
441 XSync(ioncore_g.dpy, False);
443 if(XGetWindowAttributes(ioncore_g.dpy, cwin->win, attr))
446 warn(TR("Window %#x disappeared."), cwin->win);
452 static bool do_manage_mrsh(bool (*fn)(WClientWin *cwin, WManageParams *pm),
455 return fn((WClientWin*)p[0], (WManageParams*)p[1]);
460 static bool do_manage_mrsh_extl(ExtlFn fn, void **p)
462 WClientWin *cwin=(WClientWin*)p[0];
463 WManageParams *mp=(WManageParams*)p[1];
464 ExtlTab t=manageparams_to_table(mp);
467 extl_call(fn, "ot", "b", cwin, t, &ret);
471 return (ret && REGION_MANAGER(cwin)!=NULL);
475 /* This is called when a window is mapped on the root window.
476 * We want to check if we should manage the window and how and
479 WClientWin* ioncore_manage_clientwin(Window win, bool maprq)
482 WClientWin *cwin=NULL;
483 XWindowAttributes attr;
485 int init_state=NormalState;
486 WManageParams param=MANAGEPARAMS_INIT;
491 /* Is the window already being managed? */
492 cwin=XWINDOW_REGION_OF_T(win, WClientWin);
496 /* Select for UnmapNotify and DestroyNotify as the
497 * window might get destroyed or unmapped in the meanwhile.
499 xwindow_unmanaged_selectinput(win, StructureNotifyMask);
504 hints=XGetWMHints(ioncore_g.dpy, win);
506 if(hints!=NULL && hints->flags&StateHint)
507 init_state=hints->initial_state;
509 if(!param.dockapp && init_state==WithdrawnState &&
510 hints->flags&IconWindowHint && hints->icon_window!=None){
511 /* The dockapp might be displaying its "main" window if no
512 * wm that understands dockapps has been managing it.
515 XUnmapWindow(ioncore_g.dpy, win);
517 xwindow_unmanaged_selectinput(win, 0);
519 win=hints->icon_window;
521 /* It is a dockapp, do everything again from the beginning, now
522 * with the icon window.
531 if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
533 warn(TR("Window %#x disappeared."), win);
537 attr.width=maxof(attr.width, 1);
538 attr.height=maxof(attr.height, 1);
540 /* Do we really want to manage it? */
541 if(!param.dockapp && (attr.override_redirect ||
542 (!maprq && attr.map_state!=IsViewable))){
546 /* Find root window */
547 FOR_ALL_ROOTWINS(rootwin){
548 if(WROOTWIN_ROOT(rootwin)==attr.root)
553 warn(TR("Unable to find a matching root window!"));
557 /* Allocate and initialize */
558 cwin=create_clientwin((WWindow*)rootwin, win, &attr);
565 param.geom=REGION_GEOM(cwin);
567 param.userpos=(cwin->size_hints.flags&USPosition);
568 param.switchto=(init_state!=IconicState && clientwin_get_switchto(cwin));
569 param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
570 param.gravity=(cwin->size_hints.flags&PWinGravity
571 ? cwin->size_hints.win_gravity
573 param.tfor=clientwin_get_transient_for(cwin);
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.
580 region_size_hints_correct((WRegion*)cwin, ¶m.geom.w, ¶m.geom.h,
584 if(!handle_target_prop(cwin, ¶m)){
591 managed=hook_call_alt(clientwin_do_manage_alt, &mrshpm,
592 (WHookMarshall*)do_manage_mrsh,
593 (WHookMarshallExtl*)do_manage_mrsh_extl);
596 warn(TR("Unable to manage client window %#x."), win);
601 if(ioncore_g.opmode==IONCORE_OPMODE_NORMAL &&
602 !region_is_fully_mapped((WRegion*)cwin) &&
603 !region_skip_focus((WRegion*)cwin)){
604 region_set_activity((WRegion*)cwin, SETPARAM_SET);
608 if(postmanage_check(cwin, &attr)){
609 if(param.jumpto && ioncore_g.focus_next==NULL)
610 region_goto((WRegion*)cwin);
611 hook_call_o(clientwin_mapped_hook, (Obj*)cwin);
616 clientwin_destroyed(cwin);
620 xwindow_unmanaged_selectinput(win, 0);
625 void clientwin_tfor_changed(WClientWin *cwin)
628 WManageParams param=MANAGEPARAMS_INIT;
629 bool succeeded=FALSE;
630 param.tfor=clientwin_get_transient_for(cwin);
634 region_rootpos((WRegion*)cwin, &(param.geom.x), &(param.geom.y));
635 param.geom.w=REGION_GEOM(cwin).w;
636 param.geom.h=REGION_GEOM(cwin).h;
639 param.switchto=region_may_control_focus((WRegion*)cwin);
640 param.jumpto=extl_table_is_bool_set(cwin->proptab, "jumpto");
641 param.gravity=ForgetGravity;
643 CALL_ALT_B(succeeded, clientwin_do_manage_alt, (cwin, ¶m));
644 warn("WM_TRANSIENT_FOR changed for \"%s\".",
645 region_name((WRegion*)cwin));
647 warn(TR("Changes is WM_TRANSIENT_FOR property are unsupported."));
655 /*{{{ Unmanage/destroy */
658 static bool reparent_root(WClientWin *cwin)
660 XWindowAttributes attr;
665 if(!XGetWindowAttributes(ioncore_g.dpy, cwin->win, &attr))
668 par=REGION_PARENT(cwin);
671 x=REGION_GEOM(cwin).x;
672 y=REGION_GEOM(cwin).y;
674 int dr=REGION_GEOM(par).w-REGION_GEOM(cwin).w-REGION_GEOM(cwin).x;
675 int db=REGION_GEOM(par).h-REGION_GEOM(cwin).h-REGION_GEOM(cwin).y;
679 XTranslateCoordinates(ioncore_g.dpy, par->win, attr.root, 0, 0,
682 x-=xgravity_deltax(cwin->size_hints.win_gravity,
683 maxof(0, REGION_GEOM(cwin).x), dr);
684 y-=xgravity_deltay(cwin->size_hints.win_gravity,
685 maxof(0, REGION_GEOM(cwin).y), db);
688 XReparentWindow(ioncore_g.dpy, cwin->win, attr.root, x, y);
694 void clientwin_deinit(WClientWin *cwin)
699 xwindow_unmanaged_selectinput(cwin->win, 0);
700 XUnmapWindow(ioncore_g.dpy, cwin->win);
703 configure_cwin_bw(cwin->win, cwin->orig_bw);
705 if(reparent_root(cwin)){
706 if(ioncore_g.opmode==IONCORE_OPMODE_DEINIT){
707 XMapWindow(ioncore_g.dpy, cwin->win);
708 /* Make sure the topmost window has focus; it doesn't really
709 * matter which one has as long as some has.
711 xwindow_do_set_focus(cwin->win);
713 set_clientwin_state(cwin, WithdrawnState);
714 netwm_delete_state(cwin);
718 XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
719 XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
722 clientwin_clear_colormaps(cwin);
724 if(cwin->fs_pholder!=NULL){
725 WPHolder *ph=cwin->fs_pholder;
726 cwin->fs_pholder=NULL;
727 destroy_obj((Obj*)ph);
730 region_deinit((WRegion*)cwin);
735 static bool mrsh_u_c(WHookDummy *fn, void *param)
741 static bool mrsh_u_extl(ExtlFn fn, void *param)
743 double d=*(Window*)param;
744 extl_call(fn, "d", NULL, d);
748 static void clientwin_do_unmapped(WClientWin *cwin, Window win)
750 bool mcf=region_may_control_focus((WRegion*)cwin);
752 if(mcf && cwin->fs_pholder!=NULL)
753 pholder_goto(cwin->fs_pholder);
755 destroy_obj((Obj*)cwin);
757 hook_call(clientwin_unmapped_hook, &win, mrsh_u_c, mrsh_u_extl);
760 /* Used when the window was unmapped */
761 void clientwin_unmapped(WClientWin *cwin)
763 clientwin_do_unmapped(cwin, cwin->win);
767 /* Used when the window was deastroyed */
768 void clientwin_destroyed(WClientWin *cwin)
770 Window win=cwin->win;
771 XRemoveFromSaveSet(ioncore_g.dpy, cwin->win);
772 XDeleteContext(ioncore_g.dpy, cwin->win, ioncore_g.win_context);
773 xwindow_unmanaged_selectinput(cwin->win, 0);
775 clientwin_do_unmapped(cwin, win);
785 static bool send_clientmsg(Window win, Atom a, Time stmp)
787 XClientMessageEvent ev;
789 ev.type=ClientMessage;
791 ev.message_type=ioncore_g.atom_wm_protocols;
796 return (XSendEvent(ioncore_g.dpy, win, False, 0L, (XEvent*)&ev)!=0);
801 * Attempt to kill (with XKillWindow) the client that owns the X
802 * window correspoding to \var{cwin}.
805 void clientwin_kill(WClientWin *cwin)
807 XKillClient(ioncore_g.dpy, cwin->win);
811 bool clientwin_rqclose(WClientWin *cwin, bool relocate_ignored)
813 /* Ignore relocate parameter -- client windows can always be
814 * destroyed by the application in any case, so way may just as
815 * well assume relocate is always set.
818 if(cwin->flags&CLIENTWIN_P_WM_DELETE){
819 send_clientmsg(cwin->win, ioncore_g.atom_wm_delete,
820 ioncore_get_timestamp());
823 warn(TR("Client does not support the WM_DELETE protocol."));
832 /*{{{ State (hide/show) */
835 static void set_clientwin_state(WClientWin *cwin, int state)
837 if(cwin->state!=state){
839 xwindow_set_state_property(cwin->win, state);
844 static void hide_clientwin(WClientWin *cwin)
846 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
847 XMoveWindow(ioncore_g.dpy, cwin->win,
848 -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h);
852 set_clientwin_state(cwin, IconicState);
853 XSelectInput(ioncore_g.dpy, cwin->win,
854 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
855 XUnmapWindow(ioncore_g.dpy, cwin->win);
856 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
860 static void show_clientwin(WClientWin *cwin)
862 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC){
863 XMoveWindow(ioncore_g.dpy, cwin->win,
864 REGION_GEOM(cwin).x, REGION_GEOM(cwin).y);
865 if(cwin->state==NormalState)
869 XSelectInput(ioncore_g.dpy, cwin->win,
870 cwin->event_mask&~(StructureNotifyMask|EnterWindowMask));
871 XMapWindow(ioncore_g.dpy, cwin->win);
872 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
873 set_clientwin_state(cwin, NormalState);
880 /*{{{ Resize/reparent/reconf helpers */
883 void clientwin_notify_rootpos(WClientWin *cwin, int rootx, int rooty)
893 ce.xconfigure.type=ConfigureNotify;
894 ce.xconfigure.event=win;
895 ce.xconfigure.window=win;
896 ce.xconfigure.x=rootx-cwin->orig_bw;
897 ce.xconfigure.y=rooty-cwin->orig_bw;
898 ce.xconfigure.width=REGION_GEOM(cwin).w;
899 ce.xconfigure.height=REGION_GEOM(cwin).h;
900 ce.xconfigure.border_width=cwin->orig_bw;
901 ce.xconfigure.above=None;
902 ce.xconfigure.override_redirect=False;
904 XSelectInput(ioncore_g.dpy, win, cwin->event_mask&~StructureNotifyMask);
905 XSendEvent(ioncore_g.dpy, win, False, StructureNotifyMask, &ce);
906 XSelectInput(ioncore_g.dpy, win, cwin->event_mask);
910 static void sendconfig_clientwin(WClientWin *cwin)
914 region_rootpos(&cwin->region, &rootx, &rooty);
915 clientwin_notify_rootpos(cwin, rootx, rooty);
919 static void do_reparent_clientwin(WClientWin *cwin, Window win, int x, int y)
921 XSelectInput(ioncore_g.dpy, cwin->win,
922 cwin->event_mask&~StructureNotifyMask);
923 XReparentWindow(ioncore_g.dpy, cwin->win, win, x, y);
924 XSelectInput(ioncore_g.dpy, cwin->win, cwin->event_mask);
928 static void convert_geom(const WFitParams *fp,
929 WClientWin *cwin, WRectangle *geom)
931 WFitParams fptmp=*fp;
932 WSizePolicy szplcy=SIZEPOLICY_FULL_EXACT;
934 /*if(cwin->szplcy!=SIZEPOLICY_DEFAULT)
935 szplcy=cwin->szplcy;*/
937 sizepolicy(&szplcy, (WRegion*)cwin, NULL, REGION_RQGEOM_WEAK_ALL, &fptmp);
946 /*{{{ Region dynfuns */
949 static bool clientwin_fitrep(WClientWin *cwin, WWindow *np,
950 const WFitParams *fp)
956 if(np!=NULL && !region_same_rootwin((WRegion*)cwin, (WRegion*)np))
959 if(fp->mode®ION_FIT_WHATEVER){
962 geom.w=REGION_GEOM(cwin).w;
963 geom.h=REGION_GEOM(cwin).h;
968 changes=(REGION_GEOM(cwin).x!=geom.x ||
969 REGION_GEOM(cwin).y!=geom.y ||
970 REGION_GEOM(cwin).w!=geom.w ||
971 REGION_GEOM(cwin).h!=geom.h);
973 REGION_GEOM(cwin)=geom;
975 if(np==NULL && !changes)
979 region_unset_parent((WRegion*)cwin);
980 do_reparent_clientwin(cwin, np->win, geom.x, geom.y);
981 region_set_parent((WRegion*)cwin, np);
982 sendconfig_clientwin(cwin);
984 if(!REGION_IS_FULLSCREEN(cwin) && cwin->fs_pholder!=NULL){
985 WPHolder *ph=cwin->fs_pholder;
986 cwin->fs_pholder=NULL;
987 cwin->flags&=~CLIENTWIN_FS_RQ;
988 /* Can't destroy it yet - messes up mplex placeholder
991 mainloop_defer_destroy((Obj*)ph);
994 netwm_update_state(cwin);
1000 if(cwin->flags&CLIENTWIN_PROP_ACROBATIC && !REGION_IS_MAPPED(cwin)){
1001 XMoveResizeWindow(ioncore_g.dpy, cwin->win,
1002 -2*REGION_GEOM(cwin).w, -2*REGION_GEOM(cwin).h,
1005 XMoveResizeWindow(ioncore_g.dpy, cwin->win, geom.x, geom.y, w, h);
1008 cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
1014 static void clientwin_map(WClientWin *cwin)
1016 show_clientwin(cwin);
1017 REGION_MARK_MAPPED(cwin);
1021 static void clientwin_unmap(WClientWin *cwin)
1023 hide_clientwin(cwin);
1024 REGION_MARK_UNMAPPED(cwin);
1028 static void clientwin_do_set_focus(WClientWin *cwin, bool warp)
1030 if(cwin->flags&CLIENTWIN_P_WM_TAKE_FOCUS){
1031 Time stmp=ioncore_get_timestamp();
1032 send_clientmsg(cwin->win, ioncore_g.atom_wm_take_focus, stmp);
1035 region_finalise_focusing((WRegion*)cwin, cwin->win, warp);
1037 XSync(ioncore_g.dpy, 0);
1041 void clientwin_restack(WClientWin *cwin, Window other, int mode)
1043 xwindow_restack(cwin->win, other, mode);
1047 void clientwin_stacking(WClientWin *cwin, Window *bottomret, Window *topret)
1049 *bottomret=cwin->win;
1054 static Window clientwin_x_window(WClientWin *cwin)
1060 static void clientwin_activated(WClientWin *cwin)
1062 clientwin_install_colormap(cwin);
1066 static void clientwin_size_hints(WClientWin *cwin, WSizeHints *hints_ret)
1068 if(cwin->flags&CLIENTWIN_FS_RQ){
1069 /* Do not use size hints, when full screen mode has been
1070 * requested by the client window itself.
1072 sizehints_clear(hints_ret);
1074 xsizehints_to_sizehints(&cwin->size_hints, hints_ret);
1082 /*{{{ Identity & lookup */
1086 * Returns a table containing the properties \code{WM_CLASS} (table entries
1087 * \var{instance} and \var{class}) and \code{WM_WINDOW_ROLE} (\var{role})
1088 * properties for \var{cwin}. If a property is not set, the corresponding
1089 * field(s) are unset in the table.
1093 ExtlTab clientwin_get_ident(WClientWin *cwin)
1095 char **p=NULL, *wrole=NULL;
1096 int n=0, n2=0, n3=0, tmp=0;
1099 p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
1100 wrole=xwindow_get_string_property(cwin->win, ioncore_g.atom_wm_window_role, &n2);
1102 tab=extl_create_table();
1103 if(n>=2 && p[1]!=NULL)
1104 extl_table_sets_s(tab, "class", p[1]);
1105 if(n>=1 && p[0]!=NULL)
1106 extl_table_sets_s(tab, "instance", p[0]);
1108 extl_table_sets_s(tab, "role", wrole);
1122 /*{{{ ConfigureRequest */
1125 void clientwin_handle_configure_request(WClientWin *cwin,
1126 XConfigureRequestEvent *ev)
1128 if(ev->value_mask&CWBorderWidth)
1129 cwin->orig_bw=ev->border_width;
1131 if(cwin->flags&CLIENTWIN_PROP_IGNORE_CFGRQ){
1132 sendconfig_clientwin(cwin);
1136 /* check full screen request */
1137 if((ev->value_mask&(CWWidth|CWHeight))==(CWWidth|CWHeight)){
1138 bool sw=clientwin_fullscreen_may_switchto(cwin);
1139 if(clientwin_check_fullscreen_request(cwin, ev->width, ev->height, sw))
1143 cwin->flags|=CLIENTWIN_NEED_CFGNTFY;
1145 if(ev->value_mask&(CWX|CWY|CWWidth|CWHeight)){
1146 WRQGeomParams rq=RQGEOMPARAMS_INIT;
1149 rq.flags=REGION_RQGEOM_WEAK_ALL|REGION_RQGEOM_ABSOLUTE;
1151 if(cwin->size_hints.flags&PWinGravity){
1152 rq.flags|=REGION_RQGEOM_GRAVITY;
1153 rq.gravity=cwin->size_hints.win_gravity;
1156 /* Do I need to insert another disparaging comment on the person who
1157 * invented special server-supported window borders that are not
1158 * accounted for in the window size? Keep it simple, stupid!
1160 if(cwin->size_hints.flags&PWinGravity){
1161 gdx=xgravity_deltax(cwin->size_hints.win_gravity,
1162 -cwin->orig_bw, -cwin->orig_bw);
1163 gdy=xgravity_deltay(cwin->size_hints.win_gravity,
1164 -cwin->orig_bw, -cwin->orig_bw);
1167 region_rootpos((WRegion*)cwin, &(rq.geom.x), &(rq.geom.y));
1168 rq.geom.w=REGION_GEOM(cwin).w;
1169 rq.geom.h=REGION_GEOM(cwin).h;
1171 if(ev->value_mask&CWWidth){
1172 /* If x was not changed, keep reference point where it was */
1173 if(cwin->size_hints.flags&PWinGravity){
1174 rq.geom.x+=xgravity_deltax(cwin->size_hints.win_gravity, 0,
1175 ev->width-rq.geom.w);
1177 rq.geom.w=maxof(ev->width, 1);
1178 rq.flags&=~REGION_RQGEOM_WEAK_W;
1180 if(ev->value_mask&CWHeight){
1181 /* If y was not changed, keep reference point where it was */
1182 if(cwin->size_hints.flags&PWinGravity){
1183 rq.geom.y+=xgravity_deltay(cwin->size_hints.win_gravity, 0,
1184 ev->height-rq.geom.h);
1186 rq.geom.h=maxof(ev->height, 1);
1187 rq.flags&=~REGION_RQGEOM_WEAK_H;
1189 if(ev->value_mask&CWX){
1190 rq.geom.x=ev->x+gdx;
1191 rq.flags&=~REGION_RQGEOM_WEAK_X;
1193 if(ev->value_mask&CWY){
1194 rq.geom.y=ev->y+gdy;
1195 rq.flags&=~REGION_RQGEOM_WEAK_Y;
1198 region_rqgeom((WRegion*)cwin, &rq, NULL);
1201 if(cwin->flags&CLIENTWIN_NEED_CFGNTFY){
1202 sendconfig_clientwin(cwin);
1203 cwin->flags&=~CLIENTWIN_NEED_CFGNTFY;
1215 * Attempts to fix window size problems with non-ICCCM compliant
1219 void clientwin_nudge(WClientWin *cwin)
1221 XResizeWindow(ioncore_g.dpy, cwin->win,
1222 2*REGION_GEOM(cwin).w, 2*REGION_GEOM(cwin).h);
1223 XFlush(ioncore_g.dpy);
1224 XResizeWindow(ioncore_g.dpy, cwin->win,
1225 REGION_GEOM(cwin).w, REGION_GEOM(cwin).h);
1236 * Return the X window id for the client window.
1240 double clientwin_xid(WClientWin *cwin)
1252 static int last_checkcode=1;
1255 static ExtlTab clientwin_get_configuration(WClientWin *cwin)
1259 SMCfgCallback *cfg_cb;
1260 SMAddCallback *add_cb;
1262 tab=region_get_base_configuration((WRegion*)cwin);
1264 extl_table_sets_d(tab, "windowid", (double)(cwin->win));
1266 if(last_checkcode!=0){
1267 chkc=last_checkcode++;
1268 xwindow_set_integer_property(cwin->win, ioncore_g.atom_checkcode,
1270 extl_table_sets_i(tab, "checkcode", chkc);
1273 ioncore_get_sm_callbacks(&add_cb, &cfg_cb);
1282 WRegion *clientwin_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1286 int chkc=0, real_chkc=0;
1287 WClientWin *cwin=NULL;
1288 XWindowAttributes attr;
1292 if(!extl_table_gets_d(tab, "windowid", &wind) ||
1293 !extl_table_gets_i(tab, "checkcode", &chkc)){
1299 if(XWINDOW_REGION_OF(win)!=NULL){
1300 warn("Client window %x already managed.", win);
1304 got_chkc=xwindow_get_integer_property(win, ioncore_g.atom_checkcode,
1307 if(!got_chkc || real_chkc!=chkc){
1308 ioncore_clientwin_load_missing();
1314 if(!XGetWindowAttributes(ioncore_g.dpy, win, &attr)){
1315 warn(TR("Window %#x disappeared."), win);
1319 if(attr.override_redirect ||
1320 (ioncore_g.opmode==IONCORE_OPMODE_INIT && attr.map_state!=IsViewable)){
1321 warn(TR("Saved client window does not want to be managed."));
1329 attr.height=fp->g.h;
1332 cwin=create_clientwin(par, win, &attr);
1337 /* Reparent and resize taking limits set by size hints into account */
1338 convert_geom(fp, cwin, &rg);
1339 REGION_GEOM(cwin)=rg;
1340 do_reparent_clientwin(cwin, par->win, rg.x, rg.y);
1341 XResizeWindow(ioncore_g.dpy, win, maxof(1, rg.w), maxof(1, rg.h));
1343 if(!postmanage_check(cwin, &attr)){
1344 clientwin_destroyed(cwin);
1348 return (WRegion*)cwin;
1355 /*{{{ Dynfuntab and class info */
1358 static DynFunTab clientwin_dynfuntab[]={
1359 {(DynFun*)region_fitrep,
1360 (DynFun*)clientwin_fitrep},
1368 {region_do_set_focus,
1369 clientwin_do_set_focus},
1371 {region_notify_rootpos,
1372 clientwin_notify_rootpos},
1378 clientwin_stacking},
1380 {(DynFun*)region_xwindow,
1381 (DynFun*)clientwin_x_window},
1384 clientwin_activated},
1387 clientwin_size_hints},
1389 {(DynFun*)region_rqclose,
1390 (DynFun*)clientwin_rqclose},
1392 {(DynFun*)region_get_configuration,
1393 (DynFun*)clientwin_get_configuration},
1400 IMPLCLASS(WClientWin, WRegion, clientwin_deinit, clientwin_dynfuntab);