8 #include <FL/fl_draw.H>
11 static Atom wm_state
= 0;
12 static Atom net_wm_state
= 0;
13 static Atom wm_maximize_vert
;
14 static Atom wm_maximize_horz
;
15 static Atom wm_change_state
;
16 static Atom wm_protocols
;
17 static Atom wm_delete_window
;
18 static Atom wm_take_focus
;
19 static Atom wm_save_yourself
;
20 static Atom wm_colormap_windows
;
21 static Atom _motif_wm_hints
;
22 static Atom kwm_win_decoration
;
24 static Atom kwm_win_desktop
;
25 static Atom kwm_win_sticky
;
27 //static Atom wm_client_leader;
28 static Atom _wm_quit_app
;
30 // these are set by initialize in main.C:
34 extern Atom _win_workspace
;
38 extern char clock_buf
[];
39 extern int clock_alarm_on
;
42 static const int XEventMask
=
43 ExposureMask
|StructureNotifyMask
44 |KeyPressMask
|KeyReleaseMask
|KeymapStateMask
|FocusChangeMask
45 |ButtonPressMask
|ButtonReleaseMask
46 |EnterWindowMask
|LeaveWindowMask
47 |PointerMotionMask
|SubstructureRedirectMask
|SubstructureNotifyMask
;
49 extern Fl_Window
* Root
;
51 Frame
* Frame::active_
;
54 static inline int max(int a
, int b
) {return a
> b
? a
: b
;}
55 static inline int min(int a
, int b
) {return a
< b
? a
: b
;}
57 ////////////////////////////////////////////////////////////////
58 // The constructor is by far the most complex part, as it collects
59 // all the scattered pieces of information about the window that
60 // X has and uses them to initialize the structure, position the
61 // window, and then finally create it.
63 int dont_set_event_mask
= 0; // used by FrameWindow
65 // "existing" is a pointer to an XWindowAttributes structure that is
66 // passed for an already-existing window when the window manager is
67 // starting up. If so we don't want to alter the state, size, or
68 // position. If null than this is a MapRequest of a new window.
69 Frame::Frame(Window window
, XWindowAttributes
* existing
) :
74 transient_for_xid(None
),
78 close_button(BUTTON_LEFT
,BUTTON_TOP
,BUTTON_W
,BUTTON_H
,"X"),
79 iconize_button(BUTTON_LEFT
,BUTTON_TOP
+BUTTON_H
,BUTTON_W
,BUTTON_H
,"i"),
80 max_w_button(BUTTON_LEFT
,BUTTON_TOP
+2*BUTTON_H
,BUTTON_W
,BUTTON_H
,"w"),
81 min_w_button(BUTTON_LEFT
,BUTTON_TOP
+3*BUTTON_H
,BUTTON_W
,BUTTON_H
,"W")
83 close_button
.callback(button_cb_static
);
84 iconize_button
.callback(button_cb_static
);
85 max_w_button
.type(FL_TOGGLE_BUTTON
);
86 max_w_button
.callback(button_cb_static
);
87 min_w_button
.type(FL_TOGGLE_BUTTON
);
88 min_w_button
.callback(button_cb_static
);
90 box(FL_NO_BOX
); // relies on background color erasing interior
94 // do this asap so we don't miss any events...
95 if (!dont_set_event_mask
)
96 XSelectInput(fl_display
, window_
,
97 ColormapChangeMask
| PropertyChangeMask
| FocusChangeMask
101 // allocate all the atoms if this is the first time
102 wm_state
= XInternAtom(fl_display
, "WM_STATE", 0);
103 net_wm_state
= XInternAtom(fl_display
, "_NET_WM_STATE", 0);
104 wm_maximize_vert
= XInternAtom(fl_display
, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
105 wm_maximize_horz
= XInternAtom(fl_display
, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
106 wm_change_state
= XInternAtom(fl_display
, "WM_CHANGE_STATE", 0);
107 wm_protocols
= XInternAtom(fl_display
, "WM_PROTOCOLS", 0);
108 wm_delete_window
= XInternAtom(fl_display
, "WM_DELETE_WINDOW", 0);
109 wm_take_focus
= XInternAtom(fl_display
, "WM_TAKE_FOCUS", 0);
110 wm_save_yourself
= XInternAtom(fl_display
, "WM_SAVE_YOURSELF", 0);
111 wm_colormap_windows
= XInternAtom(fl_display
, "WM_COLORMAP_WINDOWS",0);
112 _motif_wm_hints
= XInternAtom(fl_display
, "_MOTIF_WM_HINTS", 0);
113 kwm_win_decoration
= XInternAtom(fl_display
, "KWM_WIN_DECORATION", 0);
115 kwm_win_desktop
= XInternAtom(fl_display
, "KWM_WIN_DESKTOP", 0);
116 kwm_win_sticky
= XInternAtom(fl_display
, "KWM_WIN_STICKY", 0);
118 // wm_client_leader = XInternAtom(fl_display, "WM_CLIENT_LEADER", 0);
119 _wm_quit_app
= XInternAtom(fl_display
, "_WM_QUIT_APP", 0);
122 label_y
= label_h
= label_w
= 0;
126 {XWindowAttributes attr
;
127 if (existing
) attr
= *existing
;
129 // put in some legal values in case XGetWindowAttributes fails:
130 attr
.x
= attr
.y
= 0; attr
.width
= attr
.height
= 100;
131 attr
.colormap
= fl_colormap
;
132 attr
.border_width
= 0;
133 XGetWindowAttributes(fl_display
, window
, &attr
);
135 left
= top
= dwidth
= dheight
= 0; // pretend border is zero-width for now
136 app_border_width
= attr
.border_width
;
137 x(attr
.x
+app_border_width
); restore_x
= x();
138 y(attr
.y
+app_border_width
); restore_y
= y();
139 w(attr
.width
); restore_w
= w();
140 h(attr
.height
); restore_h
= h();
141 colormap
= attr
.colormap
;}
143 // TRULY HORRIBLE HACK TO WORK AROUND Y < 20...
144 if ( y( ) == 20 ) y( 0 );
149 {XWMHints
* hints
= XGetWMHints(fl_display
, window_
);
151 if ((hints
->flags
& InputHint
) && !hints
->input
) set_flag(NO_FOCUS
);
152 //if (hints && hints->flags&WindowGroupHint) group_ = hints->window_group;
154 switch (getIntProperty(wm_state
, wm_state
, 0)) {
156 state_
= NORMAL
; break;
158 state_
= ICONIC
; break;
159 // X also defines obsolete values ZoomState and InactiveState
161 if (hints
&& (hints
->flags
&StateHint
) && hints
->initial_state
==IconicState
)
166 if (hints
) XFree(hints
);}
167 // Maya sets this, seems to mean the same as group:
168 // if (!group_) group_ = getIntProperty(wm_client_leader, XA_WINDOW);
170 XGetTransientForHint(fl_display
, window_
, &transient_for_xid
);
177 int p
= getIntProperty(_win_hints
, XA_CARDINAL
);
178 if (p
&1) set_flag(NO_FOCUS
); // WIN_HINTS_SKIP_FOCUS
179 // if (p&2) // WIN_HINTS_SKIP_WINLIST
180 // if (p&4) // WIN_HINTS_SKIP_TASKBAR
181 // if (p&8) ... // WIN_HINTS_GROUP_TRANSIENT
182 if (p
&16) set_flag(CLICK_TO_FOCUS
); // WIN_HINTS_FOCUS_ON_CLICK
185 p
= getIntProperty(kwm_win_decoration
, kwm_win_decoration
, 1);
186 if (!(p
&3)) set_flag(NO_BORDER
);
187 else if (p
& 2) set_flag(THIN_BORDER
);
188 if (p
& 256) set_flag(NO_FOCUS
);
192 if (transient_for()) {
193 if (state_
== NORMAL
) state_
= transient_for()->state_
;
195 desktop_
= transient_for()->desktop_
;
199 // see if anybody thinks window is "sticky:"
200 else if ((getIntProperty(_win_state
, XA_CARDINAL
) & 1) // WIN_STATE_STICKY
201 || getIntProperty(kwm_win_sticky
, kwm_win_sticky
)) {
204 // get the desktop from either Gnome or KDE (Gnome takes precedence):
205 p
= getIntProperty(_win_workspace
, XA_CARDINAL
, -1) + 1; // Gnome desktop
206 if (p
<= 0) p
= getIntProperty(kwm_win_desktop
, kwm_win_desktop
);
208 desktop_
= Desktop::number(p
, 1);
210 desktop_
= Desktop::current();
212 if (desktop_
&& desktop_
!= Desktop::current())
213 if (state_
== NORMAL
) state_
= OTHER_DESKTOP
;
216 int autoplace
= getSizes();
217 // some Motif programs assumme this will force the size to conform :-(
218 if (w() < min_w
|| h() < min_h
) {
219 if (w() < min_w
) w(min_w
);
220 if (h() < min_h
) h(min_h
);
221 XResizeWindow(fl_display
, window_
, w(), h());
224 // try to detect programs that think "transient_for" means "no border":
225 if (transient_for_xid
&& !label() && !flag(NO_BORDER
)) {
226 set_flag(THIN_BORDER
);
231 if (autoplace
&& !existing
&& !(transient_for() && (x() || y()))) {
232 // autoplacement (stupid version for now)
233 x(Root
->x()+(Root
->w()-w())/2);
234 y(Root
->y()+((Root
->h()-20)-h())/2);
235 // move it until it does not hide any existing windows:
236 const int delta
= TITLE_WIDTH
+LEFT
;
237 for (Frame
* f
= next
; f
; f
= f
->next
) {
238 if (f
->x()+delta
> x() && f
->y()+delta
> y() &&
239 f
->x()+f
->w()-delta
< x()+w() && f
->y()+f
->h()-delta
< y()+h()) {
240 x(max(x(),f
->x()+delta
));
241 y(max(y(),f
->y()+delta
));
246 // move window so contents and border are visible:
247 x(force_x_onscreen(x(), w()));
248 y(force_y_onscreen(y(), h()));
250 // guess some values for the "restore" fields, if already maximized:
251 if (max_w_button
.value() || getIntProperty( wm_maximize_horz
, wm_maximize_horz
, 0 )) {
252 restore_w
= min_w
+ ((w()-dwidth
-min_w
)/2/inc_w
) * inc_w
;
253 restore_x
= x()+left
+ (w()-dwidth
-restore_w
)/2;
254 restore_h
= min_h
+ ((h()-dheight
-min_h
)/2/inc_h
) * inc_h
;
255 restore_y
= y()+top
+ (h()-dheight
-restore_h
)/2;
256 w( maximize_width( ) );
257 h( maximize_height( ) );
260 const int mask
= CWBorderPixel
| CWColormap
| CWEventMask
| CWBitGravity
261 | CWBackPixel
| CWOverrideRedirect
;
262 XSetWindowAttributes sattr
;
263 sattr
.event_mask
= XEventMask
;
264 sattr
.colormap
= fl_colormap
;
265 sattr
.border_pixel
= fl_xpixel(FL_GRAY0
);
266 sattr
.bit_gravity
= NorthWestGravity
;
267 sattr
.override_redirect
= 1;
268 sattr
.background_pixel
= fl_xpixel(FL_GRAY
);
269 Fl_X::set_xid(this, XCreateWindow(fl_display
, fl_xid(Root
),
270 x(), y(), w(), h(), 0,
278 if (!dont_set_event_mask
) XAddToSaveSet(fl_display
, window_
);
279 if (existing
) set_state_flag(IGNORE_UNMAP
);
280 XReparentWindow(fl_display
, window_
, fl_xid(this), left
, top
);
281 XSetWindowBorderWidth(fl_display
, window_
, 0);
282 if (state_
== NORMAL
) XMapWindow(fl_display
, window_
);
283 sendConfigureNotify(); // many apps expect this even if window size unchanged
285 #if CLICK_RAISES || CLICK_TO_TYPE
286 XGrabButton(fl_display
, AnyButton
, AnyModifier
, window
, False
,
287 ButtonPressMask
, GrabModeSync
, GrabModeAsync
, None
, None
);
290 if (state_
== NORMAL
) {
291 XMapWindow(fl_display
, fl_xid(this));
292 if (!existing
) activate_if_transient();
296 // modify the passed X & W to a legal horizontal window position
297 int Frame::force_x_onscreen(int X
, int W
) {
298 // force all except the black border on-screen:
299 X
= min(X
, Root
->x()+Root
->w()+1-W
);
300 X
= max(X
, Root
->x()-1);
301 // force the contents on-screen:
302 X
= min(X
, Root
->x()+Root
->w()-W
+dwidth
-left
);
303 if (W
-dwidth
> Root
->w() || h()-dheight
> (Root
->h()-20))
304 // windows bigger than the screen need title bar so they can move
305 X
= max(X
, Root
->x()-LEFT
);
307 X
= max(X
, Root
->x()-left
);
311 // modify the passed Y & H to a legal vertical window position:
312 int Frame::force_y_onscreen(int Y
, int H
) {
313 // force border (except black edge) to be on-screen:
314 Y
= min(Y
, Root
->y()+(Root
->h()-20)+1-H
);
315 Y
= max(Y
, Root
->y()-1);
316 // force contents to be on-screen:
317 Y
= min(Y
, Root
->y()+(Root
->h()-20)-H
+dheight
-top
);
318 Y
= max(Y
, Root
->y()-top
);
322 ////////////////////////////////////////////////////////////////
324 // The destructor is called on DestroyNotify, so I don't have to do anything
325 // to the contained window, which is already been destroyed.
327 // fltk bug: it does not clear these pointers when window is deleted,
328 // causing flwm to crash on window close sometimes:
329 extern Fl_Window
*fl_xfocus
;
330 extern Fl_Window
*fl_xmousewin
;
334 // It is possible for the frame to be destroyed while the menu is
335 // popped-up, and the menu will still contain a pointer to it. To
336 // fix this the menu checks the state_ location for a legal and
337 // non-withdrawn state value before doing anything. This should
338 // be reliable unless something reallocates the memory and writes
339 // a legal state value to this location:
347 // remove any pointers to this:
348 Frame
** cp
; for (cp
= &first
; *cp
; cp
= &((*cp
)->next
))
349 if (*cp
== this) {*cp
= next
; break;}
350 for (Frame
* f
= first
; f
; f
= f
->next
) {
351 if (f
->transient_for_
== this) f
->transient_for_
= transient_for_
;
352 if (f
->revert_to
== this) f
->revert_to
= revert_to
;
356 if (colormapWinCount
) {
357 XFree((char *)colormapWindows
);
358 delete[] window_Colormaps
;
360 //if (iconlabel()) XFree((char*)iconlabel());
361 if (label()) XFree((char*)label());
364 ////////////////////////////////////////////////////////////////
366 void Frame::getLabel(int del
) {
367 char* old
= (char*)label();
368 char* nu
= del
? 0 : (char*)getProperty(XA_WM_NAME
);
370 // since many window managers print a default label when none is
371 // given, many programs send spaces to make a blank label. Detect
372 // this and make it really be blank:
373 char* c
= nu
; while (*c
== ' ') c
++;
374 if (!*c
) {XFree(nu
); nu
= 0;}
377 if (nu
&& !strcmp(old
,nu
)) {XFree(nu
); return;}
382 Fl_Widget::label(nu
);
384 fl_font(TITLE_FONT_SLOT
, TITLE_FONT_SIZE
);
385 label_w
= int(fl_width(nu
))+6;
388 if (shown() && label_h
> 3 && left
> 3)
389 XClearArea(fl_display
, fl_xid(this), 1, label_y
+3, left
-1, label_h
-3, 1);
392 ////////////////////////////////////////////////////////////////
394 int Frame::getGnomeState(int &) {
395 // values for _WIN_STATE property are from Gnome WM compliance docs:
396 #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/
397 #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/
398 #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/
399 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/
400 #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/
401 #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/
402 #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/
403 #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/
404 #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/
405 #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/
410 ////////////////////////////////////////////////////////////////
412 // Read the sizeHints, and try to remove the vast number of mistakes
413 // that some applications seem to do writing them.
414 // Returns true if autoplace should be done.
416 int Frame::getSizes() {
418 XSizeHints sizeHints
;
420 if (!XGetWMNormalHints(fl_display
, window_
, &sizeHints
, &junk
))
423 // get the increment, use 1 if none or illegal values:
424 if (sizeHints
.flags
& PResizeInc
) {
425 inc_w
= sizeHints
.width_inc
; if (inc_w
< 1) inc_w
= 1;
426 inc_h
= sizeHints
.height_inc
; if (inc_h
< 1) inc_h
= 1;
431 // get the current size of the window:
434 // I try a lot of places to get a good minimum size value. Lots of
435 // programs set illegal or junk values, so getting this correct is
440 // guess a value for minimum size in case it is not set anywhere:
441 min_w
= min(min_w
, 4*BUTTON_H
);
442 min_w
= ((min_w
+inc_w
-1)/inc_w
) * inc_w
;
443 min_h
= min(min_h
, 4*BUTTON_H
);
444 min_h
= ((min_h
+inc_h
-1)/inc_h
) * inc_h
;
445 // some programs put the minimum size here:
446 if (sizeHints
.flags
& PBaseSize
) {
447 junk
= sizeHints
.base_width
; if (junk
> 0) min_w
= junk
;
448 junk
= sizeHints
.base_height
; if (junk
> 0) min_h
= junk
;
450 // finally, try the actual place the minimum size should be:
451 if (sizeHints
.flags
& PMinSize
) {
452 junk
= sizeHints
.min_width
; if (junk
> 0) min_w
= junk
;
453 junk
= sizeHints
.min_height
; if (junk
> 0) min_h
= junk
;
456 max_w
= max_h
= 0; // default maximum size is "infinity"
457 if (sizeHints
.flags
& PMaxSize
) {
458 // Though not defined by ICCCM standard, I interpret any maximum
459 // size that is less than the minimum to mean "infinity". This
460 // allows the maximum to be set in one direction only:
461 junk
= sizeHints
.max_width
;
462 if (junk
>= min_w
&& junk
<= W
) max_w
= junk
;
463 junk
= sizeHints
.max_height
;
464 if (junk
>= min_h
&& junk
<= H
) max_h
= junk
;
467 // set the maximize buttons according to current size:
468 max_w_button
.value(H
== maximize_height() && W
== maximize_width());
470 // Currently only 1x1 aspect works:
471 if (sizeHints
.flags
& PAspect
472 && sizeHints
.min_aspect
.x
== sizeHints
.min_aspect
.y
)
473 set_flag(KEEP_ASPECT
);
475 // another fix for gimp, which sets PPosition to 0,0:
476 if (x() <= 0 && y() <= 0) sizeHints
.flags
&= ~PPosition
;
478 return !(sizeHints
.flags
& (USPosition
|PPosition
));
482 // return width of contents when maximize button pressed:
483 int Frame::maximize_width() {
484 int W
= max_w_switch
; if (!W
) W
= Root
->w();
485 return flag( NO_BORDER
) ? W
: ((W
-TITLE_WIDTH
-min_w
)/inc_w
) * inc_w
+ min_w
;
489 int Frame::maximize_height() {
490 int H
= max_h_switch
; if (!H
) H
= Root
->h()-20;
491 return flag( NO_BORDER
) ? H
: ((H
-min_h
)/inc_h
) * inc_h
+ min_h
;
494 ////////////////////////////////////////////////////////////////
496 void Frame::getProtocols() {
497 int n
; Atom
* p
= (Atom
*)getProperty(wm_protocols
, XA_ATOM
, &n
);
499 clear_flag(DELETE_WINDOW_PROTOCOL
|TAKE_FOCUS_PROTOCOL
|QUIT_PROTOCOL
);
500 for (int i
= 0; i
< n
; ++i
) {
501 if (p
[i
] == wm_delete_window
) {
502 set_flag(DELETE_WINDOW_PROTOCOL
);
503 } else if (p
[i
] == wm_take_focus
) {
504 set_flag(TAKE_FOCUS_PROTOCOL
);
505 } else if (p
[i
] == wm_save_yourself
) {
506 set_flag(SAVE_PROTOCOL
);
507 } else if (p
[i
] == _wm_quit_app
) {
508 set_flag(QUIT_PROTOCOL
);
515 ////////////////////////////////////////////////////////////////
517 int Frame::getMotifHints() {
518 long* prop
= (long*)getProperty(_motif_wm_hints
, _motif_wm_hints
);
521 // see /usr/include/X11/Xm/MwmUtil.h for meaning of these bits...
522 // prop[0] = flags (what props are specified)
523 // prop[1] = functions (all, resize, move, minimize, maximize, close, quit)
524 // prop[2] = decorations (all, border, resize, title, menu, minimize,
526 // prop[3] = input_mode (modeless, primary application modal, system modal,
527 // full application modal)
528 // prop[4] = status (tear-off window)
530 // Fill in the default value for missing fields:
531 if (!(prop
[0]&1)) prop
[1] = 1;
532 if (!(prop
[0]&2)) prop
[2] = 1;
534 // The low bit means "turn the marked items off", invert this.
535 // Transient windows already have size & iconize buttons turned off:
536 if (prop
[1]&1) prop
[1] = ~prop
[1] & (transient_for_xid
? ~0x58 : -1);
537 if (prop
[2]&1) prop
[2] = ~prop
[2] & (transient_for_xid
? ~0x60 : -1);
539 int old_flags
= flags();
541 // see if they are trying to turn off border:
542 if (!(prop
[2])) set_flag(NO_BORDER
); else clear_flag(NO_BORDER
);
544 // see if they are trying to turn off title & close box:
545 if (!(prop
[2]&0x18)) set_flag(THIN_BORDER
); else clear_flag(THIN_BORDER
);
547 // some Motif programs use this to disable resize :-(
548 // and some programs change this after the window is shown (*&%$#%)
549 if (!(prop
[1]&2) || !(prop
[2]&4))
550 set_flag(NO_RESIZE
); else clear_flag(NO_RESIZE
);
552 // and some use this to disable the Close function. The commented
553 // out test is it trying to turn off the mwm menu button: it appears
554 // programs that do that still expect Alt+F4 to close them, so I
555 // leave the close on then:
556 if (!(prop
[1]&0x20) /*|| !(prop[2]&0x10)*/)
557 set_flag(NO_CLOSE
); else clear_flag(NO_CLOSE
);
559 // see if they set "input hint" to non-zero:
560 // prop[3] should be nonzero but the only example of this I have
561 // found is Netscape 3.0 and it sets it to zero...
562 if (!shown() && (prop
[0]&4) /*&& prop[3]*/) set_flag(MODAL
);
564 // see if it is forcing the iconize button back on. This makes
565 // transient_for act like group instead...
566 if ((prop
[1]&0x8) || (prop
[2]&0x20)) set_flag(ICONIZE
);
568 // Silly 'ol Amazon paint ignores WM_DELETE_WINDOW and expects to
569 // get the SGI-specific "_WM_QUIT_APP". It indicates this by trying
570 // to turn off the close box. SIGH!!!
571 if (flag(QUIT_PROTOCOL
) && !(prop
[1]&0x20))
572 clear_flag(DELETE_WINDOW_PROTOCOL
);
575 return (flags() ^ old_flags
);
578 ////////////////////////////////////////////////////////////////
580 void Frame::getColormaps(void) {
581 if (colormapWinCount
) {
582 XFree((char *)colormapWindows
);
583 delete[] window_Colormaps
;
586 Window
* cw
= (Window
*)getProperty(wm_colormap_windows
, XA_WINDOW
, &n
);
588 colormapWinCount
= n
;
589 colormapWindows
= cw
;
590 window_Colormaps
= new Colormap
[n
];
591 for (int i
= 0; i
< n
; ++i
) {
592 if (cw
[i
] == window_
) {
593 window_Colormaps
[i
] = colormap
;
595 XWindowAttributes attr
;
596 XSelectInput(fl_display
, cw
[i
], ColormapChangeMask
);
597 XGetWindowAttributes(fl_display
, cw
[i
], &attr
);
598 window_Colormaps
[i
] = attr
.colormap
;
602 colormapWinCount
= 0;
606 void Frame::installColormap() const {
607 for (int i
= colormapWinCount
; i
--;)
608 if (colormapWindows
[i
] != window_
&& window_Colormaps
[i
])
609 XInstallColormap(fl_display
, window_Colormaps
[i
]);
611 XInstallColormap(fl_display
, colormap
);
614 ////////////////////////////////////////////////////////////////
616 // figure out transient_for(), based on the windows that exist, the
617 // transient_for and group attributes, etc:
618 void Frame::fix_transient_for() {
620 if (transient_for_xid
&& !flag(ICONIZE
)) {
621 for (Frame
* f
= first
; f
; f
= f
->next
) {
622 if (f
!= this && f
->window_
== transient_for_xid
) {p
= f
; break;}
624 // loops are illegal:
625 for (Frame
* q
= p
; q
; q
= q
->transient_for_
) if (q
== this) {p
= 0; break;}
630 int Frame::is_transient_for(const Frame
* f
) const {
632 for (Frame
* p
= transient_for(); p
; p
= p
->transient_for())
633 if (p
== f
) return 1;
637 // When a program maps or raises a window, this is called. It guesses
638 // if this window is in fact a modal window for the currently active
639 // window and if so transfers the active state to this:
640 // This also activates new main windows automatically
641 int Frame::activate_if_transient() {
643 if (!transient_for() || is_transient_for(active_
)) return activate(1);
647 ////////////////////////////////////////////////////////////////
649 int Frame::activate(int warp
) {
650 // see if a modal & newer window is up:
651 for (Frame
* c
= first
; c
&& c
!= this; c
= c
->next
)
652 if (c
->flag(MODAL
) && c
->transient_for() == this)
653 if (c
->activate(warp
)) return 1;
654 // ignore invisible windows:
655 if (state() != NORMAL
|| w() <= dwidth
) return 0;
656 // always put in the colormap:
658 // move the pointer if desired:
659 // (note that moving the pointer is pretty much required for point-to-type
660 // unless you know the pointer is already in the window):
661 if (!warp
|| Fl::event_state() & (FL_BUTTON1
|FL_BUTTON2
|FL_BUTTON3
)) {
663 } else if (warp
==2) {
664 // warp to point at title:
665 //XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, min(label_y+label_w/2+1,h()/2));
669 // skip windows that don't want focus:
670 if (flag(NO_FOCUS
)) return 0;
671 // set this even if we think it already has it, this seems to fix
672 // bugs with Motif popups:
673 XSetInputFocus(fl_display
, window_
, RevertToPointerRoot
, fl_event_time
);
674 if (active_
!= this) {
675 if (active_
) active_
->deactivate();
677 #if defined(ACTIVE_COLOR)
678 XSetWindowAttributes a
;
679 a
.background_pixel
= fl_xpixel(FL_SELECTION_COLOR
);
680 XChangeWindowAttributes(fl_display
, fl_xid(this), CWBackPixel
, &a
);
681 labelcolor(contrast(FL_BLACK
, FL_SELECTION_COLOR
));
682 XClearArea(fl_display
, fl_xid(this), 2, 2, w()-4, h()-4, 1);
684 #if defined(SHOW_CLOCK)
688 if (flag(TAKE_FOCUS_PROTOCOL
))
689 sendMessage(wm_protocols
, wm_take_focus
);
694 // this private function should only be called by constructor and if
695 // the window is active():
696 void Frame::deactivate() {
697 #if defined(ACTIVE_COLOR)
698 XSetWindowAttributes a
;
699 a
.background_pixel
= fl_xpixel(FL_GRAY
);
700 XChangeWindowAttributes(fl_display
, fl_xid(this), CWBackPixel
, &a
);
701 labelcolor(FL_BLACK
);
702 XClearArea(fl_display
, fl_xid(this), 2, 2, w()-4, h()-4, 1);
704 #if defined(SHOW_CLOCK)
710 #if CLICK_RAISES || CLICK_TO_TYPE
711 // After the XGrabButton, the main loop will get the mouse clicks, and
712 // it will call here when it gets them:
713 void click_raise(Frame
* f
) {
716 if (fl_xevent
->xbutton
.button
<= 1) f
->raise();
718 XAllowEvents(fl_display
, ReplayPointer
, CurrentTime
);
722 // get rid of the focus by giving it to somebody, if possible:
723 void Frame::throw_focus(int destructor
) {
724 if (!active()) return;
725 if (!destructor
) deactivate();
727 if (revert_to
&& revert_to
->activate()) return;
728 for (Frame
* f
= first
; f
; f
= f
->next
)
729 if (f
!= this && f
->activate()) return;
732 ////////////////////////////////////////////////////////////////
734 // change the state of the window (this is a private function and
735 // it ignores the transient-for or desktop information):
737 void Frame::state(short newstate
) {
738 short oldstate
= state();
739 if (newstate
== oldstate
) return;
744 set_state_flag(IGNORE_UNMAP
);
745 XUnmapWindow(fl_display
, fl_xid(this));
746 XUnmapWindow(fl_display
, window_
);
747 XRemoveFromSaveSet(fl_display
, window_
);
750 if (oldstate
== UNMAPPED
) XAddToSaveSet(fl_display
, window_
);
751 if (w() > dwidth
) XMapWindow(fl_display
, window_
);
752 XMapWindow(fl_display
, fl_xid(this));
753 clear_state_flag(IGNORE_UNMAP
);
756 if (oldstate
== UNMAPPED
) {
757 XAddToSaveSet(fl_display
, window_
);
758 } else if (oldstate
== NORMAL
) {
760 set_state_flag(IGNORE_UNMAP
);
761 XUnmapWindow(fl_display
, fl_xid(this));
762 XUnmapWindow(fl_display
, window_
);
764 return; // don't setStateProperty IconicState multiple times
771 void Frame::setStateProperty() const {
775 data
[0] = WithdrawnState
; break;
778 data
[0] = NormalState
; break;
780 data
[0] = IconicState
; break;
782 data
[1] = (long)None
;
783 XChangeProperty(fl_display
, window_
, wm_state
, wm_state
,
784 32, PropModeReplace
, (unsigned char *)data
, 2);
787 ////////////////////////////////////////////////////////////////
788 // Public state modifiers that move all transient_for(this) children
789 // with the frame and do the desktops right:
791 void Frame::raise() {
794 int previous_state
= state_
;
796 // Find all the transient-for windows and this one, and raise them,
797 // preserving stacking order:
798 for (p
= &first
; *p
;) {
800 if (f
== this || f
->is_transient_for(this) && f
->state() != UNMAPPED
) {
801 *p
= f
->next
; // remove it from list
804 w
.sibling
= fl_xid(previous
);
805 w
.stack_mode
= Below
;
806 XConfigureWindow(fl_display
, fl_xid(f
), CWSibling
|CWStackMode
, &w
);
809 XRaiseWindow(fl_display
, fl_xid(f
));
813 if (f
->desktop_
&& f
->desktop_
!= Desktop::current())
814 f
->state(OTHER_DESKTOP
);
823 previous
->next
= first
;
826 if (!transient_for() && desktop_
&& desktop_
!= Desktop::current()) {
827 // for main windows we also must move to the current desktop
828 desktop(Desktop::current());
831 if (previous_state
!= NORMAL
&& newtop
->state_
==NORMAL
)
832 newtop
->activate_if_transient();
835 void Frame::lower() {
836 Frame
* t
= transient_for(); if (t
) t
->lower();
837 if (!next
|| next
== t
) return; // already on bottom
838 // pull it out of the list:
840 for (; *p
!= this; p
= &((*p
)->next
)) {}
843 Frame
* f
= next
; while (f
->next
!= t
) f
= f
->next
;
844 // insert it after that:
845 f
->next
= this; next
= t
;
846 // and move the X window:
848 w
.sibling
= fl_xid(f
);
849 w
.stack_mode
= Below
;
850 XConfigureWindow(fl_display
, fl_xid(this), CWSibling
|CWStackMode
, &w
);
853 void Frame::iconize() {
854 for (Frame
* c
= first
; c
; c
= c
->next
) {
855 if (c
== this || c
->is_transient_for(this) && c
->state() != UNMAPPED
)
861 void Frame::desktop(Desktop
* d
) {
862 if (d
== desktop_
) return;
863 // Put all the relatives onto the desktop as well:
864 for (Frame
* c
= first
; c
; c
= c
->next
) {
865 if (c
== this || c
->is_transient_for(this)) {
867 c
->setProperty(_win_state
, XA_CARDINAL
, !d
);
868 c
->setProperty(kwm_win_sticky
, kwm_win_sticky
, !d
);
870 c
->setProperty(kwm_win_desktop
, kwm_win_desktop
, d
->number());
871 c
->setProperty(_win_workspace
, XA_CARDINAL
, d
->number()-1);
873 if (!d
|| d
== Desktop::current()) {
874 if (c
->state() == OTHER_DESKTOP
) c
->state(NORMAL
);
876 if (c
->state() == NORMAL
) c
->state(OTHER_DESKTOP
);
883 ////////////////////////////////////////////////////////////////
885 // Resize and/or move the window. The size is given for the frame, not
886 // the contents. This also sets the buttons on/off as needed:
888 void Frame::set_size(int nx
, int ny
, int nw
, int nh
, int warp
) {
889 if ( ( flag( NO_BORDER
) || flag( THIN_BORDER
) ) && ( max_w_button
.value( ) || nh
== maximize_height( ) ) ) {
892 nh
= maximize_height( );
894 app_border_width
= 0;
896 if ( nh
>= maximize_height( ) ) nh
= maximize_height( );
897 int dx
= nx
-x(); x(nx
);
898 int dy
= ny
-y(); y(ny
);
899 if (!dx
&& !dy
&& nw
== w() && nh
== h()) return;
902 // use XClearArea to cause correct damage events:
904 max_w_button
.value(nw
-dwidth
== maximize_width());
905 min_w_button
.value(nw
<= dwidth
);
909 if (w() <= dwidth
) remap
= 1;
911 int minw
= (nw
< w()) ? nw
: w();
912 XClearArea(fl_display
, fl_xid(this), minw
-RIGHT
, 0, RIGHT
, nh
, 1);
916 int minh
= (nh
< h()) ? nh
: h();
917 XClearArea(fl_display
, fl_xid(this), 0, minh
-BOTTOM
, w(), BOTTOM
, 1);
918 // see if label or close box moved, erase the minimum area:
919 int old_label_y
= label_y
;
920 int old_label_h
= label_h
;
921 h(nh
); show_hide_buttons();
923 int t
= label_y
+ 3; // we have to clear the entire label area
926 if (label_y
!= old_label_y
) {
927 t
= label_y
; if (old_label_y
< t
) t
= old_label_y
;
928 } else if (label_y
+label_h
!= old_label_y
+old_label_h
) {
930 if (old_label_y
+old_label_h
< t
) t
= old_label_y
+old_label_h
;
933 if (t
< nh
&& left
>LEFT
)
934 XClearArea(fl_display
,fl_xid(this), 1, t
, left
-1, nh
-t
, 1);
936 // for maximize button move the cursor first if window gets smaller
937 //if (warp == 1 && (dx || dy))
938 //XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy);
939 // for configure request, move the cursor first
940 if (warp
== 2 && active() && !Fl::pushed()) warp_pointer();
941 XMoveResizeWindow(fl_display
, fl_xid(this), nx
, ny
, nw
, nh
);
944 set_state_flag(IGNORE_UNMAP
);
945 XUnmapWindow(fl_display
, window_
);
948 XResizeWindow(fl_display
, window_
, nw
-dwidth
, nh
-dheight
);
950 XMapWindow(fl_display
, window_
);
952 if (active()) activate();
958 // for maximize button move the cursor second if window gets bigger:
959 //if (warp == 3 && (dx || dy))
960 //XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy);
962 sendConfigureNotify();
964 fprintf( stderr
, "using %dx%d+%d+%d\n", w(), h(), x(), y() );
967 void Frame::sendConfigureNotify() const {
969 ce
.type
= ConfigureNotify
;
972 ce
.x
= x()+left
-app_border_width
;
973 ce
.y
= y()+top
-app_border_width
;
974 ce
.width
= w()-dwidth
;
975 ce
.height
= h()-dheight
;
976 ce
.border_width
= app_border_width
;
978 ce
.override_redirect
= 0;
979 XSendEvent(fl_display
, window_
, False
, StructureNotifyMask
, (XEvent
*)&ce
);
982 // move the pointer inside the window:
983 void Frame::warp_pointer() {
984 int X
,Y
; Fl::get_mouse(X
,Y
);
986 if (X
<= 0) X
= left
/2+1;
987 if (X
>= w()) X
= w()-(RIGHT
/2+1);
989 if (Y
< 0) Y
= TOP
/2+1;
990 if (Y
>= h()) Y
= h()-(BOTTOM
/2+1);
991 //if (X != Xi || Y != Yi)
992 //XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, X, Y);
995 // Resize the frame to match the current border type:
996 void Frame::updateBorder() {
997 if ( flag( NO_BORDER
) )
1002 int nw
= w()-dwidth
;
1003 int nh
= h()-dheight
;
1004 if (flag(NO_BORDER
)) {
1005 left
= top
= dwidth
= dheight
= 0;
1007 left
= flag(THIN_BORDER
) ? LEFT
: LEFT
+TITLE_WIDTH
;
1008 dwidth
= left
+RIGHT
;
1010 dheight
= TOP
+BOTTOM
;
1016 if (x()==nx
&& y()==ny
&& w()==nw
&& h()==nh
) return;
1017 x(nx
); y(ny
); w(nw
); h(nh
);
1018 if (!shown()) return; // this is so constructor can call this
1019 // try to make the contents not move while the border changes around it:
1020 XSetWindowAttributes a
;
1021 a
.win_gravity
= StaticGravity
;
1022 XChangeWindowAttributes(fl_display
, window_
, CWWinGravity
, &a
);
1023 XMoveResizeWindow(fl_display
, fl_xid(this), nx
, ny
, nw
, nh
);
1024 a
.win_gravity
= NorthWestGravity
;
1025 XChangeWindowAttributes(fl_display
, window_
, CWWinGravity
, &a
);
1026 // fix the window position if the X server didn't do the gravity:
1027 XMoveWindow(fl_display
, window_
, left
, top
);
1030 // position and show the buttons according to current border, size,
1031 // and other state information:
1032 void Frame::show_hide_buttons() {
1033 if (flag(THIN_BORDER
|NO_BORDER
)) {
1034 iconize_button
.hide();
1035 max_w_button
.hide();
1036 min_w_button
.hide();
1037 close_button
.hide();
1040 int by
= BUTTON_TOP
;
1042 if (flag(NO_CLOSE
)) {
1044 close_button
.hide();
1047 close_button
.show();
1048 close_button
.position(BUTTON_LEFT
,by
);
1052 if (transient_for()) {
1053 iconize_button
.hide();
1054 min_w_button
.hide();
1056 iconize_button
.hide();
1057 //iconize_button.position(BUTTON_LEFT,by);
1058 //iconize_button.show();
1061 min_w_button
.position(BUTTON_LEFT
,by
);
1062 min_w_button
.show();
1065 min_w_button
.hide();
1068 if (min_w
== max_w
|| flag(KEEP_ASPECT
|NO_RESIZE
) ||
1069 !max_w_button
.value() && by
+label_w
+2*BUTTON_H
> h()-BUTTON_BOTTOM
) {
1070 max_w_button
.hide();
1072 max_w_button
.position(BUTTON_LEFT
,by
);
1073 max_w_button
.show();
1076 if (label_y
!= by
&& shown())
1077 XClearArea(fl_display
,fl_xid(this), 1, by
, left
-1, label_h
+label_y
-by
, 1);
1079 label_h
= h( ) - by
;
1082 // make sure fltk does not try to set the window size:
1083 void Frame::resize(int, int, int, int) {}
1085 ////////////////////////////////////////////////////////////////
1087 void Frame::close() {
1088 if (flag(DELETE_WINDOW_PROTOCOL
))
1089 sendMessage(wm_protocols
, wm_delete_window
);
1090 else if (flag(QUIT_PROTOCOL
))
1091 sendMessage(wm_protocols
, _wm_quit_app
);
1096 void Frame::kill() {
1097 XKillClient(fl_display
, window_
);
1100 // this is called when window manager exits:
1101 void Frame::save_protocol() {
1103 for (f
= first
; f
; f
= f
->next
) if (f
->flag(SAVE_PROTOCOL
)) {
1104 f
->set_state_flag(SAVE_PROTOCOL_WAIT
);
1105 f
->sendMessage(wm_protocols
, wm_save_yourself
);
1107 double t
= 10.0; // number of seconds to wait before giving up
1109 for (f
= first
; ; f
= f
->next
) {
1111 if (f
->flag(SAVE_PROTOCOL
) && f
->state_flags_
&SAVE_PROTOCOL_WAIT
) break;
1117 ////////////////////////////////////////////////////////////////
1120 void Frame::draw() {
1121 if (flag(NO_BORDER
)) return;
1122 if (!flag(THIN_BORDER
)) Fl_Window::draw();
1123 if (damage() != FL_DAMAGE_CHILD
) {
1125 fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h());
1127 fl_color(FL_GRAY_RAMP
+('N'-'A'));
1128 fl_xyline(2, h()-3, w()-3, 2);
1131 fl_frame("AAAAWWJJTTNN",0,0,w(),h());
1133 if (!flag(THIN_BORDER
) && label_h
> 3) {
1136 int clkw
= int(fl_width(clock_buf
));
1137 if (clock_alarm_on
) {
1138 fl_font(TITLE_FONT_SLOT
, TITLE_FONT_SIZE
);
1139 fl_rectf(LEFT
-1, label_y
+ label_h
- 3 - clkw
, TITLE_WIDTH
, clkw
,
1140 (ALARM_BG_COLOR
>>16)&0xff,
1141 (ALARM_BG_COLOR
>>8)&0xff,
1142 ALARM_BG_COLOR
&0xff);
1143 fl_color((ALARM_FG_COLOR
>>16)&0xff,
1144 (ALARM_FG_COLOR
>>8)&0xff,
1145 ALARM_FG_COLOR
&0xff);
1147 fl_font(MENU_FONT_SLOT
, TITLE_FONT_SIZE
);
1148 // This might overlay the label if the label is long enough
1149 // and the window height is short enough. For now, we'll
1150 // assume this is not enough of a problem to be concerned
1152 draw_rotated90(clock_buf
, 1, label_y
+3, left
-1, label_h
-6,
1153 Fl_Align(FL_ALIGN_BOTTOM
|FL_ALIGN_CLIP
));
1155 // Only show the clock on the active frame.
1156 XClearArea(fl_display
, fl_xid(this), 1, label_y
+3,
1157 left
-1, label_h
-3, 0);
1159 fl_color(labelcolor());
1160 fl_font(TITLE_FONT_SLOT
, TITLE_FONT_SIZE
);
1161 draw_rotated90(label(), 1, label_y
+3, left
-1, label_h
-3,
1162 Fl_Align(FL_ALIGN_TOP
|FL_ALIGN_CLIP
));
1168 void Frame::redraw_clock() {
1169 double clkw
= fl_width(clock_buf
);
1170 XClearArea(fl_display
, fl_xid(this),
1171 1, label_y
+label_h
-3-(int)clkw
,
1172 left
-1, (int)clkw
, 1);
1176 void FrameButton::draw() {
1177 Fl_Widget::draw_box(value() ? FL_DOWN_FRAME
: FL_UP_FRAME
, FL_GRAY
);
1178 fl_color(parent()->labelcolor());
1179 switch (label()[0]) {
1182 fl_line (x()+2,y()+(h())/2,x()+w()-4,y()+h()/2);
1183 fl_line (x()+2,y()+(h())/2,x()+2+4,y()+h()/2+4);
1184 fl_line (x()+2,y()+(h())/2,x()+2+4,y()+h()/2-4);
1186 fl_rect(x()+(h()-7)/2,y()+3,2,h()-6);
1190 fl_rect(x()+2,y()+(h()-7)/2,w()-4,7);
1193 fl_rect(x()+(h()-7)/2,y()+2,7,h()-4);
1197 fl_line(x()+2,y()+3,x()+w()-5,y()+h()-4);
1198 fl_line(x()+3,y()+3,x()+w()-4,y()+h()-4);
1199 fl_line(x()+2,y()+h()-4,x()+w()-5,y()+3);
1200 fl_line(x()+3,y()+h()-4,x()+w()-4,y()+3);
1202 #if CLOSE_HITTITE_LIGHTNING
1203 fl_arc(x()+3,y()+3,w()-6,h()-6,0,360);
1204 fl_line(x()+7,y()+3, x()+7,y()+11);
1209 fl_rect(x()+w()/2-1,y()+h()/2-1,3,3);
1215 ////////////////////////////////////////////////////////////////
1216 // User interface code:
1218 // this is called when user clicks the buttons:
1219 void Frame::button_cb(Fl_Button
* b
) {
1220 switch (b
->label()[0]) {
1221 case 'W': // minimize button
1223 if (!max_w_button
.value()) {
1224 restore_x
= x()+left
;
1225 restore_y
= y()+top
;
1227 restore_w
=w()-dwidth
;
1228 restore_h
= h()-dwidth
;
1232 set_size(x(), y(), dwidth
-1,
1233 min(h(),min(350,label_w
+3*BUTTON_H
+BUTTON_TOP
+BUTTON_BOTTOM
)),
1236 set_size(x(), y(), dwidth
-1, h(), 1);
1240 set_size(x(), y(), restore_w
+dwidth
, restore_h
+dwidth
, 1);
1242 set_size(x(), y(), restore_w
+dwidth
, h(), 1);
1245 show_hide_buttons();
1247 case 'w': // max-width button
1249 if (!min_w_button
.value()) {restore_x
=x()+left
; restore_w
=w()-dwidth
;}
1250 int W
= maximize_width()+dwidth
;
1251 int X
= force_x_onscreen(x() + (w()-W
)/2, W
);
1252 set_size(X
, y(), W
, h(), 3);
1254 set_size(restore_x
-left
, y(), restore_w
+dwidth
, h(), 1);
1256 show_hide_buttons();
1258 restore_y
= y()+top
;
1259 restore_h
= h()-dwidth
;
1260 int H
= maximize_height()+dheight
;
1261 int Y
= force_y_onscreen(y() + (h()-H
)/2, H
);
1262 set_size(x(), Y
, w(), H
, 3);
1264 set_size(x(), restore_y
-top
, w(), restore_h
+dwidth
, 1);
1270 default: // iconize button
1276 // static callback for fltk:
1277 void Frame::button_cb_static(Fl_Widget
* w
, void*) {
1278 ((Frame
*)(w
->parent()))->button_cb((Fl_Button
*)w
);
1281 // This method figures out what way the mouse will resize the window.
1282 // It is used to set the cursor and to actually control what you grab.
1283 // If the window cannot be resized in some direction this should not
1284 // return that direction.
1285 int Frame::mouse_location() {
1286 int x
= Fl::event_x();
1287 int y
= Fl::event_y();
1289 if (flag(NO_RESIZE
)) return 0;
1290 if (min_h
!= max_h
) {
1291 if (y
< RESIZE_EDGE
) r
|= FL_ALIGN_TOP
;
1292 else if (y
>= h()-RESIZE_EDGE
) r
|= FL_ALIGN_BOTTOM
;
1294 if (min_w
!= max_w
) {
1296 if (x
< RESIZE_EDGE
) r
|= FL_ALIGN_LEFT
;
1298 if (x
< RESIZE_EDGE
&& r
) r
|= FL_ALIGN_LEFT
;
1300 else if (x
>= w()-RESIZE_EDGE
) r
|= FL_ALIGN_RIGHT
;
1305 // set the cursor correctly for a return value from mouse_location():
1306 void Frame::set_cursor(int r
) {
1307 Fl_Cursor c
= r
? FL_CURSOR_ARROW
: FL_CURSOR_MOVE
;
1310 case FL_ALIGN_BOTTOM
:
1314 case FL_ALIGN_RIGHT
:
1317 case FL_ALIGN_LEFT
|FL_ALIGN_TOP
:
1318 case FL_ALIGN_RIGHT
|FL_ALIGN_BOTTOM
:
1321 case FL_ALIGN_LEFT
|FL_ALIGN_BOTTOM
:
1322 case FL_ALIGN_RIGHT
|FL_ALIGN_TOP
:
1326 static Frame
* previous_frame
;
1327 static Fl_Cursor previous_cursor
;
1328 if (this != previous_frame
|| c
!= previous_cursor
) {
1329 previous_frame
= this;
1330 previous_cursor
= c
;
1331 cursor(c
, CURSOR_FG_SLOT
, CURSOR_BG_SLOT
);
1336 // timeout callback to cause autoraise:
1337 void auto_raise(void*) {
1338 if (Frame::activeFrame() && !Fl::grab() && !Fl::pushed())
1339 Frame::activeFrame()->raise();
1343 extern void ShowMenu();
1345 // If cursor is in the contents of a window this is set to that window.
1346 // This is only used to force the cursor to an arrow even though X keeps
1347 // sending mysterious erroneous move events:
1348 static Frame
* cursor_inside
= 0;
1350 // Handle an fltk event.
1351 int Frame::handle(int e
) {
1352 static int what
, dx
, dy
, ix
, iy
, iw
, ih
;
1353 // see if child widget handles event:
1354 if (Fl_Window::handle(e
) && e
!= FL_ENTER
&& e
!= FL_MOVE
) {
1355 if (e
== FL_PUSH
) set_cursor(-1);
1362 return 0; // prevent fltk from messing things up
1366 if (Fl::pushed() || Fl::grab()) return 1;
1369 Fl::remove_timeout(auto_raise
);
1370 Fl::add_timeout(AUTO_RAISE
, auto_raise
);
1377 #if !CLICK_TO_TYPE && !STICKY_FOCUS
1380 XSetInputFocus(fl_display
, PointerRoot
, RevertToPointerRoot
,
1389 // set cursor_inside to true when the mouse is inside a window
1390 // set it false when mouse is on a frame or outside a window.
1391 // fltk mangles the X enter/leave events, we need the original ones:
1393 switch (fl_xevent
->type
) {
1396 // see if cursor skipped over frame and directly to interior:
1397 if (fl_xevent
->xcrossing
.detail
== NotifyVirtual
||
1398 fl_xevent
->xcrossing
.detail
== NotifyNonlinearVirtual
)
1399 cursor_inside
= this;
1402 // cursor is now pointing at frame:
1406 // fall through to FL_MOVE:
1410 if (fl_xevent
->xcrossing
.detail
== NotifyInferior
) {
1411 // cursor moved from frame to interior
1412 cursor_inside
= this;
1419 return 0; // other X event we don't understand
1423 if (Fl::belowmouse() != this || cursor_inside
== this)
1426 set_cursor(mouse_location());
1430 if (Fl::event_button() > 2) {
1435 ix
= x(); iy
= y(); iw
= w(); ih
= h();
1436 if (!max_w_button
.value() && !min_w_button
.value()) {
1437 restore_x
= ix
+left
; restore_w
= iw
-dwidth
;
1440 if (!min_w_button
.value())
1442 if (!max_w_button
.value()) {
1443 restore_y
= iy
+top
; restore_h
= ih
-dwidth
;
1445 what
= mouse_location();
1446 if (Fl::event_button() > 1) what
= 0; // middle button does drag
1447 dx
= Fl::event_x_root()-ix
;
1448 if (what
& FL_ALIGN_RIGHT
) dx
-= iw
;
1449 dy
= Fl::event_y_root()-iy
;
1450 if (what
& FL_ALIGN_BOTTOM
) dy
-= ih
;
1454 if (Fl::event_is_click()) return 1; // don't drag yet
1456 if (Fl::event_is_click()) {
1457 if (Fl::grab()) return 1;
1460 if (Fl::event_button() <= 1) raise();
1464 if (Fl::event_button() > 1) lower(); else raise();
1466 int nx
= Fl::event_x_root()-dx
;
1467 int W
= Root
->x()+Root
->w();
1468 if (nx
+iw
> W
&& nx
+iw
< W
+SCREEN_SNAP
) {
1470 if (iw
>= Root
->w() || x() > t
|| nx
+iw
>= W
+EDGE_SNAP
)
1471 t
= W
+(dwidth
-left
)-iw
;
1472 if (t
>= x() && t
< nx
) nx
= t
;
1475 if (nx
< X
&& nx
> X
-SCREEN_SNAP
) {
1477 if (iw
>= Root
->w() || x() < t
|| nx
<= X
-EDGE_SNAP
) t
= X
-BUTTON_LEFT
;
1478 if (t
<= x() && t
> nx
) nx
= t
;
1480 int ny
= Fl::event_y_root()-dy
;
1481 int H
= Root
->y()+Root
->h()-20;
1482 if (ny
+ih
> H
&& ny
+ih
< H
+SCREEN_SNAP
) {
1484 if (ih
>= (Root
->h()-20) || y() > t
|| ny
+ih
>= H
+EDGE_SNAP
)
1485 t
= H
+(dheight
-top
)-ih
;
1486 if (t
>= y() && t
< ny
) ny
= t
;
1489 if (ny
< Y
&& ny
> Y
-SCREEN_SNAP
) {
1491 if (ih
>= H
|| y() < t
|| ny
<= Y
-EDGE_SNAP
) t
= Y
-top
;
1492 if (t
<= y() && t
> ny
) ny
= t
;
1494 set_size(nx
, ny
, iw
, ih
);
1500 if (what
& FL_ALIGN_RIGHT
)
1501 nw
= Fl::event_x_root()-dx
-nx
;
1502 else if (what
& FL_ALIGN_LEFT
)
1503 nw
= ix
+iw
-(Fl::event_x_root()-dx
);
1504 else {nx
= x(); nw
= w();}
1505 if (what
& FL_ALIGN_BOTTOM
)
1506 nh
= Fl::event_y_root()-dy
-ny
;
1507 else if (what
& FL_ALIGN_TOP
)
1508 nh
= iy
+ih
-(Fl::event_y_root()-dy
);
1509 else {ny
= y(); nh
= h();}
1510 if (flag(KEEP_ASPECT
)) {
1511 if (nw
-dwidth
> nh
-dwidth
1512 && (what
&(FL_ALIGN_LEFT
|FL_ALIGN_RIGHT
))
1513 || !(what
&(FL_ALIGN_TOP
|FL_ALIGN_BOTTOM
)))
1514 nh
= nw
-dwidth
+dheight
;
1516 nw
= nh
-dheight
+dwidth
;
1518 int MINW
= min_w
+dwidth
;
1519 if (nw
<= dwidth
&& dwidth
> TITLE_WIDTH
) {
1525 if (inc_w
> 1) nw
= ((nw
-MINW
+inc_w
/2)/inc_w
)*inc_w
+MINW
;
1526 if (nw
< MINW
) nw
= MINW
;
1527 else if (max_w
&& nw
> max_w
+dwidth
) nw
= max_w
+dwidth
;
1529 int MINH
= min_h
+dheight
;
1530 const int MINH_B
= BUTTON_H
+BUTTON_TOP
+BUTTON_BOTTOM
;
1531 if (MINH_B
> MINH
) MINH
= MINH_B
;
1532 if (inc_h
> 1) nh
= ((nh
-MINH
+inc_h
/2)/inc_h
)*inc_h
+MINH
;
1533 if (nh
< MINH
) nh
= MINH
;
1534 else if (max_h
&& nh
> max_h
+dheight
) nh
= max_h
+dheight
;
1535 if (what
& FL_ALIGN_LEFT
) nx
= ix
+iw
-nw
;
1536 if (what
& FL_ALIGN_TOP
) ny
= iy
+ih
-nh
;
1537 set_size(nx
,ny
,nw
,nh
);
1544 // Handle events that fltk did not recognize (mostly ones directed
1547 int Frame::handle(const XEvent
* ei
) {
1551 case ConfigureRequest
: {
1552 const XConfigureRequestEvent
* e
= &(ei
->xconfigurerequest
);
1553 unsigned long mask
= e
->value_mask
;
1554 if (mask
& CWBorderWidth
) app_border_width
= e
->border_width
;
1555 // Try to detect if the application is really trying to move the
1556 // window, or is simply echoing it's postion, possibly with some
1557 // variation (such as echoing the parent window position), and
1558 // dont' move it in that case:
1559 int X
= (mask
& CWX
&& e
->x
!= x()) ? e
->x
+app_border_width
-left
: x();
1560 int Y
= (mask
& CWY
&& e
->y
!= y()) ? e
->y
+app_border_width
-top
: y();
1561 int W
= (mask
& CWWidth
) ? e
->width
+dwidth
: w();
1562 int H
= (mask
& CWHeight
) ? e
->height
+dheight
: h();
1563 // Generally we want to obey any application positioning of the
1564 // window, except when it appears the app is trying to position
1565 // the window "at the edge".
1566 if (!(mask
& CWX
) || (X
>= -2*left
&& X
< 0)) X
= force_x_onscreen(X
,W
);
1567 if (!(mask
& CWY
) || (Y
>= -2*top
&& Y
< 0)) Y
= force_y_onscreen(Y
,H
);
1568 // Fix Rick Sayre's program that resizes it's windows bigger than the
1570 if (W
> max_w
+dwidth
) max_w
= 0;
1571 if (H
> max_h
+dheight
) max_h
= 0;
1572 set_size(X
, Y
, W
, H
, 2);
1573 if (e
->value_mask
& CWStackMode
&& e
->detail
== Above
&& state()==NORMAL
)
1578 //const XMapRequestEvent* e = &(ei->xmaprequest);
1583 const XUnmapEvent
* e
= &(ei
->xunmap
);
1584 if (e
->from_configure
);
1585 else if (state_flags_
&IGNORE_UNMAP
) clear_state_flag(IGNORE_UNMAP
);
1586 else state(UNMAPPED
);
1589 case DestroyNotify
: {
1590 //const XDestroyWindowEvent* e = &(ei->xdestroywindow);
1594 case ReparentNotify
: {
1595 const XReparentEvent
* e
= &(ei
->xreparent
);
1596 if (e
->parent
==fl_xid(this)) return 1; // echo
1597 if (e
->parent
==fl_xid(Root
)) return 1; // app is trying to tear-off again?
1598 delete this; // guess they are trying to paste tear-off thing back?
1601 case ClientMessage
: {
1602 const XClientMessageEvent
* e
= &(ei
->xclient
);
1603 if (e
->message_type
== wm_change_state
&& e
->format
== 32) {
1604 if (e
->data
.l
[0] == NormalState
) raise();
1605 else if (e
->data
.l
[0] == IconicState
) iconize();
1607 else if ( e
->message_type
== net_wm_state
&& e
->format
== 32 )
1609 if ( e
->data
.l
[0] && ( int )e
->data
.l
[1] == ( int )wm_maximize_horz
)
1611 max_w_button
.value( 1 );
1612 set_size( 0, 0, Root
->w( ), maximize_height( ), 1 );
1616 // we may want to ignore _WIN_LAYER from xmms?
1617 Fl::warning("flwm: unexpected XClientMessageEvent, type 0x%lx, "
1618 "window 0x%lx\n", e
->message_type
, e
->window
);
1621 case ColormapNotify
: {
1622 const XColormapEvent
* e
= &(ei
->xcolormap
);
1623 if (e
->c_new
) { // this field is called "new" in the old C++-unaware Xlib
1624 colormap
= e
->colormap
;
1625 if (active()) installColormap();
1629 case PropertyNotify
: {
1630 const XPropertyEvent
* e
= &(ei
->xproperty
);
1633 // case XA_WM_ICON_NAME: (do something similar to name)
1634 if (a
== XA_WM_NAME
) {
1635 getLabel(e
->state
== PropertyDelete
);
1637 } else if (a
== wm_state
) {
1638 // it's not clear if I really need to look at this. Need to make
1639 // sure it is not seeing the state echoed by the application by
1640 // checking for it being different...
1641 switch (getIntProperty(wm_state
, wm_state
, state())) {
1643 if (state() == NORMAL
|| state() == OTHER_DESKTOP
) iconize(); break;
1645 if (state() != NORMAL
&& state() != OTHER_DESKTOP
) raise(); break;
1648 } else if (a
== wm_colormap_windows
) {
1650 if (active()) installColormap();
1652 } else if (a
== _motif_wm_hints
) {
1653 // some #%&%$# SGI Motif programs change this after mapping the window!
1654 // :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( :-( :=(
1655 if (getMotifHints()) { // returns true if any flags changed
1656 fix_transient_for();
1658 show_hide_buttons();
1661 } else if (a
== wm_protocols
) {
1663 // get Motif hints since they may do something with QUIT:
1666 } else if (a
== XA_WM_NORMAL_HINTS
|| a
== XA_WM_SIZE_HINTS
) {
1668 show_hide_buttons();
1670 } else if (a
== XA_WM_TRANSIENT_FOR
) {
1671 XGetTransientForHint(fl_display
, window_
, &transient_for_xid
);
1672 fix_transient_for();
1673 show_hide_buttons();
1675 } else if (a
== XA_WM_COMMAND
) {
1676 clear_state_flag(SAVE_PROTOCOL_WAIT
);
1685 ////////////////////////////////////////////////////////////////
1686 // X utility routines:
1688 void* Frame::getProperty(Atom a
, Atom type
, int* np
) const {
1689 return ::getProperty(window_
, a
, type
, np
);
1692 void* getProperty(Window w
, Atom a
, Atom type
, int* np
) {
1695 unsigned long n
, extra
;
1698 status
= XGetWindowProperty(fl_display
, w
,
1699 a
, 0L, 256L, False
, type
, &realType
,
1700 &format
, &n
, &extra
, (uchar
**)&prop
);
1701 if (status
!= Success
) return 0;
1702 if (!prop
) return 0;
1703 if (!n
) {XFree(prop
); return 0;}
1704 if (np
) *np
= (int)n
;
1708 int Frame::getIntProperty(Atom a
, Atom type
, int deflt
) const {
1709 return ::getIntProperty(window_
, a
, type
, deflt
);
1712 int getIntProperty(Window w
, Atom a
, Atom type
, int deflt
) {
1713 void* prop
= getProperty(w
, a
, type
);
1714 if (!prop
) return deflt
;
1715 int r
= int(*(long*)prop
);
1720 void setProperty(Window w
, Atom a
, Atom type
, int v
) {
1722 XChangeProperty(fl_display
, w
, a
, type
, 32, PropModeReplace
, (uchar
*)&prop
,1);
1725 void Frame::setProperty(Atom a
, Atom type
, int v
) const {
1726 ::setProperty(window_
, a
, type
, v
);
1729 void Frame::sendMessage(Atom a
, Atom l
) const {
1732 memset(&ev
, 0, sizeof(ev
));
1733 ev
.xclient
.type
= ClientMessage
;
1734 ev
.xclient
.window
= window_
;
1735 ev
.xclient
.message_type
= a
;
1736 ev
.xclient
.format
= 32;
1737 ev
.xclient
.data
.l
[0] = long(l
);
1738 ev
.xclient
.data
.l
[1] = long(fl_event_time
);
1740 XSendEvent(fl_display
, window_
, False
, mask
, &ev
);