4 * Copyright (c) Tuomo Valkonen 1999-2001.
6 * See the included file LICENSE for details.
13 #include "clientwin.h"
24 #include "splitmisc.h"
28 static WThingFuntab clientwin_funtab
={
33 IMPLOBJ(WClientWin
, WThing
, &clientwin_funtab
)
35 static void set_clientwin_state(WClientWin
*cwin
, int state
);
36 static void send_clientmsg(Window win
, Atom a
);
39 /*{{{ Get properties */
42 void get_protocols(WClientWin
*cwin
)
44 Atom
*protocols
=NULL
, *p
;
47 cwin
->flags
&=~(CWIN_P_WM_DELETE
|CWIN_P_WM_TAKE_FOCUS
);
49 if(!XGetWMProtocols(wglobal
.dpy
, cwin
->win
, &protocols
, &n
))
52 for(p
=protocols
; n
; n
--, p
++){
53 if(*p
==wglobal
.atom_wm_delete
)
54 cwin
->flags
|=CWIN_P_WM_DELETE
;
55 else if(*p
==wglobal
.atom_wm_take_focus
)
56 cwin
->flags
|=CWIN_P_WM_TAKE_FOCUS
;
60 XFree((char*)protocols
);
64 static void set_winprops(WClientWin
*cwin
, const WWinProp
*winprop
)
66 cwin
->flags
|=winprop
->flags
;
68 if(cwin
->flags
&CWIN_PROP_MAXSIZE
){
69 cwin
->size_hints
.max_width
=winprop
->max_w
;
70 cwin
->size_hints
.max_height
=winprop
->max_h
;
71 cwin
->size_hints
.flags
|=PMaxSize
;
74 if(cwin
->flags
&CWIN_PROP_ASPECT
){
75 cwin
->size_hints
.min_aspect
.x
=winprop
->aspect_w
;
76 cwin
->size_hints
.max_aspect
.x
=winprop
->aspect_w
;
77 cwin
->size_hints
.min_aspect
.y
=winprop
->aspect_h
;
78 cwin
->size_hints
.max_aspect
.y
=winprop
->aspect_h
;
79 cwin
->size_hints
.flags
|=PAspect
;
87 /*{{{ Manage/create */
90 static void configure_cwin_bw(Window win
, int bw
)
93 ulong wcmask
=CWBorderWidth
;
96 XConfigureWindow(wglobal
.dpy
, win
, wcmask
, &wc
);
100 static bool init_clientwin(WClientWin
*cwin
, Window win
, int flags
,
101 const XWindowAttributes
*attr
, const WWinProp
*props
)
105 cwin
->orig_bw
=attr
->border_width
;
106 cwin
->geom
.h
=attr
->height
;
107 cwin
->geom
.w
=attr
->width
;
108 cwin
->cmap
=attr
->colormap
;
109 cwin
->name
=get_string_property(cwin
->win
, XA_WM_NAME
, NULL
);
112 cwin
->event_mask
=CLIENT_MASK
;
113 cwin
->transient_for
=None
;
114 cwin
->state
=WithdrawnState
;
117 set_winprops(cwin
, props
);
119 get_clientwin_size_hints(cwin
);
122 XSelectInput(wglobal
.dpy
, win
, cwin
->event_mask
);
124 XSaveContext(wglobal
.dpy
, win
, wglobal
.win_context
, (XPointer
)cwin
);
125 XAddToSaveSet(wglobal
.dpy
, win
);
128 configure_cwin_bw(win
, 0);
134 WClientWin
*create_clientwin(WScreen
*scr
,
135 Window win
, int flags
,
136 const XWindowAttributes
*attr
,
137 const WWinProp
*props
)
139 CREATETHING_IMPL(WClientWin
, clientwin
, scr
,
140 (p
, win
, flags
, attr
, props
));
144 static bool add_clientwin(WClientWin
*cwin
, int state
,
145 WWinProp
*props
, XWindowAttributes
*attr
)
148 Window win
=cwin
->win
;
149 WClient
*client
=NULL
;
153 #ifdef CF_SWITCH_NEW_CLIENTS
159 int transient_mode
=TRANSIENT_MODE_NORMAL
;
162 transient_mode
=props
->transient_mode
;
164 if(transient_mode
!=TRANSIENT_MODE_CURRENT
){
165 cwin
->transient_for
=None
;
166 if(XGetTransientForHint(wglobal
.dpy
, win
, &(cwin
->transient_for
))){
167 tfor
=find_clientwin(cwin
->transient_for
);
168 if(tfor
!=NULL
&& transient_mode
==TRANSIENT_MODE_NORMAL
){
169 if(CLIENTWIN_HAS_CLIENT(tfor
))
170 client
=CLIENTWIN_CLIENT(tfor
);
172 cwin
->transient_for
=None
;
175 }else{ /*TRANSIENT_MODE_CURRENT*/
176 tmp_wwin
=find_current(SCREEN_OF(cwin
)->current_workspace
);
177 if(WTHING_IS(tmp_wwin
, WFrame
)){
178 client
=((WFrame
*)tmp_wwin
)->current_client
;
179 cwin
->transient_for
=tmp_wwin
->win
;
183 get_integer_property(win
, wglobal
.atom_frame_id
, &frame_id
);
185 /* Get client to place this window in */
189 frame
=find_frame_by_id(frame_id
);
192 if(SCREEN_OF(frame
)!=SCREEN_OF(cwin
)){
193 warn("The frame id property of window %#x is set to"
194 "a frame on different screen", cwin
->win
);
201 ws
=SCREEN_OF(cwin
)->current_workspace
;
203 #ifdef CF_PLACEMENT_GEOM
204 geomset
=(cwin
->size_hints
.win_gravity
!=ForgetGravity
&&
205 (attr
->x
>CF_STUBBORN_TRESH
&&
206 attr
->y
>CF_STUBBORN_TRESH
));
207 if(ws
!=NULL
&& geomset
&& (!props
|| !props
->stubborn
))
208 frame
=find_frame_at(ws
, attr
->x
, attr
->y
);
215 frame
=(WFrame
*)find_current(ws
);
217 if(frame
==NULL
|| !WTHING_IS(frame
, WFrame
)){
218 warn("No client-supplied frame for window %d and no"
219 "current frame", win
);
224 /* Create the client, don't attach yet */
225 client
=create_client(SCREEN_OF(cwin
));
231 client_add_clientwin(client
, cwin
);
233 /* Attach, switch and focus */
235 /* frame!=NULL -> new client */
236 if(wglobal
.opmode
==OPMODE_INIT
)
237 switchto
=(state
!=IconicState
);
238 else if(props
!=NULL
&& props
->switchto
>=0)
239 switchto
=props
->switchto
;
241 frame_attach_client(frame
, client
, switchto
);
242 }else if(!CLIENT_HAS_FRAME(client
)){
243 hide_clientwin(cwin
);
250 WClientWin
* manage_clientwin(Window win
, int mflags
)
254 int state
=NormalState
;
255 XWindowAttributes attr
;
262 /* catch UnmapNotify and DestroyNotify */
263 XSelectInput(wglobal
.dpy
, win
, StructureNotifyMask
);
265 if(!XGetWindowAttributes(wglobal
.dpy
, win
, &attr
)){
266 warn("Window disappeared");
270 scr
=FIND_WINDOW_T(attr
.root
, WScreen
);
276 hints
=XGetWMHints(wglobal
.dpy
, win
);
278 if(hints
!=NULL
&& hints
->flags
&StateHint
)
279 state
=hints
->initial_state
;
281 if(!dock
&& state
==WithdrawnState
){
282 if(hints
->flags
&IconWindowHint
&& hints
->icon_window
!=None
){
283 /* The dockapp might be displaying its "main" window if no
284 * wm that understands dockapps has been managing it.
286 if(mflags
&MANAGE_INITIAL
)
287 XUnmapWindow(wglobal
.dpy
, win
);
289 XSelectInput(wglobal
.dpy
, win
, 0);
291 win
=hints
->icon_window
;
293 /* Is the icon window already being managed? */
294 cwin
=find_clientwin(win
);
296 if(WTHING_IS(cwin
, WDockwin
))
298 unmanage_clientwin(cwin
);
309 /* Get the actual state if any */
310 get_win_state(win
, &state
);
313 if(!dock
&& (attr
.override_redirect
||
314 (mflags
&MANAGE_INITIAL
&& attr
.map_state
!=IsViewable
)))
317 if(attr
.override_redirect
|| (mflags
&MANAGE_INITIAL
&&
318 attr
.map_state
!=IsViewable
))
322 if(state
!=NormalState
&& state
!=IconicState
)
326 props
=find_winprop_win(win
);
328 /* Allocate and initialize */
329 cwin
=create_clientwin(scr
, win
, 0, &attr
, props
);
336 winprop
=find_winprop_win(origwin
);
338 cwin
->dockpos
=winprop
->dockpos
;
340 if(!add_dockwin(cwin
))
345 CALL_ALT_B_ARG(b
, add_clientwin
, (cwin
, state
, props
, &attr
));
349 /*if(!add_clientwin(cwin, state, props, &attr))
353 /* Check that the window exists. The previous check selectinput do not
354 * seem to catch all cases of window destroyal.
356 XSync(wglobal
.dpy
, False
);
357 if(XGetWindowAttributes(wglobal
.dpy
, win
, &attr
))
360 warn("Window disappeared");
362 destroy_clientwin(cwin
);
366 unmap_clientwin(cwin
);
370 XSelectInput(wglobal
.dpy
, win
, 0);
378 /*{{{ Unmanage/destroy */
381 static void get_clientwin_rootpos(WClientWin
*cwin
, int *xret
, int *yret
)
388 void deinit_clientwin(WClientWin
*cwin
)
391 XWindowAttributes attr
;
394 XSelectInput(wglobal
.dpy
, cwin
->win
, 0);
396 get_clientwin_rootpos(cwin
, &x
, &y
);
397 /*XReparentWindow(wglobal.dpy, cwin->win, SCREEN->root.win, x, y);*/
398 if(XGetWindowAttributes(wglobal
.dpy
, cwin
->win
, &attr
))
399 XReparentWindow(wglobal
.dpy
, cwin
->win
, attr
.root
, x
, y
);
402 configure_cwin_bw(cwin
->win
, cwin
->orig_bw
);
404 XRemoveFromSaveSet(wglobal
.dpy
, cwin
->win
);
405 XDeleteContext(wglobal
.dpy
, cwin
->win
, wglobal
.win_context
);
407 if(wglobal
.opmode
==OPMODE_DEINIT
)
408 XMapWindow(wglobal
.dpy
, cwin
->win
);
410 XDeleteProperty(wglobal
.dpy
, cwin
->win
, wglobal
.atom_frame_id
);
414 XFree((void*)cwin
->name
);
418 /* Used when the the window is not to be managed anymore, but should
421 /*void unmanage_clientwin(WClientWin *cwin)
423 do_unmanage_clientwin(cwin, UNMANAGE);
427 /* Used when the window was unmapped */
428 void unmap_clientwin(WClientWin
*cwin
)
430 destroy_thing((WThing
*)cwin
);
434 /* Used when the window was deastroyed */
435 void destroy_clientwin(WClientWin
*cwin
)
437 XDeleteContext(wglobal
.dpy
, cwin
->win
, wglobal
.win_context
);
439 destroy_thing((WThing
*)cwin
);
449 void kill_clientwin(WClientWin
*cwin
)
451 XKillClient(wglobal
.dpy
, cwin
->win
);
455 void close_clientwin(WClientWin
*cwin
)
457 if(cwin
->flags
&CWIN_P_WM_DELETE
)
458 send_clientmsg(cwin
->win
, wglobal
.atom_wm_delete
);
464 /*{{{ State (hide/show) */
467 static void set_clientwin_state(WClientWin
*cwin
, int state
)
469 if(cwin
->state
!=state
){
471 set_win_state(cwin
->win
, state
);
476 void hide_clientwin(WClientWin
*cwin
)
481 if(cwin
->flags
&CWIN_KLUDGE_ACROBATIC
){
482 XMoveWindow(wglobal
.dpy
, cwin
->win
, -2*cwin
->geom
.w
, -2*cwin
->geom
.h
);
486 set_clientwin_state(cwin
, IconicState
);
487 XSelectInput(wglobal
.dpy
, cwin
->win
,
488 cwin
->event_mask
&~(StructureNotifyMask
|EnterWindowMask
));
489 XUnmapWindow(wglobal
.dpy
, cwin
->win
);
490 XSelectInput(wglobal
.dpy
, cwin
->win
, cwin
->event_mask
);
494 void show_clientwin(WClientWin
*cwin
)
501 /* The main window must be lowered for xprop/xwininfo et all
504 if(cwin
->transient_for
==None
){
505 XLowerWindow(wglobal
.dpy
, cwin
->win
);
507 XRaiseWindow(wglobal
.dpy
, cwin
->win
);
508 /*wc.stack_mode=Above;
509 wc.sibling=cwin->transient_for;
510 XConfigureWindow(wglobal.dpy, cwin->win, CWStackMode|CWSibling,
514 if(cwin
->flags
&CWIN_KLUDGE_ACROBATIC
){
515 XMoveWindow(wglobal
.dpy
, cwin
->win
, cwin
->geom
.x
, cwin
->geom
.y
);
516 if(cwin
->state
==NormalState
)
520 XSelectInput(wglobal
.dpy
, cwin
->win
,
521 cwin
->event_mask
&~(StructureNotifyMask
|EnterWindowMask
));
522 XMapWindow(wglobal
.dpy
, cwin
->win
);
523 /*if(cwin->state==WithdrawnState)
524 XResizeWindow(wglobal.dpy, cwin->win, cwin->geom.w, cwin->geom.h);*/
525 XSelectInput(wglobal
.dpy
, cwin
->win
, cwin
->event_mask
);
526 set_clientwin_state(cwin
, NormalState
);
530 void focus_clientwin(WClientWin
*cwin
)
532 set_input_focus(cwin
->win
);
534 if(cwin
->flags
&CWIN_P_WM_TAKE_FOCUS
)
535 send_clientmsg(cwin
->win
, wglobal
.atom_wm_take_focus
);
539 void iconify_clientwin(WClientWin
*cwin
)
541 #ifndef CF_IGNORE_ICONIFY_REQUEST
542 WFrame
*frame
=FIND_PARENT(cwin
, WFrame
);
545 frame_switch_next(frame
);
555 WClientWin
*find_clientwin(Window win
)
557 return FIND_WINDOW_T(win
, WClientWin
);
561 static void send_clientmsg(Window win
, Atom a
)
563 XClientMessageEvent ev
;
565 ev
.type
=ClientMessage
;
567 ev
.message_type
=wglobal
.atom_wm_protocols
;
570 ev
.data
.l
[1]=CurrentTime
;
572 XSendEvent(wglobal
.dpy
, win
, False
, 0L, (XEvent
*)&ev
);
576 void set_clientwin_name(WClientWin
*cwin
, char *p
)
578 WClient
*client
=FIND_PARENT(cwin
, WClient
);
586 if(FIRST_THING(client
, WClientWin
)!=cwin
)
591 client_unuse_label(client
);
599 client_use_label(client
);
606 /*{{{ Resize/reparent/reconf */
609 void reconf_clientwin(WClientWin
*cwin
, int rootx
, int rooty
)
621 /* Frame has a border (one drawn by X) set */
622 bdif
=-cwin
->orig_bw
+GRDATA_OF(cwin
)->spacing
;
627 ce
.xconfigure
.type
=ConfigureNotify
;
628 ce
.xconfigure
.event
=win
;
629 ce
.xconfigure
.window
=win
;
630 ce
.xconfigure
.x
=rootx
+bdif
;
631 ce
.xconfigure
.y
=rooty
+bdif
;
632 ce
.xconfigure
.width
=cwin
->geom
.w
;
633 ce
.xconfigure
.height
=cwin
->geom
.h
;
634 ce
.xconfigure
.border_width
=cwin
->orig_bw
;
635 ce
.xconfigure
.above
=None
;
636 ce
.xconfigure
.override_redirect
=False
;
638 XSelectInput(wglobal
.dpy
, win
, cwin
->event_mask
&~StructureNotifyMask
);
639 XSendEvent(wglobal
.dpy
, win
, False
, StructureNotifyMask
, &ce
);
640 XSelectInput(wglobal
.dpy
, win
, cwin
->event_mask
);
644 void sendconfig_clientwin(WClientWin
*cwin
)
646 WFrame
*frame
=FIND_PARENT(cwin
, WFrame
);
649 reconf_clientwin(cwin
, FRAME_X(frame
)+cwin
->geom
.x
,
650 FRAME_Y(frame
)+cwin
->geom
.y
);
655 void reparent_clientwin(WClientWin
*cwin
, Window win
, int x
, int y
)
657 XSelectInput(wglobal
.dpy
, cwin
->win
,
658 cwin
->event_mask
&~StructureNotifyMask
);
659 XReparentWindow(wglobal
.dpy
, cwin
->win
, win
, x
, y
);
660 XSelectInput(wglobal
.dpy
, cwin
->win
, cwin
->event_mask
);
664 static WRectangle
cwin_geom(WClientWin
*cwin
, bool bottom
,
665 WRectangle geom
, int w
, int h
)
669 r
.w
=(w
<geom
.w
? w
: geom
.w
);
670 r
.h
=(h
<geom
.h
? h
: geom
.h
);
672 correct_size(&(r
.w
), &(r
.h
), &(cwin
->size_hints
), FALSE
);
674 if(bottom
&& r
.h
>cwin
->geom
.h
)
677 r
.x
=geom
.x
+geom
.w
/2-r
.w
/2;
680 r
.y
=geom
.y
+geom
.h
-r
.h
;
682 r
.y
=geom
.y
+geom
.h
/2-r
.h
/2;
688 static WRectangle
cwin_frame_geom(WClientWin
*cwin
, WFrame
*frame
)
691 frame_client_geom(frame
, &geom
);
692 return cwin_geom(cwin
, cwin
->transient_for
!=None
, geom
, geom
.w
, geom
.h
);
696 void fit_clientwin_frame(WClientWin
*cwin
, WFrame
*frame
)
698 WRectangle geom
=cwin_frame_geom(cwin
, frame
);
700 XMoveResizeWindow(wglobal
.dpy
, cwin
->win
, geom
.x
, geom
.y
, geom
.w
, geom
.h
);
702 reconf_clientwin(cwin
, FRAME_X(frame
)+geom
.x
, FRAME_Y(frame
)+geom
.y
);
706 void reparent_fit_clientwin_frame(WClientWin
*cwin
, WFrame
*frame
)
708 WRectangle geom
=cwin_frame_geom(cwin
, frame
);
710 XResizeWindow(wglobal
.dpy
, cwin
->win
, geom
.w
, geom
.h
);
711 reparent_clientwin(cwin
, FRAME_WIN(frame
), geom
.x
, geom
.y
);
713 reconf_clientwin(cwin
, FRAME_X(frame
)+geom
.x
, FRAME_Y(frame
)+geom
.y
);
717 void reconf_clientwin_frame(WClientWin
*cwin
, WFrame
*frame
)
719 WRectangle geom
=cwin_frame_geom(cwin
, frame
);
720 reconf_clientwin(cwin
, FRAME_X(frame
)+geom
.x
, FRAME_Y(frame
)+geom
.y
);
724 void refit(WClientWin
*cwin
, int w
, int h
)
729 frame
=FIND_PARENT(cwin
, WFrame
);
734 frame_client_geom(frame
, &geom
);
736 #ifndef CF_KLUDGE_RESPECT_SHRINK
741 geom
=cwin_geom(cwin
, cwin
->transient_for
!=None
, geom
, w
, h
);
742 XMoveResizeWindow(wglobal
.dpy
, cwin
->win
, geom
.x
, geom
.y
, geom
.w
, geom
.h
);
744 reconf_clientwin(cwin
, FRAME_X(frame
)+geom
.x
, FRAME_Y(frame
)+geom
.y
);