1 // Generic window handling
2 // Copyright (C) 2000 Core Technologies.
3
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16
17 #include "includes.h"
18
19 static bool
20 hadInvalidation=false;
21
GetEditorDisplayDimensions(UINT32 * width,UINT32 * height)22 void GetEditorDisplayDimensions(UINT32 *width,UINT32 *height)
23 // Return the dimensions of the total display (across all screens).
24 // NOTE: this information is looked up each time it is needed. Although
25 // that's a little inefficient, it means that the display size can be adjusted
26 // and e93 will adapt on the fly.
27 {
28 XWindowAttributes
29 attributes;
30
31 XGetWindowAttributes(xDisplay,RootWindow(xDisplay,xScreenNum),&attributes);
32 *width=attributes.width;
33 *height=attributes.height;
34 }
35
GetEditorScreenOffsetAndDimensions(INT32 * xOffset,INT32 * yOffset,UINT32 * width,UINT32 * height)36 void GetEditorScreenOffsetAndDimensions(INT32 *xOffset,INT32 *yOffset,UINT32 *width,UINT32 *height)
37 // Return the offset and dimensions of the primary screen that the editor is
38 // running on.
39 // NOTE: On multi-head displays, it is possible that the primary screen
40 // (the one that e93 is opening windows onto) is not at offset 0,0 within
41 // the entire display.
42 // NOTE: this information is looked up each time it is needed. Although
43 // that's a little inefficient, it means that the screens can be adjusted
44 // and e93 will adapt on the fly.
45 {
46 XineramaScreenInfo
47 *screenInfo;
48 int
49 numHeads;
50 XWindowAttributes
51 attributes;
52
53 if((screenInfo=XineramaQueryScreens(xDisplay,&numHeads))) // success implies numHeads>0
54 {
55 // use the 0th display as the primary one
56 *xOffset=screenInfo[0].x_org;
57 *yOffset=screenInfo[0].y_org;
58 *width=screenInfo[0].width;
59 *height=screenInfo[0].height;
60 XFree(screenInfo);
61 }
62 else // single head
63 {
64 XGetWindowAttributes(xDisplay,RootWindow(xDisplay,xScreenNum),&attributes);
65 *xOffset=0;
66 *yOffset=0;
67 *width=attributes.width;
68 *height=attributes.height;
69 }
70 }
71
InvalidateWindowRect(EDITOR_WINDOW * window,EDITOR_RECT * rect)72 void InvalidateWindowRect(EDITOR_WINDOW *window,EDITOR_RECT *rect)
73 // Invalidate rect of window, so it is drawn the next time we redraw.
74 {
75 XRectangle
76 windowRect;
77
78 if(rect->w&&rect->h) // NOTE: this gets around a bug in X11/MOTIF (does not properly handle adding 0 width or height to a region!)
79 {
80 windowRect.x=rect->x;
81 windowRect.y=rect->y;
82 windowRect.width=rect->w;
83 windowRect.height=rect->h;
84 XUnionRectWithRegion(&windowRect,((WINDOW_LIST_ELEMENT *)(window->userData))->invalidRegion,((WINDOW_LIST_ELEMENT *)(window->userData))->invalidRegion); // add to invalid region of the window
85 hadInvalidation=true;
86 }
87 }
88
InvalidateWindowRegion(EDITOR_WINDOW * window,Region region)89 void InvalidateWindowRegion(EDITOR_WINDOW *window,Region region)
90 // Invalidate region of window, so it is drawn the next time we redraw.
91 {
92 XUnionRegion(region,((WINDOW_LIST_ELEMENT *)window->userData)->invalidRegion,((WINDOW_LIST_ELEMENT *)window->userData)->invalidRegion); // make this part invalid
93 hadInvalidation=true;
94 }
95
RedrawInvalidWindows()96 void RedrawInvalidWindows()
97 // Run though the window list, and redraw anything that
98 // has invalid area.
99 // NOTE: for efficiency, this checks a global which tells
100 // if any invalidations have happened, and if not, it does nothing.
101 {
102 WINDOW_LIST_ELEMENT
103 *element;
104
105 if(hadInvalidation)
106 {
107 hadInvalidation=false; // clear now, so updates can cause further invalidation (not that they should want to, but just to be correct)
108 element=windowListHead; // update all windows that need it
109 while(element)
110 {
111 switch(element->editorWindow->windowType)
112 {
113 case EWT_DOCUMENT:
114 UpdateDocumentWindow(element->editorWindow);
115 break;
116 case EWT_DIALOG:
117 UpdateDialogWindow(element->editorWindow);
118 break;
119 case EWT_MENU:
120 UpdateMenuWindow(element->editorWindow);
121 break;
122 default:
123 break;
124 }
125 element=element->nextElement;
126 }
127 }
128 }
129
SetClassHints(EDITOR_WINDOW * window)130 void SetClassHints(EDITOR_WINDOW *window)
131 // Set the class hints for this window.
132 // Used to create a class of windows so that window list applications can group things properly.
133 {
134 XClassHint
135 classHints;
136 WINDOW_LIST_ELEMENT
137 *windowElement;
138 char
139 uniqueClass[64];
140
141 windowElement=(WINDOW_LIST_ELEMENT *)window->userData;
142
143 classHints.res_name=(char *)"e93";
144 sprintf(uniqueClass,"e93 (%d)",getpid());
145 classHints.res_class=uniqueClass;
146 XSetClassHint(xDisplay,windowElement->xWindow,&classHints);
147 }
148
MapEventPredicate(Display * display,XEvent * event,char * arg)149 static Bool MapEventPredicate(Display *display,XEvent *event,char *arg)
150 // Use this to locate the mapping event for the given window.
151 {
152 if((event->type==MapNotify)&&(event->xmap.window==*((Window *)arg)))
153 {
154 return(True);
155 }
156 return(False);
157 }
158
FocusEventPredicate(Display * display,XEvent * event,char * arg)159 static Bool FocusEventPredicate(Display *display,XEvent *event,char *arg)
160 // Use this to locate the focus event for the given window.
161 {
162 if((event->type==FocusIn)&&(event->xfocus.window==*((Window *)arg)))
163 {
164 return(True);
165 }
166 return(False);
167 }
168
DestroyEventPredicate(Display * display,XEvent * event,char * arg)169 static Bool DestroyEventPredicate(Display *display,XEvent *event,char *arg)
170 // Use this to locate the destroy event for the given window (actually, this
171 // gets ANY event for the window -- that way, no stray events are left after
172 // the window is gone).
173 {
174 if(event->xmap.window==*((Window *)arg))
175 {
176 return(True);
177 }
178 return(False);
179 }
180
WaitUntilMappedNoFocus(EDITOR_WINDOW * window)181 void WaitUntilMappedNoFocus(EDITOR_WINDOW *window)
182 // Wait until a mapping event for window arrives.
183 {
184 XEvent
185 event;
186 WINDOW_LIST_ELEMENT
187 *windowElement;
188
189 windowElement=(WINDOW_LIST_ELEMENT *)window->userData;
190 XIfEvent(xDisplay,&event,MapEventPredicate,(char *)&windowElement->xWindow); // wait here until our window is mapped
191 }
192
WaitUntilMapped(EDITOR_WINDOW * window)193 void WaitUntilMapped(EDITOR_WINDOW *window)
194 // Wait until a mapping event for window arrives, then set the focus to that window, and
195 // wait for it to complete.
196 {
197 XEvent
198 event;
199 WINDOW_LIST_ELEMENT
200 *windowElement;
201
202 windowElement=(WINDOW_LIST_ELEMENT *)window->userData;
203 XIfEvent(xDisplay,&event,MapEventPredicate,(char *)&windowElement->xWindow); // wait here until our window is mapped
204 XSetInputFocus(xDisplay,windowElement->xWindow,RevertToPointerRoot,CurrentTime);
205 XIfEvent(xDisplay,&event,FocusEventPredicate,(char *)&windowElement->xWindow); // wait here until our window has focus
206 }
207
WaitUntilDestroyed(EDITOR_WINDOW * window)208 void WaitUntilDestroyed(EDITOR_WINDOW *window)
209 // Wait until a the destroy event for window arrives, eat all other events arriving for the window
210 // while we wait.
211 {
212 XEvent
213 event;
214 WINDOW_LIST_ELEMENT
215 *windowElement;
216
217 windowElement=(WINDOW_LIST_ELEMENT *)window->userData;
218 do
219 {
220 XIfEvent(xDisplay,&event,DestroyEventPredicate,(char *)&windowElement->xWindow); // wait here until our window is destroyed
221 }
222 while(event.type!=DestroyNotify);
223 }
224
LinkWindowElement(WINDOW_LIST_ELEMENT * element)225 void LinkWindowElement(WINDOW_LIST_ELEMENT *element)
226 // Passed a WINDOW_LIST_ELEMENT, link it to the start of the global list.
227 {
228 if(windowListHead) // point previous back to new
229 {
230 windowListHead->previousElement=element;
231 }
232 element->nextElement=windowListHead; // point to the old head of the list
233 element->previousElement=NULL; // no previous element
234 windowListHead=element; // point the head at the new element
235 }
236
UnlinkWindowElement(WINDOW_LIST_ELEMENT * element)237 void UnlinkWindowElement(WINDOW_LIST_ELEMENT *element)
238 // Passed a WINDOW_LIST_ELEMENT, unlink it from the global list.
239 {
240 if(element->nextElement)
241 {
242 element->nextElement->previousElement=element->previousElement;
243 }
244 if(element->previousElement)
245 {
246 element->previousElement->nextElement=element->nextElement;
247 }
248 else
249 {
250 windowListHead=element->nextElement; // this one was at the head, so update
251 }
252 }
253
CheckChildMatch(Window testWindow,unsigned int numChildren,Window * children)254 static bool CheckChildMatch(Window testWindow,unsigned int numChildren,Window *children)
255 // See if testWindow matches any of the children in the passed list.
256 // Return true if so, false if not.
257 {
258 unsigned int
259 i;
260
261 for(i=0;i<numChildren;i++)
262 {
263 if(testWindow==children[i])
264 {
265 return(true);
266 }
267 }
268 return(false);
269 }
270
LocateWindowElement(Window xWindow)271 WINDOW_LIST_ELEMENT *LocateWindowElement(Window xWindow)
272 // Passed an xWindow which is a client window (potentially one of ours),
273 // locate the editor window element associated with it (if there is one).
274 // NOTE: this expects that the window is a *client* window (not a window
275 // made by the WM as decoration).
276 {
277 WINDOW_LIST_ELEMENT
278 *element;
279 bool
280 found;
281
282 element=windowListHead;
283 found=false;
284 while(element&&!found)
285 {
286 if(element->xWindow==xWindow)
287 {
288 found=true;
289 }
290 else
291 {
292 element=element->nextElement;
293 }
294 }
295 return(element);
296 }
297
LocateArbitraryWindowElement(Window xWindow)298 static WINDOW_LIST_ELEMENT *LocateArbitraryWindowElement(Window xWindow)
299 // Passed an arbitrary xWindow, see if it contains one of our client windows.
300 // NOTE: since windows can be reparented by the WM (and changed on the fly),
301 // this must look through the list of children of the given X window for the editor window.
302 {
303 Window
304 root,
305 parent,
306 *children;
307 unsigned int
308 numChildren;
309 WINDOW_LIST_ELEMENT
310 *element;
311 bool
312 found;
313
314 element=LocateWindowElement(xWindow); // start by just seeing if it is a client window
315
316 if(!element) // do it the hard way by looking through the children
317 {
318 if(XQueryTree(xDisplay,xWindow,&root,&parent,&children,&numChildren)) // get the children of this window (window could be going away, so we must allow this to fail)
319 {
320 element=windowListHead;
321 found=false;
322 while(element&&!found)
323 {
324 if(CheckChildMatch(element->xWindow,numChildren,children))
325 {
326 found=true;
327 }
328 else
329 {
330 element=element->nextElement;
331 }
332 }
333 if(children)
334 {
335 XFree(children);
336 }
337 }
338 }
339 return(element);
340 }
341
MinimizeEditorWindow(EDITOR_WINDOW * window)342 void MinimizeEditorWindow(EDITOR_WINDOW *window)
343 // Iconify the passed window.
344 {
345 WINDOW_LIST_ELEMENT
346 *windowElement;
347
348 windowElement=(WINDOW_LIST_ELEMENT *)window->userData; // get the x window information associated with this document window
349 XIconifyWindow(xDisplay,windowElement->xWindow,xScreenNum);
350 }
351
UnminimizeEditorWindow(EDITOR_WINDOW * window)352 void UnminimizeEditorWindow(EDITOR_WINDOW *window)
353 // Undo what MinimizeEditorWindow did.
354 {
355 WINDOW_LIST_ELEMENT
356 *windowElement;
357
358 windowElement=(WINDOW_LIST_ELEMENT *)window->userData; // get the x window information associated with this document window
359 XMapWindow(xDisplay,windowElement->xWindow);
360 }
361
GetTopEditorWindow()362 EDITOR_WINDOW *GetTopEditorWindow()
363 // Return the top-most editor window, or NULL if there is none.
364 {
365 WINDOW_LIST_ELEMENT
366 *element;
367 EDITOR_WINDOW
368 *window;
369 Window
370 root,
371 parent,
372 *children;
373 unsigned int
374 child,
375 numChildren;
376
377 XQueryTree(xDisplay,RootWindow(xDisplay,xScreenNum),&root,&parent,&children,&numChildren); // get all the children of the root (in order)
378 window=NULL;
379 for(child=numChildren;!window&&(child>0);child--)
380 {
381 if((element=LocateArbitraryWindowElement(children[child-1]))) // see if this window contains an editor window
382 {
383 window=element->editorWindow;
384 }
385 }
386 if(children)
387 {
388 XFree((char *)children);
389 }
390 return(window);
391 }
392
SetTopEditorWindow(EDITOR_WINDOW * window)393 void SetTopEditorWindow(EDITOR_WINDOW *window)
394 // Set the passed document window to the top of the display
395 // NOTE: X windows has evolved since e93 was written. It has become even
396 // more needlessly complex, and now window managers don't always honor
397 // XRaiseWindow requests (Metacity, I'm looking at you).
398 // The Window Manager authors apparently feel it is the duty of the WM to
399 // police which applications can raise windows and when.
400 // To me, this is nonsense. When I call XRaiseWindow, it should raise
401 // the damn window.
402 // Now however, to try to convince these misguided WMs, e93 attempts to follow:
403 // http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#id2506353
404 // by sending a _NET_ACTIVE_WINDOW message to the root window.
405 // If it works, the effect will be the same as having called XRaiseWindow --
406 // just more complex and difficult to follow.
407 {
408 WINDOW_LIST_ELEMENT
409 *windowElement;
410 XEvent
411 xEvent;
412
413 windowElement=(WINDOW_LIST_ELEMENT *)window->userData; // get the x window information associated with this document window
414 XMapWindow(xDisplay,windowElement->xWindow); // make sure it's not iconified -- Thanks Michael Manning
415 XRaiseWindow(xDisplay,windowElement->xWindow); // place it on top
416 XSetInputFocus(xDisplay,windowElement->xWindow,RevertToPointerRoot,CurrentTime); // give it the focus
417
418 xEvent.xclient.type=ClientMessage;
419 xEvent.xclient.serial=0;
420 xEvent.xclient.send_event=True;
421 xEvent.xclient.display=xDisplay;
422 xEvent.xclient.window=windowElement->xWindow;
423 xEvent.xclient.message_type=XInternAtom(xDisplay,"_NET_ACTIVE_WINDOW",True);
424 xEvent.xclient.format=32;
425 xEvent.xclient.data.l[0]=1; // 1 tells the WM we're an application
426 xEvent.xclient.data.l[1]=CurrentTime; // for now, send the timestamp as CurrentTime, later if it matters, figure out what we really need
427 xEvent.xclient.data.l[2]=0; // for now, send the currently active window as 0, later if it matters, figure out what we really need
428 xEvent.xclient.data.l[3]=0;
429 xEvent.xclient.data.l[4]=0;
430 XSendEvent(xDisplay,XDefaultRootWindow(xDisplay),False,SubstructureRedirectMask|SubstructureNotifyMask,&xEvent);
431 }
432
GetTopEditorWindowType(UINT16 windowType)433 EDITOR_WINDOW *GetTopEditorWindowType(UINT16 windowType)
434 // Return the top-most window of windowType, or NULL if there is none.
435 {
436 WINDOW_LIST_ELEMENT
437 *element;
438 EDITOR_WINDOW
439 *window;
440 Window
441 root,
442 parent,
443 *children;
444 unsigned int
445 child,
446 numChildren;
447
448 XQueryTree(xDisplay,RootWindow(xDisplay,xScreenNum),&root,&parent,&children,&numChildren); // get all the children of the root (in order) (which may reparent editor windows)
449 window=NULL;
450 for(child=numChildren;!window&&(child>0);child--)
451 {
452 if((element=LocateArbitraryWindowElement(children[child-1]))) // see if this window contains an editor window
453 {
454 if(element->editorWindow->windowType==windowType)
455 {
456 window=element->editorWindow;
457 }
458 }
459 }
460 if(children)
461 {
462 XFree((char *)children);
463 }
464 return(window);
465 }
466
GetSortedEditorWindowTypeList(UINT16 windowType,UINT32 * numElements,EDITOR_WINDOW *** list)467 bool GetSortedEditorWindowTypeList(UINT16 windowType,UINT32 *numElements,EDITOR_WINDOW ***list)
468 // Return a list of window pointers, sorted top to bottom for the given type.
469 // If there is a problem, SetError, and return false.
470 // If true is returned, then list must be MDisposePtr'ed later (even if numElements
471 // is 0).
472 {
473 WINDOW_LIST_ELEMENT
474 *element;
475 Window
476 root,
477 parent,
478 *children;
479 unsigned int
480 child,
481 numChildren;
482 bool
483 fail;
484
485 fail=false;
486 *numElements=0; // no elements yet
487 XQueryTree(xDisplay,RootWindow(xDisplay,xScreenNum),&root,&parent,&children,&numChildren);
488 if(((*list)=(EDITOR_WINDOW **)MNewPtr(sizeof(EDITOR_WINDOW *)*numChildren))) // create a list of pointers large enough to hold the maximum number of windows located
489 {
490 for(child=numChildren;child>0;child--)
491 {
492 if((element=LocateArbitraryWindowElement(children[child-1]))) // hunt down the window based on this
493 {
494 if(element->editorWindow->windowType==windowType) // see if it belongs on the list
495 {
496 (*list)[(*numElements)++]=element->editorWindow; // assign it to the list
497 }
498 }
499 }
500 }
501 else
502 {
503 fail=true;
504 }
505 if(children)
506 {
507 XFree((char *)children);
508 }
509 return(!fail);
510 }
511
GetEditorFocusWindow()512 EDITOR_WINDOW *GetEditorFocusWindow()
513 // Return the editor window with the focus, or the top window
514 // if none has focus.
515 // If there is no top window, return NULL.
516 {
517 EDITOR_WINDOW
518 *window;
519 WINDOW_LIST_ELEMENT
520 *windowElement;
521 Window
522 xWindow;
523 int
524 focusRevert;
525
526 XGetInputFocus(xDisplay,&xWindow,&focusRevert); // find out who has the focus
527 if((windowElement=LocateWindowElement(xWindow))) // see if it is one of ours
528 {
529 if((windowElement->editorWindow->windowType==EWT_DOCUMENT)||(windowElement->editorWindow->windowType==EWT_DIALOG))
530 {
531 return(windowElement->editorWindow); // if there is one of our documents/dialogs with focus, it is preferred
532 }
533 else
534 {
535 if((window=GetTopEditorWindowType(EWT_DIALOG))) // try to find the top-most dialog
536 {
537 return(window);
538 }
539 else
540 {
541 if((window=GetTopEditorWindowType(EWT_DOCUMENT))) // try to find the top-most document
542 {
543 return(window);
544 }
545 else
546 {
547 return(windowElement->editorWindow); // no document/dialog, so give back whatever it was we found in the first place
548 }
549 }
550 }
551 }
552 if((window=GetTopEditorWindowType(EWT_DIALOG))) // if focus window was not ours try to find the top-most dialog
553 {
554 return(window);
555 }
556 else
557 {
558 if((window=GetTopEditorWindowType(EWT_DOCUMENT))) // try to find the top-most document
559 {
560 return(window);
561 }
562 }
563 return(rootMenuWindow); // as a last resort, give back the root menu
564 }
565
FocusAwayFrom(EDITOR_WINDOW * window)566 void FocusAwayFrom(EDITOR_WINDOW *window)
567 // Set the focus to the topmost window that is not window.
568 // This is done to fool X into not readjusting the focus when we
569 // delete window.
570 {
571 WINDOW_LIST_ELEMENT
572 *element;
573 bool
574 haveFocus;
575 Window
576 focusWindow;
577 Window
578 root,
579 parent,
580 *children;
581 unsigned int
582 child,
583 numChildren;
584
585 XQueryTree(xDisplay,RootWindow(xDisplay,xScreenNum),&root,&parent,&children,&numChildren);
586 haveFocus=false;
587 focusWindow=0; // stop compiler from complaining
588 for(child=numChildren;!haveFocus&&(child>0);child--)
589 {
590 if((element=LocateArbitraryWindowElement(children[child-1])))
591 {
592 if(element->editorWindow!=window)
593 {
594 focusWindow=element->xWindow;
595 haveFocus=true;
596 }
597 }
598 }
599 if(children)
600 {
601 XFree((char *)children);
602 }
603 if(haveFocus)
604 {
605 XSetInputFocus(xDisplay,focusWindow,RevertToPointerRoot,CurrentTime); // give focus to this window
606 XSync(xDisplay,false);
607 }
608 }
609