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