Renamed package to ion1, and made it conflict with package 'ion'.
[ion1.git] / src / clientwin.c
blob31af600c9f396f46d7643861907216da19294730
1 /*
2 * ion/clientwin.c
4 * Copyright (c) Tuomo Valkonen 1999-2001.
5 *
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
10 #include <limits.h>
12 #include "common.h"
13 #include "clientwin.h"
14 #include "client.h"
15 #include "frame.h"
16 #include "frameid.h"
17 #include "property.h"
18 #include "event.h"
19 #include "focus.h"
20 #include "winprops.h"
21 #include "sizehint.h"
22 #include "global.h"
23 #include "thingp.h"
24 #include "splitmisc.h"
25 #include "modules.h"
28 static WThingFuntab clientwin_funtab={
29 deinit_clientwin,
30 NULL
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;
45 int n;
47 cwin->flags&=~(CWIN_P_WM_DELETE|CWIN_P_WM_TAKE_FOCUS);
49 if(!XGetWMProtocols(wglobal.dpy, cwin->win, &protocols, &n))
50 return;
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;
59 if(protocols!=NULL)
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;
84 /*}}}*/
87 /*{{{ Manage/create */
90 static void configure_cwin_bw(Window win, int bw)
92 XWindowChanges wc;
93 ulong wcmask=CWBorderWidth;
95 wc.border_width=bw;
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)
103 cwin->flags=flags;
104 cwin->win=win;
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);
110 if(cwin->name!=NULL)
111 stripws(cwin->name);
112 cwin->event_mask=CLIENT_MASK;
113 cwin->transient_for=None;
114 cwin->state=WithdrawnState;
116 if(props!=NULL)
117 set_winprops(cwin, props);
119 get_clientwin_size_hints(cwin);
120 get_protocols(cwin);
122 XSelectInput(wglobal.dpy, win, cwin->event_mask);
124 XSaveContext(wglobal.dpy, win, wglobal.win_context, (XPointer)cwin);
125 XAddToSaveSet(wglobal.dpy, win);
127 if(cwin->orig_bw!=0)
128 configure_cwin_bw(win, 0);
130 return TRUE;
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)
147 int frame_id=0;
148 Window win=cwin->win;
149 WClient *client=NULL;
150 WClientWin *tfor;
151 WFrame *frame=NULL;
152 WWorkspace *ws=NULL;
153 #ifdef CF_SWITCH_NEW_CLIENTS
154 bool switchto=TRUE;
155 #else
156 bool switchto=FALSE;
157 #endif
158 WWindow *tmp_wwin;
159 int transient_mode=TRANSIENT_MODE_NORMAL;
161 if(props!=NULL)
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);
171 }else{
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 */
186 if(client==NULL){
187 /* Frame first */
188 if(frame_id!=0)
189 frame=find_frame_by_id(frame_id);
191 if(frame!=NULL){
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);
195 frame=NULL;
199 if(frame==NULL){
200 bool geomset;
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);
210 if(frame==NULL){
211 #else
213 #endif
214 if(ws!=NULL)
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);
220 return FALSE;
224 /* Create the client, don't attach yet */
225 client=create_client(SCREEN_OF(cwin));
227 if(client==NULL)
228 return FALSE;
231 client_add_clientwin(client, cwin);
233 /* Attach, switch and focus */
234 if(frame!=NULL){
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);
246 return TRUE;
250 WClientWin* manage_clientwin(Window win, int mflags)
252 WScreen *scr;
253 WClientWin *cwin;
254 int state=NormalState;
255 XWindowAttributes attr;
256 WWinProp *props;
257 bool b;
258 /*XWMHints *hints;*/
259 /*bool dock=FALSE;*/
261 again:
262 /* catch UnmapNotify and DestroyNotify */
263 XSelectInput(wglobal.dpy, win, StructureNotifyMask);
265 if(!XGetWindowAttributes(wglobal.dpy, win, &attr)){
266 warn("Window disappeared");
267 goto fail2;
270 scr=FIND_WINDOW_T(attr.root, WScreen);
272 if(scr==NULL)
273 return NULL;
275 #if 0
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);
295 if(cwin!=NULL){
296 if(WTHING_IS(cwin, WDockwin))
297 return cwin;
298 unmanage_clientwin(cwin);
301 dock=TRUE;
302 goto again;
305 if(hints!=NULL)
306 XFree((void*)hints);
307 #endif
309 /* Get the actual state if any */
310 get_win_state(win, &state);
312 #if 0
313 if(!dock && (attr.override_redirect ||
314 (mflags&MANAGE_INITIAL && attr.map_state!=IsViewable)))
315 goto fail2;
316 #else
317 if(attr.override_redirect || (mflags&MANAGE_INITIAL &&
318 attr.map_state!=IsViewable))
319 goto fail2;
320 #endif
322 if(state!=NormalState && state!=IconicState)
323 state=NormalState;
325 /* Get winprops */
326 props=find_winprop_win(win);
328 /* Allocate and initialize */
329 cwin=create_clientwin(scr, win, 0, &attr, props);
331 if(cwin==NULL)
332 goto fail2;
334 #if 0
335 if(dock){
336 winprop=find_winprop_win(origwin);
337 if(winprop!=NULL)
338 cwin->dockpos=winprop->dockpos;
340 if(!add_dockwin(cwin))
341 goto failure;
342 }else
343 #endif
345 CALL_ALT_B_ARG(b, add_clientwin, (cwin, state, props, &attr));
346 if(!b)
347 goto failure;
349 /*if(!add_clientwin(cwin, state, props, &attr))
350 goto failure;*/
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))
358 return cwin;
360 warn("Window disappeared");
362 destroy_clientwin(cwin);
363 return NULL;
365 failure:
366 unmap_clientwin(cwin);
367 return NULL;
369 fail2:
370 XSelectInput(wglobal.dpy, win, 0);
371 return NULL;
375 /*}}}*/
378 /*{{{ Unmanage/destroy */
381 static void get_clientwin_rootpos(WClientWin *cwin, int *xret, int *yret)
383 *xret=0;
384 *yret=0;
388 void deinit_clientwin(WClientWin *cwin)
390 int x, y;
391 XWindowAttributes attr;
393 if(cwin->win!=None){
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);
401 if(cwin->orig_bw!=0)
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);
409 else
410 XDeleteProperty(wglobal.dpy, cwin->win, wglobal.atom_frame_id);
413 if(cwin->name!=NULL)
414 XFree((void*)cwin->name);
418 /* Used when the the window is not to be managed anymore, but should
419 * be mapped (deinit)
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);
438 cwin->win=None;
439 destroy_thing((WThing*)cwin);
443 /*}}}*/
446 /*{{{ Kill/close */
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);
461 /*}}}*/
464 /*{{{ State (hide/show) */
467 static void set_clientwin_state(WClientWin *cwin, int state)
469 if(cwin->state!=state){
470 cwin->state=state;
471 set_win_state(cwin->win, state);
476 void hide_clientwin(WClientWin *cwin)
478 if(cwin==NULL)
479 return;
481 if(cwin->flags&CWIN_KLUDGE_ACROBATIC){
482 XMoveWindow(wglobal.dpy, cwin->win, -2*cwin->geom.w, -2*cwin->geom.h);
483 return;
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)
496 XWindowChanges wc;
498 if(cwin==NULL)
499 return;
501 /* The main window must be lowered for xprop/xwininfo et all
502 * to work.
504 if(cwin->transient_for==None){
505 XLowerWindow(wglobal.dpy, cwin->win);
506 }else{
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,
511 &wc);*/
514 if(cwin->flags&CWIN_KLUDGE_ACROBATIC){
515 XMoveWindow(wglobal.dpy, cwin->win, cwin->geom.x, cwin->geom.y);
516 if(cwin->state==NormalState)
517 return;
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);
544 if(frame!=NULL)
545 frame_switch_next(frame);
546 #endif
549 /*}}}*/
552 /*{{{ Misc */
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;
566 ev.window=win;
567 ev.message_type=wglobal.atom_wm_protocols;
568 ev.format=32;
569 ev.data.l[0]=a;
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);
580 if(p==NULL)
581 return;
583 stripws(p);
585 if(client!=NULL){
586 if(FIRST_THING(client, WClientWin)!=cwin)
587 client=NULL;
590 if(client!=NULL)
591 client_unuse_label(client);
593 if(cwin->name!=NULL)
594 XFree(cwin->name);
596 cwin->name=p;
598 if(client!=NULL)
599 client_use_label(client);
603 /*}}}*/
606 /*{{{ Resize/reparent/reconf */
609 void reconf_clientwin(WClientWin *cwin, int rootx, int rooty)
611 XEvent ce;
612 Window win;
613 int bdif;
615 if(cwin==NULL)
616 return;
618 win=cwin->win;
620 #if 1
621 /* Frame has a border (one drawn by X) set */
622 bdif=-cwin->orig_bw+GRDATA_OF(cwin)->spacing;
623 #else
624 bdif=-cwin->orig_bw;
625 #endif
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);
648 if(frame!=NULL){
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)
667 WRectangle r;
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)
675 r.h=cwin->geom.h;
677 r.x=geom.x+geom.w/2-r.w/2;
679 if(bottom)
680 r.y=geom.y+geom.h-r.h;
681 else
682 r.y=geom.y+geom.h/2-r.h/2;
684 return r;
688 static WRectangle cwin_frame_geom(WClientWin *cwin, WFrame *frame)
690 WRectangle geom;
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);
701 cwin->geom=geom;
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);
712 cwin->geom=geom;
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)
726 WFrame *frame;
727 WRectangle geom;
729 frame=FIND_PARENT(cwin, WFrame);
731 if(frame==NULL)
732 return;
734 frame_client_geom(frame, &geom);
736 #ifndef CF_KLUDGE_RESPECT_SHRINK
737 w=geom.w;
738 h=geom.h;
739 #endif
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);
743 cwin->geom=geom;
744 reconf_clientwin(cwin, FRAME_X(frame)+geom.x, FRAME_Y(frame)+geom.y);
748 /*}}}*/