├── .gitignore ├── Desktop.C ├── Desktop.H ├── Frame.C ├── Frame.H ├── FrameWindow.C ├── FrameWindow.H ├── Hotkeys.C ├── Menu.C ├── README ├── compileit ├── config.h ├── flwm.1 ├── logo.fl ├── main.C └── old └── rotated_test.C /.gitignore: -------------------------------------------------------------------------------- 1 | flwm 2 | flwm_topside 3 | *.o 4 | -------------------------------------------------------------------------------- /Desktop.C: -------------------------------------------------------------------------------- 1 | // Desktop.C 2 | 3 | #include "config.h" 4 | 5 | #if DESKTOPS 6 | 7 | #include "Frame.H" 8 | #include "Desktop.H" 9 | #include 10 | #include 11 | #include 12 | 13 | Desktop* Desktop::current_ = 0; 14 | Desktop* Desktop::first = 0; 15 | 16 | // return the highest desktop number: 17 | int Desktop::max_number() { 18 | int n = 0; 19 | for (Desktop* d = first; d; d = d->next) 20 | if (d->number_ > n) n = d->number_; 21 | return n; 22 | } 23 | 24 | // return an empty slot number: 25 | int Desktop::available_number() { 26 | int n = 1; 27 | for (Desktop* d = first; d;) { 28 | if (d->number_ == n) {n++; d = first;} 29 | else d = d->next; 30 | } 31 | return n; 32 | } 33 | 34 | // these are set by main.C: 35 | Atom _win_workspace; 36 | Atom _win_workspace_count; 37 | Atom _win_workspace_names; 38 | #ifndef __sgi 39 | static Atom kwm_current_desktop; 40 | #endif 41 | extern Fl_Window* Root; 42 | 43 | static int dont_send; 44 | static void send_desktops() { 45 | if (dont_send) return; 46 | int n = Desktop::max_number(); 47 | setProperty(fl_xid(Root), _win_workspace_count, XA_CARDINAL, n); 48 | char buffer[1025]; 49 | char* p = buffer; 50 | for (int i = 1; i <= n; i++) { 51 | Desktop* d = Desktop::number(i); 52 | const char* name = d ? d->name() : ""; 53 | while (p < buffer+1024 && *name) *p++ = *name++; 54 | *p++ = 0; 55 | if (p >= buffer+1024) break; 56 | } 57 | XChangeProperty(fl_display, fl_xid(Root), _win_workspace_names, XA_STRING, 58 | 8, PropModeReplace, (unsigned char *)buffer, p-buffer-1); 59 | } 60 | 61 | Desktop::Desktop(const char* n, int num) { 62 | next = first; 63 | first = this; 64 | name_ = strdup(n); 65 | number_ = num; 66 | send_desktops(); 67 | } 68 | 69 | Desktop::~Desktop() { 70 | // remove from list: 71 | for (Desktop** p = &first; *p; p = &((*p)->next)) 72 | if (*p == this) {*p = next; break;} 73 | send_desktops(); 74 | if (current_ == this || !first->next) current(first); 75 | // put any clients onto another desktop: 76 | for (Frame* c = Frame::first; c; c = c->next) 77 | if (c->desktop() == this) c->desktop(first); 78 | free((void*)name_); 79 | } 80 | 81 | void Desktop::name(const char* l) { 82 | free((void*)name_); 83 | name_ = strdup(l); 84 | } 85 | 86 | void Desktop::current(Desktop* n) { 87 | if (n == current_) return; 88 | current_ = n; 89 | for (Frame* c = Frame::first; c; c = c->next) { 90 | if (c->desktop() == n) { 91 | if (c->state() == OTHER_DESKTOP) c->state(NORMAL); 92 | } else if (c->desktop()) { 93 | if (c->state() == NORMAL) c->state(OTHER_DESKTOP); 94 | } 95 | } 96 | if (n && !dont_send) { 97 | #ifndef __sgi 98 | setProperty(fl_xid(Root), kwm_current_desktop, kwm_current_desktop, n->number()); 99 | #endif 100 | setProperty(fl_xid(Root), _win_workspace, XA_CARDINAL, n->number()-1); 101 | } 102 | } 103 | 104 | // return desktop with given number, create it if necessary: 105 | Desktop* Desktop::number(int n, int create) { 106 | if (!n) return 0; 107 | Desktop* d; 108 | for (d = first; d; d = d->next) if (d->number() == n) return d; 109 | if (create) { 110 | char buf[20]; sprintf(buf, "Desktop %d", n); 111 | d = new Desktop(buf,n); 112 | } 113 | return d; 114 | } 115 | 116 | // called at startup, read the list of desktops from the root 117 | // window properties, or on failure make some default desktops. 118 | void init_desktops() { 119 | dont_send = 1; 120 | int length; 121 | char* buffer = 122 | (char*)getProperty(fl_xid(Root), _win_workspace_names, XA_STRING, &length); 123 | if (buffer) { 124 | char* c = buffer; 125 | for (int i = 1; c < buffer+length; i++) { 126 | char* d = c; while (*d) d++; 127 | if (*c != '<') new Desktop(c,i); 128 | c = d+1; 129 | } 130 | XFree(buffer); 131 | } 132 | int current_num = 0; 133 | int p = getIntProperty(fl_xid(Root), _win_workspace, XA_CARDINAL, -1); 134 | if (p >= 0 && p < 25) current_num = p+1; 135 | #ifndef __sgi 136 | // SGI's Xlib barfs when you try to do this XInternAtom! 137 | // Maybe somebody there does not like KDE? 138 | kwm_current_desktop = XInternAtom(fl_display, "KWM_CURRENT_DESKTOP", 0); 139 | if (!current_num) { 140 | p = getIntProperty(fl_xid(Root), kwm_current_desktop, kwm_current_desktop); 141 | if (p > 0 && p < 25) current_num = p; 142 | } 143 | #endif 144 | if (!current_num) current_num = 1; 145 | Desktop::current(Desktop::number(current_num, 1)); 146 | dont_send = 0; 147 | } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /Desktop.H: -------------------------------------------------------------------------------- 1 | // Desktop.H 2 | 3 | class Desktop { 4 | const char* name_; 5 | int number_; 6 | static Desktop* current_; 7 | public: 8 | static Desktop* first; 9 | Desktop* next; 10 | const char* name() const {return name_;} 11 | void name(const char*); 12 | int number() const {return number_;} 13 | static Desktop* current() {return current_;} 14 | static Desktop* number(int, int create = 0); 15 | static void current(Desktop*); 16 | static int available_number(); 17 | static int max_number(); 18 | Desktop(const char*, int); 19 | ~Desktop(); 20 | int junk; // for temporary storage by menu builder 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /Frame.C: -------------------------------------------------------------------------------- 1 | // Frame.C 2 | // 3 | // CHANGES 4 | // 20090927: Some modifications by Michael A. Losh, tagged "ML" below, 5 | // or bracketed by TOPSIDE manifest constant 6 | // 20160402: some tests clarified (more parentheses ...); dentonlt 7 | // 20190303: Added: DoNotWarp variable to overide mouse cursor warping if desired. 8 | // Added: Double click titlebar to toggle window max size and normal size. 9 | // Compiler Warnings: Moved break statements to their own lines. Rich 10 | 11 | #define FL_INTERNALS 1 12 | 13 | #include "config.h" 14 | #include "Frame.H" 15 | #include "Desktop.H" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #if FL_API_VERSION >= 10400 21 | # if FLTK_USE_CAIRO 22 | # include 23 | # endif 24 | #endif 25 | 26 | #ifndef HAVE_XFT 27 | #include "Rotated.H" // text rotation code; not supported by non-Xft FLTK 1.3.x 28 | #endif 29 | 30 | // From Hotkeys.C 31 | extern void ToggleWinMax(void); 32 | void ToggleVertMax(void); 33 | extern void ToggleHorzMax(void); 34 | 35 | static Atom wm_state = 0; 36 | static Atom wm_change_state; 37 | static Atom wm_protocols; 38 | static Atom wm_delete_window; 39 | static Atom wm_take_focus; 40 | static Atom wm_save_yourself; 41 | static Atom wm_colormap_windows; 42 | static Atom _motif_wm_hints; 43 | static Atom kwm_win_decoration; 44 | #if DESKTOPS 45 | static Atom kwm_win_desktop; 46 | static Atom kwm_win_sticky; 47 | #endif 48 | //static Atom wm_client_leader; 49 | static Atom _wm_quit_app; 50 | 51 | // these are set by initialize in main.C: 52 | Atom _win_hints; 53 | Atom _win_state; 54 | #if DESKTOPS 55 | extern Atom _win_workspace; 56 | #endif 57 | 58 | #ifdef SHOW_CLOCK 59 | extern char clock_buf[]; 60 | extern int clock_alarm_on; 61 | #endif 62 | 63 | // Set by main in main.C: 64 | extern int DoNotWarp; // Used to override mouse pointer warping if environmental variable NOWARP exists. 65 | 66 | static const int XEventMask = 67 | ExposureMask|StructureNotifyMask 68 | |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask 69 | |ButtonPressMask|ButtonReleaseMask 70 | |EnterWindowMask|LeaveWindowMask 71 | |PointerMotionMask|SubstructureRedirectMask|SubstructureNotifyMask; 72 | 73 | extern Fl_Window* Root; 74 | 75 | Frame* Frame::active_; 76 | Frame* Frame::first; 77 | 78 | static inline int max(int a, int b) {return a > b ? a : b;} 79 | static inline int min(int a, int b) {return a < b ? a : b;} 80 | 81 | //////////////////////////////////////////////////////////////// 82 | // The constructor is by far the most complex part, as it collects 83 | // all the scattered pieces of information about the window that 84 | // X has and uses them to initialize the structure, position the 85 | // window, and then finally create it. 86 | 87 | int dont_set_event_mask = 0; // used by FrameWindow 88 | 89 | // "existing" is a pointer to an XWindowAttributes structure that is 90 | // passed for an already-existing window when the window manager is 91 | // starting up. If so we don't want to alter the state, size, or 92 | // position. If null than this is a MapRequest of a new window. 93 | Frame::Frame(XWindow window, XWindowAttributes* existing) : 94 | Fl_Window(0,0), 95 | window_(window), 96 | state_flags_(0), 97 | flags_(0), 98 | transient_for_xid(None), 99 | transient_for_(0), 100 | revert_to(active_), 101 | colormapWinCount(0), 102 | close_button(left,top,ButtonSz,ButtonSz,"X"), 103 | max_w_button(left,top+ButtonSz,ButtonSz,ButtonSz,"w"), 104 | min_w_button(left,top+2*ButtonSz,ButtonSz,ButtonSz,"W"), 105 | max_h_button(left,top+3*ButtonSz,ButtonSz,ButtonSz,"h"), 106 | iconize_button(left,top+4*ButtonSz,ButtonSz,ButtonSz,"i") 107 | { 108 | close_button.callback(button_cb_static); 109 | iconize_button.callback(button_cb_static); 110 | max_h_button.type(FL_TOGGLE_BUTTON); 111 | max_h_button.callback(button_cb_static); 112 | max_w_button.type(FL_TOGGLE_BUTTON); 113 | max_w_button.callback(button_cb_static); 114 | min_w_button.type(FL_TOGGLE_BUTTON); 115 | min_w_button.callback(button_cb_static); 116 | end(); 117 | box(FL_NO_BOX); // relies on background color erasing interior 118 | // ML ------------------------------- 119 | #ifdef ML_TITLEBAR_COLOR 120 | char * titlebar_color_str = getenv("FLWM_TITLEBAR_COLOR"); 121 | int r = 0x90, g = 0x90, b = 0x90; 122 | int fields = 0; 123 | if (titlebar_color_str) { 124 | fields = sscanf(titlebar_color_str, "%02X:%02X:%02X", &r, &g, &b); 125 | } 126 | if (titlebar_color_str && (fields == 3)) { 127 | Fl::set_color(FL_BACKGROUND2_COLOR, r, g, b); 128 | } 129 | else { 130 | Fl::set_color(FL_BACKGROUND2_COLOR, 0x90, 0x90, 0x90); 131 | } 132 | #else 133 | Fl::set_color(FL_BACKGROUND2_COLOR, 0x90, 0x90, 0x90); 134 | #endif 135 | // --------------------------------ML 136 | labelcolor(FL_FOREGROUND_COLOR); 137 | next = first; 138 | first = this; 139 | 140 | // do this asap so we don't miss any events... 141 | if (!dont_set_event_mask) 142 | XSelectInput(fl_display, window_, 143 | ColormapChangeMask | PropertyChangeMask | FocusChangeMask 144 | ); 145 | 146 | if (!wm_state) { 147 | // allocate all the atoms if this is the first time 148 | wm_state = XInternAtom(fl_display, "WM_STATE", 0); 149 | wm_change_state = XInternAtom(fl_display, "WM_CHANGE_STATE", 0); 150 | wm_protocols = XInternAtom(fl_display, "WM_PROTOCOLS", 0); 151 | wm_delete_window = XInternAtom(fl_display, "WM_DELETE_WINDOW", 0); 152 | wm_take_focus = XInternAtom(fl_display, "WM_TAKE_FOCUS", 0); 153 | wm_save_yourself = XInternAtom(fl_display, "WM_SAVE_YOURSELF", 0); 154 | wm_colormap_windows = XInternAtom(fl_display, "WM_COLORMAP_WINDOWS",0); 155 | _motif_wm_hints = XInternAtom(fl_display, "_MOTIF_WM_HINTS", 0); 156 | kwm_win_decoration = XInternAtom(fl_display, "KWM_WIN_DECORATION", 0); 157 | #if DESKTOPS 158 | kwm_win_desktop = XInternAtom(fl_display, "KWM_WIN_DESKTOP", 0); 159 | kwm_win_sticky = XInternAtom(fl_display, "KWM_WIN_STICKY", 0); 160 | #endif 161 | // wm_client_leader = XInternAtom(fl_display, "WM_CLIENT_LEADER", 0); 162 | _wm_quit_app = XInternAtom(fl_display, "_WM_QUIT_APP", 0); 163 | } 164 | 165 | #ifdef TOPSIDE 166 | label_x = label_h = label_w = 0; 167 | #else 168 | label_y = label_h = label_w = 0; 169 | #endif 170 | getLabel(); 171 | // getIconLabel(); 172 | 173 | {XWindowAttributes attr; 174 | if (existing) attr = *existing; 175 | else { 176 | // put in some legal values in case XGetWindowAttributes fails: 177 | attr.x = attr.y = 0; attr.width = attr.height = 100; 178 | attr.colormap = fl_colormap; 179 | attr.border_width = 0; 180 | XGetWindowAttributes(fl_display, window, &attr); 181 | } 182 | left = top = dwidth = dheight = 0; // pretend border is zero-width for now 183 | app_border_width = attr.border_width; 184 | x(attr.x+app_border_width); restore_x = x(); 185 | y(attr.y+app_border_width); restore_y = y(); 186 | w(attr.width); restore_w = w(); 187 | h(attr.height); restore_h = h(); 188 | colormap = attr.colormap;} 189 | 190 | getColormaps(); 191 | 192 | //group_ = 0; 193 | {XWMHints* hints = XGetWMHints(fl_display, window_); 194 | if (hints) { 195 | if ((hints->flags & InputHint) && !hints->input) set_flag(NO_FOCUS); 196 | //if (hints && hints->flags&WindowGroupHint) group_ = hints->window_group; 197 | } 198 | switch (getIntProperty(wm_state, wm_state, 0)) { 199 | case NormalState: 200 | state_ = NORMAL; break; 201 | case IconicState: 202 | state_ = ICONIC; break; 203 | // X also defines obsolete values ZoomState and InactiveState 204 | default: 205 | if ((hints && (hints->flags&StateHint)) && (hints->initial_state==IconicState)) 206 | state_ = ICONIC; 207 | else 208 | state_ = NORMAL; 209 | } 210 | if (hints) XFree(hints);} 211 | // Maya sets this, seems to mean the same as group: 212 | // if (!group_) group_ = getIntProperty(wm_client_leader, XA_WINDOW); 213 | 214 | XGetTransientForHint(fl_display, window_, &transient_for_xid); 215 | 216 | getProtocols(); 217 | 218 | getMotifHints(); 219 | 220 | // get Gnome hints: 221 | int p = getIntProperty(_win_hints, XA_CARDINAL); 222 | if (p&1) set_flag(NO_FOCUS); // WIN_HINTS_SKIP_FOCUS 223 | // if (p&2) // WIN_HINTS_SKIP_WINLIST 224 | // if (p&4) // WIN_HINTS_SKIP_TASKBAR 225 | // if (p&8) ... // WIN_HINTS_GROUP_TRANSIENT 226 | if (p&16) set_flag(CLICK_TO_FOCUS); // WIN_HINTS_FOCUS_ON_CLICK 227 | 228 | // get KDE hints: 229 | p = getIntProperty(kwm_win_decoration, kwm_win_decoration, 1); 230 | if (!(p&3)) set_flag(NO_BORDER); 231 | else if (p & 2) set_flag(THIN_BORDER); 232 | if (p & 256) set_flag(NO_FOCUS); 233 | 234 | fix_transient_for(); 235 | 236 | if (transient_for()) { 237 | if (state_ == NORMAL) state_ = transient_for()->state_; 238 | #if DESKTOPS 239 | desktop_ = transient_for()->desktop_; 240 | #endif 241 | } 242 | #if DESKTOPS 243 | // see if anybody thinks window is "sticky:" 244 | else if ((getIntProperty(_win_state, XA_CARDINAL) & 1) // WIN_STATE_STICKY 245 | || getIntProperty(kwm_win_sticky, kwm_win_sticky)) { 246 | desktop_ = 0; 247 | } else { 248 | // get the desktop from either Gnome or KDE (Gnome takes precedence): 249 | p = getIntProperty(_win_workspace, XA_CARDINAL, -1) + 1; // Gnome desktop 250 | if (p <= 0) p = getIntProperty(kwm_win_desktop, kwm_win_desktop); 251 | if ((p > 0) && (p < 25)) 252 | desktop_ = Desktop::number(p, 1); 253 | else 254 | desktop_ = Desktop::current(); 255 | } 256 | if (desktop_ && (desktop_ != Desktop::current())) 257 | if (state_ == NORMAL) state_ = OTHER_DESKTOP; 258 | #endif 259 | 260 | int autoplace = getSizes(); 261 | // some Motif programs assumme this will force the size to conform :-( 262 | if (w() < min_w || h() < min_h) { 263 | if (w() < min_w) w(min_w); 264 | if (h() < min_h) h(min_h); 265 | XResizeWindow(fl_display, window_, w(), h()); 266 | } 267 | 268 | // try to detect programs that think "transient_for" means "no border": 269 | if (transient_for_xid && !label() && !flag(NO_BORDER)) { 270 | set_flag(THIN_BORDER); 271 | } 272 | updateBorder(); 273 | show_hide_buttons(); 274 | 275 | if (autoplace && !existing && !(transient_for() && (x() || y()))) { 276 | place_window(); 277 | } 278 | 279 | // move window so contents and border are visible: 280 | x(force_x_onscreen(x(), w())); 281 | y(force_y_onscreen(y(), h())); 282 | 283 | // guess some values for the "restore" fields, if already maximized: 284 | if (max_w_button.value()) { 285 | restore_w = min_w + ((w()-dwidth-min_w)/2/inc_w) * inc_w; 286 | restore_x = x()+left + (w()-dwidth-restore_w)/2; 287 | } 288 | if (max_h_button.value()) { 289 | restore_h = min_h + ((h()-dheight-min_h)/2/inc_h) * inc_h; 290 | restore_y = y()+top + (h()-dheight-restore_h)/2; 291 | } 292 | 293 | const int mask = CWBorderPixel | CWColormap | CWEventMask | CWBitGravity 294 | | CWBackPixel | CWOverrideRedirect; 295 | XSetWindowAttributes sattr; 296 | sattr.event_mask = XEventMask; 297 | sattr.colormap = fl_colormap; 298 | sattr.border_pixel = fl_xpixel(FL_GRAY0); 299 | sattr.bit_gravity = NorthWestGravity; 300 | sattr.override_redirect = 1; 301 | sattr.background_pixel = fl_xpixel(FL_GRAY); 302 | Fl_X::set_xid(this, XCreateWindow(fl_display, 303 | RootWindow(fl_display,fl_screen), 304 | x(), y(), w(), h(), 0, 305 | fl_visual->depth, 306 | InputOutput, 307 | fl_visual->visual, 308 | mask, &sattr)); 309 | 310 | setStateProperty(); 311 | 312 | if (!dont_set_event_mask) XAddToSaveSet(fl_display, window_); 313 | if (existing) set_state_flag(IGNORE_UNMAP); 314 | XReparentWindow(fl_display, window_, fl_xid(this), left, top); 315 | XSetWindowBorderWidth(fl_display, window_, 0); 316 | if (state_ == NORMAL) XMapWindow(fl_display, window_); 317 | sendConfigureNotify(); // many apps expect this even if window size unchanged 318 | 319 | #if CLICK_RAISES || CLICK_TO_TYPE 320 | if (!dont_set_event_mask) 321 | XGrabButton(fl_display, AnyButton, AnyModifier, window, False, 322 | ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); 323 | #endif 324 | 325 | if (state_ == NORMAL) { 326 | XMapWindow(fl_display, fl_xid(this)); 327 | if (!existing) activate_if_transient(); 328 | } 329 | set_visible(); 330 | } 331 | 332 | #if SMART_PLACEMENT 333 | // Helper functions for "smart" window placement. 334 | int overlap1(int p1, int l1, int p2, int l2) { 335 | int ret = 0; 336 | if(p1 <= p2 && p2 <= p1 + l1) { 337 | ret = min(p1 + l1 - p2, l2); 338 | } else if (p2 <= p1 && p1 <= p2 + l2) { 339 | ret = min(p2 + l2 - p1, l1); 340 | } 341 | return ret; 342 | } 343 | 344 | int overlap(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { 345 | return (overlap1(x1, w1, x2, w2) * overlap1(y1, h1, y2, h2)); 346 | } 347 | 348 | // Compute the overlap with existing windows. 349 | // For normal windows the overlapping area is taken into account plus a 350 | // constant value for every overlapping window. 351 | // The active window counts twice. 352 | // For iconic windows half the overlapping area is taken into account. 353 | int getOverlap(int x, int y, int w, int h, Frame *first, Frame *self) { 354 | int ret = 0; 355 | short state; 356 | for (Frame* f = first; f; f = f->next) { 357 | if (f != self) { 358 | state = f->state(); 359 | if (state == NORMAL || state == ICONIC) { 360 | int o = overlap(x, y, w, h, f->x(), f->y(), f->w(), f->h()); 361 | if (state == NORMAL) { 362 | ret = ret + o + (o>0?40000:0) + (o * f->active()); 363 | } else if (state == ICONIC) { 364 | ret = ret + o/2; 365 | } 366 | } 367 | } 368 | } 369 | return ret; 370 | } 371 | 372 | // autoplacement (brute force version for now) 373 | void Frame::place_window() { 374 | int min_overlap = -1; 375 | int tmp_x, tmp_y, tmp_o; 376 | int best_x = 0; 377 | int best_y = 0; 378 | int _w = w(); 379 | int _h = h(); 380 | int max_x = Root->x() + Root->w(); 381 | int max_y = Root->y() + Root->h(); 382 | 383 | Frame *f1 = Frame::first; 384 | for(int i=0;; i++) { 385 | if (i==0) { 386 | tmp_x = 0; 387 | } else if (i==1) { 388 | tmp_x = max_x - _w; 389 | } else { 390 | if (f1 == this) { 391 | f1 = f1->next; 392 | } 393 | if (!f1) { 394 | break; 395 | } 396 | tmp_x = f1->x() + f1->w(); 397 | f1 = f1->next; 398 | } 399 | Frame *f2 = Frame::first; 400 | for(int j=0;; j++) { 401 | if (j==0) { 402 | tmp_y = 0; 403 | } else if (j==1) { 404 | tmp_y = max_y - _h; 405 | } else { 406 | if (f2 == this) { 407 | f2 = f2->next; 408 | } 409 | if (!f2) { 410 | break; 411 | } 412 | tmp_y = f2->y() + f2->h(); 413 | f2 = f2->next; 414 | } 415 | 416 | if ((tmp_x + _w <= max_x) && (tmp_y + _h <= max_y)) { 417 | tmp_o = getOverlap(tmp_x, tmp_y, _w, _h, Frame::first, this); 418 | if(tmp_o < min_overlap || min_overlap < 0) { 419 | best_x = tmp_x; 420 | best_y = tmp_y; 421 | min_overlap = tmp_o; 422 | if (min_overlap == 0) { 423 | break; 424 | } 425 | } 426 | } 427 | } 428 | if (min_overlap == 0) { 429 | break; 430 | } 431 | } 432 | x(best_x); 433 | y(best_y); 434 | } 435 | 436 | #else 437 | 438 | // autoplacement (stupid version for now) 439 | void Frame::place_window() { 440 | x(Root->x()+(Root->w()-w())/2); 441 | y(Root->y()+(Root->h()-h())/2); 442 | // move it until it does not hide any existing windows: 443 | const int delta = TITLE_WIDTH+LEFT; 444 | for (Frame* f = next; f; f = f->next) { 445 | if (f->x()+delta > x() && f->y()+delta > y() && 446 | f->x()+f->w()-delta < x()+w() && f->y()+f->h()-delta < y()+h()) { 447 | x(max(x(),f->x()+delta)); 448 | y(max(y(),f->y()+delta)); 449 | f = this; 450 | } 451 | } 452 | } 453 | #endif 454 | 455 | // modify the passed X & W to a legal horizontal window position 456 | int Frame::force_x_onscreen(int X, int W) { 457 | // force all except the black border on-screen: 458 | X = min(X, Root->x()+Root->w()+1-W); 459 | X = max(X, Root->x()-1); 460 | // force the contents on-screen: 461 | X = min(X, Root->x()+Root->w()-W+dwidth-left); 462 | if (W-dwidth > Root->w() || h()-dheight > Root->h()) 463 | // windows bigger than the screen need title bar so they can move 464 | X = max(X, Root->x()-LftSz); 465 | else 466 | X = max(X, Root->x()-left); 467 | return X; 468 | } 469 | 470 | // modify the passed Y & H to a legal vertical window position: 471 | int Frame::force_y_onscreen(int Y, int H) { 472 | // force border (except black edge) to be on-screen: 473 | Y = min(Y, Root->y()+Root->h()+1-H); 474 | Y = max(Y, Root->y()-1); 475 | // force contents to be on-screen: 476 | Y = min(Y, Root->y()+Root->h()-H+dheight-top); 477 | Y = max(Y, Root->y()-top); 478 | return Y; 479 | } 480 | 481 | //////////////////////////////////////////////////////////////// 482 | // destructor 483 | // The destructor is called on DestroyNotify, so I don't have to do anything 484 | // to the contained window, which is already been destroyed. 485 | 486 | Frame::~Frame() { 487 | 488 | // It is possible for the frame to be destroyed while the menu is 489 | // popped-up, and the menu will still contain a pointer to it. To 490 | // fix this the menu checks the state_ location for a legal and 491 | // non-withdrawn state value before doing anything. This should 492 | // be reliable unless something reallocates the memory and writes 493 | // a legal state value to this location: 494 | state_ = UNMAPPED; 495 | 496 | // remove any pointers to this: 497 | Frame** cp; for (cp = &first; *cp; cp = &((*cp)->next)) 498 | if (*cp == this) {*cp = next; break;} 499 | for (Frame* f = first; f; f = f->next) { 500 | if (f->transient_for_ == this) f->transient_for_ = transient_for_; 501 | if (f->revert_to == this) f->revert_to = revert_to; 502 | } 503 | throw_focus(1); 504 | 505 | if (colormapWinCount) { 506 | XFree((char *)colormapWindows); 507 | delete[] window_Colormaps; 508 | } 509 | //if (iconlabel()) XFree((char*)iconlabel()); 510 | if (label()) XFree((char*)label()); 511 | } 512 | 513 | //////////////////////////////////////////////////////////////// 514 | 515 | void Frame::getLabel(int del) { 516 | int clr_h = label_h; 517 | int clr_w = label_w; 518 | char* old = (char*)label(); 519 | char* nu = del ? 0 : (char*)getProperty(XA_WM_NAME); 520 | if (nu) { 521 | // since many window managers print a default label when none is 522 | // given, many programs send spaces to make a blank label. Detect 523 | // this and make it really be blank: 524 | char* c = nu; while (*c == ' ') c++; 525 | if (!*c) {XFree(nu); nu = 0;} 526 | } 527 | if (old) { 528 | if (nu && !strcmp(old,nu)) {XFree(nu); return;} 529 | XFree(old); 530 | } else { 531 | if (!nu) return; 532 | } 533 | Fl_Widget::label(nu); 534 | if (nu) { 535 | fl_font(TITLE_FONT_SLOT, TitleFontSz); 536 | int lbl_pix = fl_width(nu); 537 | #ifdef TOPSIDE 538 | label_h = (int)fl_size() + TopSz + 1; 539 | label_w = lbl_pix + RgtSz; 540 | clr_h = label_h; 541 | clr_w = w() - RgtSz; // clr_pix; 542 | #else 543 | label_w = (int)fl_size() + LftSz + 1; 544 | label_h = lbl_pix + TopSz + BtmSz; 545 | clr_h = h() - BtmSz; 546 | clr_w = label_w; 547 | #endif 548 | } else { 549 | label_h = 0; 550 | label_w = 0; 551 | } 552 | if (shown()) { // && label_h > 3 && left > 3) 553 | XClearArea(fl_display, fl_xid(this), LftSz, TopSz, clr_w, clr_h, 1); 554 | } 555 | } 556 | 557 | //////////////////////////////////////////////////////////////// 558 | 559 | int Frame::getGnomeState(int &) { 560 | // values for _WIN_STATE property are from Gnome WM compliance docs: 561 | #define WIN_STATE_STICKY (1<<0) /*everyone knows sticky*/ 562 | #define WIN_STATE_MINIMIZED (1<<1) /*Reserved - definition is unclear*/ 563 | #define WIN_STATE_MAXIMIZED_VERT (1<<2) /*window in maximized V state*/ 564 | #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /*window in maximized H state*/ 565 | #define WIN_STATE_HIDDEN (1<<4) /*not on taskbar but window visible*/ 566 | #define WIN_STATE_SHADED (1<<5) /*shaded (MacOS / Afterstep style)*/ 567 | #define WIN_STATE_HID_WORKSPACE (1<<6) /*not on current desktop*/ 568 | #define WIN_STATE_HID_TRANSIENT (1<<7) /*owner of transient is hidden*/ 569 | #define WIN_STATE_FIXED_POSITION (1<<8) /*window is fixed in position even*/ 570 | #define WIN_STATE_ARRANGE_IGNORE (1<<9) /*ignore for auto arranging*/ 571 | // nyi 572 | return 0; 573 | } 574 | 575 | //////////////////////////////////////////////////////////////// 576 | 577 | // Read the sizeHints, and try to remove the vast number of mistakes 578 | // that some applications seem to do writing them. 579 | // Returns true if autoplace should be done. 580 | 581 | int Frame::getSizes() { 582 | 583 | XSizeHints sizeHints; 584 | long junk; 585 | if (!XGetWMNormalHints(fl_display, window_, &sizeHints, &junk)) 586 | sizeHints.flags = 0; 587 | 588 | // get the increment, use 1 if none or illegal values: 589 | if (sizeHints.flags & PResizeInc) { 590 | inc_w = sizeHints.width_inc; if (inc_w < 1) inc_w = 1; 591 | inc_h = sizeHints.height_inc; if (inc_h < 1) inc_h = 1; 592 | } else { 593 | inc_w = inc_h = 1; 594 | } 595 | 596 | // get the current size of the window: 597 | int W = w()-dwidth; 598 | int H = h()-dheight; 599 | // I try a lot of places to get a good minimum size value. Lots of 600 | // programs set illegal or junk values, so getting this correct is 601 | // difficult: 602 | min_w = W; 603 | min_h = H; 604 | 605 | // guess a value for minimum size in case it is not set anywhere: 606 | min_w = min(min_w, 4*ButtonSz); 607 | min_w = ((min_w+inc_w-1)/inc_w) * inc_w; 608 | min_h = min(min_h, 4*ButtonSz); 609 | min_h = ((min_h+inc_h-1)/inc_h) * inc_h; 610 | // some programs put the minimum size here: 611 | if (sizeHints.flags & PBaseSize) { 612 | junk = sizeHints.base_width; if (junk > 0) min_w = junk; 613 | junk = sizeHints.base_height; if (junk > 0) min_h = junk; 614 | } 615 | // finally, try the actual place the minimum size should be: 616 | if (sizeHints.flags & PMinSize) { 617 | junk = sizeHints.min_width; if (junk > 0) min_w = junk; 618 | junk = sizeHints.min_height; if (junk > 0) min_h = junk; 619 | } 620 | 621 | max_w = max_h = 0; // default maximum size is "infinity" 622 | if (sizeHints.flags & PMaxSize) { 623 | // Though not defined by ICCCM standard, I interpret any maximum 624 | // size that is less than the minimum to mean "infinity". This 625 | // allows the maximum to be set in one direction only: 626 | junk = sizeHints.max_width; 627 | if (junk >= min_w && junk <= W) max_w = junk; 628 | junk = sizeHints.max_height; 629 | if (junk >= min_h && junk <= H) max_h = junk; 630 | } 631 | 632 | // set the maximize buttons according to current size: 633 | max_w_button.value(W == maximize_width()); 634 | max_h_button.value(H == maximize_height()); 635 | 636 | // Currently only 1x1 aspect works: 637 | if (sizeHints.flags & PAspect 638 | && sizeHints.min_aspect.x == sizeHints.min_aspect.y) 639 | set_flag(KEEP_ASPECT); 640 | 641 | // another fix for gimp, which sets PPosition to 0,0: 642 | if (x() <= 0 && y() <= 0) sizeHints.flags &= ~PPosition; 643 | 644 | return !(sizeHints.flags & (USPosition|PPosition)); 645 | } 646 | 647 | int max_w_switch; 648 | // return width of contents when maximize button pressed: 649 | int Frame::maximize_width() { 650 | int W = max_w_switch; if (!W) W = Root->w(); 651 | #ifdef TOPSIDE 652 | return ((W-min_w)/inc_w) * inc_w + min_w; 653 | #else 654 | return ((W-TITLE_WIDTH-min_w)/inc_w) * inc_w + min_w; 655 | #endif 656 | } 657 | 658 | int max_h_switch; 659 | int Frame::maximize_height() { 660 | int H = max_h_switch; if (!H) H = Root->h(); 661 | #ifdef TOPSIDE 662 | return ((H-TITLE_HEIGHT-min_h)/inc_h) * inc_h + min_h; 663 | #else 664 | return ((H-min_h)/inc_h) * inc_h + min_h; 665 | #endif 666 | } 667 | 668 | //////////////////////////////////////////////////////////////// 669 | 670 | void Frame::getProtocols() { 671 | int n; Atom* p = (Atom*)getProperty(wm_protocols, XA_ATOM, &n); 672 | if (p) { 673 | clear_flag(DELETE_WINDOW_PROTOCOL|TAKE_FOCUS_PROTOCOL|QUIT_PROTOCOL); 674 | for (int i = 0; i < n; ++i) { 675 | if (p[i] == wm_delete_window) { 676 | set_flag(DELETE_WINDOW_PROTOCOL); 677 | } else if (p[i] == wm_take_focus) { 678 | set_flag(TAKE_FOCUS_PROTOCOL); 679 | } else if (p[i] == wm_save_yourself) { 680 | set_flag(SAVE_PROTOCOL); 681 | } else if (p[i] == _wm_quit_app) { 682 | set_flag(QUIT_PROTOCOL); 683 | } 684 | } 685 | } 686 | XFree((char*)p); 687 | } 688 | 689 | //////////////////////////////////////////////////////////////// 690 | 691 | int Frame::getMotifHints() { 692 | long* prop = (long*)getProperty(_motif_wm_hints, _motif_wm_hints); 693 | if (!prop) return 0; 694 | 695 | // see /usr/include/X11/Xm/MwmUtil.h for meaning of these bits... 696 | // prop[0] = flags (what props are specified) 697 | // prop[1] = functions (all, resize, move, minimize, maximize, close, quit) 698 | // prop[2] = decorations (all, border, resize, title, menu, minimize, 699 | // maximize) 700 | // prop[3] = input_mode (modeless, primary application modal, system modal, 701 | // full application modal) 702 | // prop[4] = status (tear-off window) 703 | 704 | // Fill in the default value for missing fields: 705 | if (!(prop[0]&1)) prop[1] = 1; 706 | if (!(prop[0]&2)) prop[2] = 1; 707 | 708 | // The low bit means "turn the marked items off", invert this. 709 | // Transient windows already have size & iconize buttons turned off: 710 | if (prop[1]&1) prop[1] = ~prop[1] & (transient_for_xid ? ~0x58 : -1); 711 | if (prop[2]&1) prop[2] = ~prop[2] & (transient_for_xid ? ~0x60 : -1); 712 | 713 | int old_flags = flags(); 714 | 715 | // see if they are trying to turn off border: 716 | if (!(prop[2])) set_flag(NO_BORDER); else clear_flag(NO_BORDER); 717 | 718 | // see if they are trying to turn off title & close box: 719 | if (!(prop[2]&0x18)) set_flag(THIN_BORDER); else clear_flag(THIN_BORDER); 720 | 721 | // some Motif programs use this to disable resize :-( 722 | // and some programs change this after the window is shown (*&%$#%) 723 | if (!(prop[1]&2) || !(prop[2]&4)) 724 | set_flag(NO_RESIZE); else clear_flag(NO_RESIZE); 725 | 726 | // and some use this to disable the Close function. The commented 727 | // out test is it trying to turn off the mwm menu button: it appears 728 | // programs that do that still expect Alt+F4 to close them, so I 729 | // leave the close on then: 730 | if (!(prop[1]&0x20) /*|| !(prop[2]&0x10)*/) 731 | set_flag(NO_CLOSE); else clear_flag(NO_CLOSE); 732 | 733 | // see if they set "input hint" to non-zero: 734 | // prop[3] should be nonzero but the only example of this I have 735 | // found is Netscape 3.0 and it sets it to zero... 736 | if (!shown() && (prop[0]&4) /*&& prop[3]*/) set_flag(::MODAL); 737 | 738 | // see if it is forcing the iconize button back on. This makes 739 | // transient_for act like group instead... 740 | if ((prop[1]&0x8) || (prop[2]&0x20)) set_flag(ICONIZE); 741 | 742 | // Silly 'ol Amazon paint ignores WM_DELETE_WINDOW and expects to 743 | // get the SGI-specific "_WM_QUIT_APP". It indicates this by trying 744 | // to turn off the close box. SIGH!!! 745 | if (flag(QUIT_PROTOCOL) && !(prop[1]&0x20)) 746 | clear_flag(DELETE_WINDOW_PROTOCOL); 747 | 748 | XFree((char*)prop); 749 | return (flags() ^ old_flags); 750 | } 751 | 752 | //////////////////////////////////////////////////////////////// 753 | 754 | void Frame::getColormaps(void) { 755 | if (colormapWinCount) { 756 | XFree((char *)colormapWindows); 757 | delete[] window_Colormaps; 758 | } 759 | int n; 760 | XWindow* cw = (XWindow*)getProperty(wm_colormap_windows, XA_WINDOW, &n); 761 | if (cw) { 762 | colormapWinCount = n; 763 | colormapWindows = cw; 764 | window_Colormaps = new Colormap[n]; 765 | for (int i = 0; i < n; ++i) { 766 | if (cw[i] == window_) { 767 | window_Colormaps[i] = colormap; 768 | } else { 769 | XWindowAttributes attr; 770 | XSelectInput(fl_display, cw[i], ColormapChangeMask); 771 | XGetWindowAttributes(fl_display, cw[i], &attr); 772 | window_Colormaps[i] = attr.colormap; 773 | } 774 | } 775 | } else { 776 | colormapWinCount = 0; 777 | } 778 | } 779 | 780 | void Frame::installColormap() const { 781 | for (int i = colormapWinCount; i--;) 782 | if (colormapWindows[i] != window_ && window_Colormaps[i]) 783 | XInstallColormap(fl_display, window_Colormaps[i]); 784 | if (colormap) 785 | XInstallColormap(fl_display, colormap); 786 | } 787 | 788 | //////////////////////////////////////////////////////////////// 789 | 790 | // figure out transient_for(), based on the windows that exist, the 791 | // transient_for and group attributes, etc: 792 | void Frame::fix_transient_for() { 793 | Frame* p = 0; 794 | if (transient_for_xid && !flag(ICONIZE)) { 795 | for (Frame* f = first; f; f = f->next) { 796 | if (f != this && f->window_ == transient_for_xid) {p = f; break;} 797 | } 798 | // loops are illegal: 799 | for (Frame* q = p; q; q = q->transient_for_) if (q == this) {p = 0; break;} 800 | } 801 | transient_for_ = p; 802 | } 803 | 804 | int Frame::is_transient_for(const Frame* f) const { 805 | if (f) 806 | for (Frame* p = transient_for(); p; p = p->transient_for()) 807 | if (p == f) return 1; 808 | return 0; 809 | } 810 | 811 | // When a program maps or raises a window, this is called. It guesses 812 | // if this window is in fact a modal window for the currently active 813 | // window and if so transfers the active state to this: 814 | // This also activates new main windows automatically 815 | int Frame::activate_if_transient() { 816 | if (!Fl::pushed()) 817 | if (!transient_for() || is_transient_for(active_)) return activate(1); 818 | return 0; 819 | } 820 | 821 | //////////////////////////////////////////////////////////////// 822 | 823 | int Frame::activate(int warp) { 824 | // see if a modal & newer window is up: 825 | for (Frame* c = first; c && c != this; c = c->next) 826 | if (c->flag(::MODAL) && c->transient_for() == this) 827 | if (c->activate(warp)) return 1; 828 | // ignore invisible windows: 829 | if (state() != NORMAL || w() <= dwidth) return 0; 830 | // always put in the colormap: 831 | installColormap(); 832 | // move the pointer if desired: 833 | // (note that moving the pointer is pretty much required for point-to-type 834 | // unless you know the pointer is already in the window): 835 | if (!warp || Fl::event_state() & (FL_BUTTON1|FL_BUTTON2|FL_BUTTON3)) { 836 | ; 837 | } else if (warp==2 && !DoNotWarp) { 838 | // warp to point at title: 839 | #ifdef TOPSIDE 840 | XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, 841 | min(label_x+label_w/2+1, label_h/2)); 842 | #else 843 | XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, left/2+1, 844 | min(label_y+label_w/2+1,h()/2)); 845 | #endif 846 | } else { 847 | warp_pointer(); 848 | } 849 | // skip windows that don't want focus: 850 | if (flag(NO_FOCUS)) return 0; 851 | // set this even if we think it already has it, this seems to fix 852 | // bugs with Motif popups: 853 | XSetInputFocus(fl_display, window_, RevertToPointerRoot, fl_event_time); 854 | if (active_ != this) { 855 | if (active_) active_->deactivate(); 856 | active_ = this; 857 | //#ifdef ACTIVE_COLOR 858 | // XSetWindowAttributes a; 859 | // a.background_pixel = fl_xpixel(FL_SELECTION_COLOR); 860 | // XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 861 | // labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND_COLOR)); // ML changed color comparison 862 | // XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 863 | //#else 864 | XSetWindowAttributes a; 865 | a.background_pixel = fl_xpixel(FL_BACKGROUND2_COLOR); 866 | XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 867 | labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); // ML changed color comparison 868 | XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 869 | #ifdef SHOW_CLOCK 870 | redraw(); 871 | #endif 872 | //#endif 873 | if (flag(TAKE_FOCUS_PROTOCOL)) 874 | sendMessage(wm_protocols, wm_take_focus); 875 | } 876 | return 1; 877 | } 878 | 879 | // this private function should only be called by constructor and if 880 | // the window is active(): 881 | void Frame::deactivate() { 882 | #ifdef ACTIVE_COLOR 883 | XSetWindowAttributes a; 884 | a.background_pixel = fl_xpixel(FL_GRAY); 885 | XChangeWindowAttributes(fl_display, fl_xid(this), CWBackPixel, &a); 886 | labelcolor(FL_FOREGROUND_COLOR); 887 | XClearArea(fl_display, fl_xid(this), 2, 2, w()-4, h()-4, 1); 888 | #else 889 | #ifdef SHOW_CLOCK 890 | redraw(); 891 | #endif 892 | #endif 893 | } 894 | 895 | #if CLICK_RAISES || CLICK_TO_TYPE 896 | // After the XGrabButton, the main loop will get the mouse clicks, and 897 | // it will call here when it gets them: 898 | void click_raise(Frame* f) { 899 | f->activate(); 900 | #if CLICK_RAISES 901 | if (fl_xevent->xbutton.button <= 1) f->raise(); 902 | #endif 903 | XAllowEvents(fl_display, ReplayPointer, CurrentTime); 904 | } 905 | #endif 906 | 907 | // get rid of the focus by giving it to somebody, if possible: 908 | void Frame::throw_focus(int destructor) { 909 | if (!active()) return; 910 | if (!destructor) deactivate(); 911 | active_ = 0; 912 | if (revert_to && revert_to->activate()) return; 913 | for (Frame* f = first; f; f = f->next) 914 | if (f != this && f->activate()) return; 915 | } 916 | 917 | //////////////////////////////////////////////////////////////// 918 | 919 | // change the state of the window (this is a private function and 920 | // it ignores the transient-for or desktop information): 921 | 922 | void Frame::state(short newstate) { 923 | short oldstate = state(); 924 | if (newstate == oldstate) return; 925 | state_ = newstate; 926 | switch (newstate) { 927 | case UNMAPPED: 928 | throw_focus(); 929 | XUnmapWindow(fl_display, fl_xid(this)); 930 | //set_state_flag(IGNORE_UNMAP); 931 | //XUnmapWindow(fl_display, window_); 932 | XRemoveFromSaveSet(fl_display, window_); 933 | break; 934 | case NORMAL: 935 | if (oldstate == UNMAPPED) XAddToSaveSet(fl_display, window_); 936 | if (w() > dwidth) XMapWindow(fl_display, window_); 937 | XMapWindow(fl_display, fl_xid(this)); 938 | clear_state_flag(IGNORE_UNMAP); 939 | break; 940 | default: 941 | if (oldstate == UNMAPPED) { 942 | XAddToSaveSet(fl_display, window_); 943 | } else if (oldstate == NORMAL) { 944 | throw_focus(); 945 | XUnmapWindow(fl_display, fl_xid(this)); 946 | //set_state_flag(IGNORE_UNMAP); 947 | //XUnmapWindow(fl_display, window_); 948 | } else { 949 | return; // don't setStateProperty IconicState multiple times 950 | } 951 | break; 952 | } 953 | setStateProperty(); 954 | } 955 | 956 | void Frame::setStateProperty() const { 957 | long data[2]; 958 | switch (state()) { 959 | case UNMAPPED : 960 | data[0] = WithdrawnState; break; 961 | case NORMAL : 962 | case OTHER_DESKTOP : 963 | data[0] = NormalState; break; 964 | default : 965 | data[0] = IconicState; break; 966 | } 967 | data[1] = (long)None; 968 | XChangeProperty(fl_display, window_, wm_state, wm_state, 969 | 32, PropModeReplace, (unsigned char *)data, 2); 970 | } 971 | 972 | //////////////////////////////////////////////////////////////// 973 | // Public state modifiers that move all transient_for(this) children 974 | // with the frame and do the desktops right: 975 | 976 | void Frame::raise() { 977 | Frame* newtop = 0; 978 | Frame* previous = 0; 979 | int previous_state = state_; 980 | Frame** p; 981 | // Find all the transient-for windows and this one, and raise them, 982 | // preserving stacking order: 983 | for (p = &first; *p;) { 984 | Frame* f = *p; 985 | if ((f == this) || (f->is_transient_for(this) && (f->state() != UNMAPPED))) { 986 | *p = f->next; // remove it from list 987 | if (previous) { 988 | XWindowChanges w; 989 | w.sibling = fl_xid(previous); 990 | w.stack_mode = Below; 991 | XConfigureWindow(fl_display, fl_xid(f), CWSibling|CWStackMode, &w); 992 | previous->next = f; 993 | } else { 994 | XRaiseWindow(fl_display, fl_xid(f)); 995 | newtop = f; 996 | } 997 | #if DESKTOPS 998 | if (f->desktop_ && f->desktop_ != Desktop::current()) 999 | f->state(OTHER_DESKTOP); 1000 | else 1001 | #endif 1002 | f->state(NORMAL); 1003 | previous = f; 1004 | } else { 1005 | p = &((*p)->next); 1006 | } 1007 | } 1008 | previous->next = first; 1009 | first = newtop; 1010 | #if DESKTOPS 1011 | if (!transient_for() && desktop_ && desktop_ != Desktop::current()) { 1012 | // for main windows we also must move to the current desktop 1013 | desktop(Desktop::current()); 1014 | } 1015 | #endif 1016 | if (previous_state != NORMAL && newtop->state_==NORMAL) 1017 | newtop->activate_if_transient(); 1018 | } 1019 | 1020 | void Frame::lower() { 1021 | Frame* t = transient_for(); if (t) t->lower(); 1022 | if (!next || next == t) return; // already on bottom 1023 | // pull it out of the list: 1024 | Frame** p = &first; 1025 | for (; *p != this; p = &((*p)->next)) {} 1026 | *p = next; 1027 | // find end of list: 1028 | Frame* f = next; while (f->next != t) f = f->next; 1029 | // insert it after that: 1030 | f->next = this; next = t; 1031 | // and move the X window: 1032 | XWindowChanges w; 1033 | w.sibling = fl_xid(f); 1034 | w.stack_mode = Below; 1035 | XConfigureWindow(fl_display, fl_xid(this), CWSibling|CWStackMode, &w); 1036 | } 1037 | 1038 | void Frame::iconize() { 1039 | for (Frame* c = first; c; c = c->next) { 1040 | if ((c == this) || (c->is_transient_for(this) && (c->state() != UNMAPPED))) 1041 | c->state(ICONIC); 1042 | } 1043 | } 1044 | 1045 | #if DESKTOPS 1046 | void Frame::desktop(Desktop* d) { 1047 | if (d == desktop_) return; 1048 | // Put all the relatives onto the desktop as well: 1049 | for (Frame* c = first; c; c = c->next) { 1050 | if (c == this || c->is_transient_for(this)) { 1051 | c->desktop_ = d; 1052 | c->setProperty(_win_state, XA_CARDINAL, !d); 1053 | c->setProperty(kwm_win_sticky, kwm_win_sticky, !d); 1054 | if (d) { 1055 | c->setProperty(kwm_win_desktop, kwm_win_desktop, d->number()); 1056 | c->setProperty(_win_workspace, XA_CARDINAL, d->number()-1); 1057 | } 1058 | if (!d || d == Desktop::current()) { 1059 | if (c->state() == OTHER_DESKTOP) c->state(NORMAL); 1060 | } else { 1061 | if (c->state() == NORMAL) c->state(OTHER_DESKTOP); 1062 | } 1063 | } 1064 | } 1065 | } 1066 | #endif 1067 | 1068 | //////////////////////////////////////////////////////////////// 1069 | 1070 | // Resize and/or move the window. The size is given for the frame, not 1071 | // the contents. This also sets the buttons on/off as needed: 1072 | 1073 | void Frame::set_size(int nx, int ny, int nw, int nh, int warp) { 1074 | int dx = nx-x(); x(nx); 1075 | int dy = ny-y(); y(ny); 1076 | if (!dx && !dy && nw == w() && nh == h()) return; 1077 | int unmap = 0; 1078 | int remap = 0; 1079 | // use XClearArea to cause correct damage events: 1080 | if (nw != w()) { 1081 | max_w_button.value(nw-dwidth == maximize_width()); 1082 | min_w_button.value(nw <= dwidth); 1083 | if (nw <= dwidth) { 1084 | unmap = 1; 1085 | } else { 1086 | if (w() <= dwidth) remap = 1; 1087 | } 1088 | int minw = (nw < w()) ? nw : w(); 1089 | XClearArea(fl_display, fl_xid(this), minw-RIGHT, 0, RIGHT, nh, 1); 1090 | w(nw); 1091 | //#ifdef TOPSIDE 1092 | // show_hide_buttons(); 1093 | //#endif 1094 | } 1095 | if (nh != h()) { 1096 | max_h_button.value(nh-dheight == maximize_height()); 1097 | #ifdef TOPSIDE 1098 | // min_w_button is used for rollup windowshade effect 1099 | min_w_button.value(nh <= dheight); 1100 | if (nh <= dheight) { 1101 | unmap = 1; 1102 | } else { 1103 | if (h() <= dheight) remap = 1; 1104 | } 1105 | #endif 1106 | 1107 | int minh = (nh < h()) ? nh : h(); 1108 | XClearArea(fl_display, fl_xid(this), 0, minh-BOTTOM, w(), BOTTOM, 1); 1109 | // see if label or close box moved, erase the minimum area: 1110 | // int old_label_y = label_y; 1111 | // int old_label_h = label_h; 1112 | h(nh); 1113 | #if 1 //def SHOW_CLOCK 1114 | #ifdef TOPSIDE 1115 | XClearArea(fl_display,fl_xid(this), label_x, BUTTON_TOP, label_x + label_w, 1116 | BUTTON_H, 1); // ML 1117 | #else 1118 | //int t = label_y + 3; // we have to clear the entire label area 1119 | XClearArea(fl_display,fl_xid(this), 1, label_y, left-1, nh-label_y, 1); // ML 1120 | #endif 1121 | #else 1122 | int t = nh; 1123 | if (label_y != old_label_y) { 1124 | t = label_y; if (old_label_y < t) t = old_label_y; 1125 | } else if (label_y+label_h != old_label_y+old_label_h) { 1126 | t = label_y+label_h; 1127 | if (old_label_y+old_label_h < t) t = old_label_y+old_label_h; 1128 | } 1129 | #endif 1130 | } 1131 | show_hide_buttons(); 1132 | // for the maximize button, move the cursor first if window gets smaller 1133 | if (warp == 1 && !DoNotWarp && (dx || dy)) 1134 | XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy); 1135 | // for configure request, move the cursor first 1136 | if (warp == 2 && active() && !Fl::pushed()) warp_pointer(); 1137 | if (ny < 0) ny = 0; 1138 | if (nh < 8) nh = 8; 1139 | 1140 | XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh); 1141 | #if FL_API_VERSION >= 10400 1142 | # if FLTK_USE_CAIRO 1143 | make_current(); 1144 | if (fl_cairo_gc()) cairo_xlib_surface_set_size(cairo_get_target(fl_cairo_gc()), nw, nh); 1145 | # endif 1146 | #endif 1147 | if (nw <= dwidth || nh <= dheight) { 1148 | if (unmap) { 1149 | set_state_flag(IGNORE_UNMAP); 1150 | XUnmapWindow(fl_display, window_); 1151 | } 1152 | } else { 1153 | XResizeWindow(fl_display, window_, nw-dwidth, nh-dheight); 1154 | if (remap) { 1155 | XMapWindow(fl_display, window_); 1156 | #if CLICK_TO_TYPE 1157 | if (active()) activate(); 1158 | #else 1159 | activate(); 1160 | #endif 1161 | } 1162 | } 1163 | // for maximize button move the cursor second if window gets bigger: 1164 | if (warp == 3 && !DoNotWarp && (dx || dy)) 1165 | XWarpPointer(fl_display, None,None,0,0,0,0, dx, dy); 1166 | if (nw > dwidth) sendConfigureNotify(); 1167 | XSync(fl_display,0); 1168 | } 1169 | 1170 | void Frame::sendConfigureNotify() const { 1171 | XConfigureEvent ce; 1172 | ce.type = ConfigureNotify; 1173 | ce.event = window_; 1174 | ce.window = window_; 1175 | ce.x = x()+left-app_border_width; 1176 | ce.y = y()+top-app_border_width; 1177 | ce.width = w()-dwidth; 1178 | ce.height = h()-dheight; 1179 | if (ce.height < 8) ce.height = 8; 1180 | ce.border_width = app_border_width; 1181 | ce.above = None; 1182 | ce.override_redirect = 0; 1183 | XSendEvent(fl_display, window_, False, StructureNotifyMask, (XEvent*)&ce); 1184 | } 1185 | 1186 | // move the pointer inside the window: 1187 | void Frame::warp_pointer() { 1188 | if(DoNotWarp) return; 1189 | int X,Y; Fl::get_mouse(X,Y); 1190 | X -= x(); 1191 | int Xi = X; 1192 | if (X <= 0) X = left/2+1; 1193 | if (X >= w()) X = w()-(RIGHT/2+1); 1194 | Y -= y(); 1195 | int Yi = Y; 1196 | if (Y < 0) Y = TOP/2+1; 1197 | if (Y >= h()) Y = h()-(BOTTOM/2+1); 1198 | if (X != Xi || Y != Yi) 1199 | XWarpPointer(fl_display, None, fl_xid(this), 0,0,0,0, X, Y); 1200 | } 1201 | 1202 | // Resize the frame to match the current border type: 1203 | void Frame::updateBorder() { 1204 | int nx = x()+left; 1205 | int ny = y()+top; 1206 | int nw = w()-dwidth; 1207 | int nh = h()-dheight; 1208 | if (flag(NO_BORDER)) { 1209 | left = top = dwidth = dheight = 0; 1210 | } else { 1211 | #ifdef TOPSIDE 1212 | left = LftSz; 1213 | dwidth = left+RgtSz; 1214 | top = flag(THIN_BORDER) ? TopSz : TopSz+TitleSz; 1215 | dheight = top+BtmSz; 1216 | #else 1217 | left = flag(THIN_BORDER) ? LftSz : LftSz+TitleSz; 1218 | dwidth = left+RgtSz; 1219 | top = TopSz; 1220 | dheight = top+BtmSz; 1221 | #endif 1222 | } 1223 | nx -= left; 1224 | ny -= top; 1225 | nw += dwidth; 1226 | nh += dheight; 1227 | if (x()==nx && y()==ny && w()==nw && h()==nh) return; 1228 | x(nx); y(ny); w(nw); h(nh); 1229 | if (!shown()) return; // this is so constructor can call this 1230 | // try to make the contents not move while the border changes around it: 1231 | XSetWindowAttributes a; 1232 | a.win_gravity = StaticGravity; 1233 | XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a); 1234 | XMoveResizeWindow(fl_display, fl_xid(this), nx, ny, nw, nh); 1235 | a.win_gravity = NorthWestGravity; 1236 | XChangeWindowAttributes(fl_display, window_, CWWinGravity, &a); 1237 | // fix the window position if the X server didn't do the gravity: 1238 | XMoveWindow(fl_display, window_, left, top); 1239 | } 1240 | 1241 | // position and show the buttons according to current border, size, 1242 | // and other state information: 1243 | #ifdef TOPSIDE 1244 | // FIRST, the TOPSIDE VERSION of the function 1245 | void Frame::show_hide_buttons() { 1246 | if (flag(THIN_BORDER|NO_BORDER)) { 1247 | iconize_button.hide(); 1248 | max_w_button.hide(); 1249 | min_w_button.hide(); 1250 | max_h_button.hide(); 1251 | close_button.hide(); 1252 | return; 1253 | } 1254 | int bx = left; 1255 | int lbl_pix = fl_width(label()); 1256 | label_w = lbl_pix + LftSz; 1257 | 1258 | // RS: ICONIZE BUTTON IS TOP LEFT -- 1259 | if (transient_for() || label_w > (w() - (3*ButtonSz + LftSz + 2*RgtSz))) { 1260 | // don't show iconize button for "transient" (e.g. dialog box) windows 1261 | iconize_button.hide(); 1262 | label_x = 2*LftSz; 1263 | } else { 1264 | iconize_button.show(); 1265 | iconize_button.position(left, TopSz); 1266 | } 1267 | // ML: OTHER BUTTONS ARE PLACED IN UPPER RIGHT 1268 | bx = w() - RgtSz - ButtonSz; 1269 | 1270 | // RS: FIRST, PLACE CLOSE BUTTON FARTHEST INTO UPPER RIGHT 1271 | #if CLOSE_BOX 1272 | if (flag(NO_CLOSE)) { 1273 | #endif 1274 | close_button.hide(); 1275 | #if CLOSE_BOX 1276 | } else { 1277 | close_button.show(); 1278 | close_button.position(bx, TopSz); 1279 | bx -= ButtonSz; 1280 | } 1281 | #endif 1282 | if (transient_for() || label_w > (w() - (2*ButtonSz + LftSz + 2*RgtSz))) { 1283 | // don't show resize and minimize buttons for "transient" (e.g. dialog box) windows 1284 | min_w_button.hide(); 1285 | } else { 1286 | min_w_button.position(bx, TopSz); 1287 | min_w_button.show(); 1288 | bx -= ButtonSz; 1289 | } 1290 | 1291 | if (transient_for() || flag(KEEP_ASPECT|NO_RESIZE) || 1292 | label_w > (w() - (4*ButtonSz + LftSz + 2*RgtSz))) { 1293 | max_h_button.hide(); 1294 | } else { 1295 | max_h_button.position(bx, TopSz); 1296 | max_h_button.show(); 1297 | bx -= ButtonSz; 1298 | } 1299 | 1300 | if (transient_for() || flag(KEEP_ASPECT|NO_RESIZE) || 1301 | label_w > (w() - (5*ButtonSz + LftSz + 2*RgtSz))) { 1302 | max_w_button.hide(); 1303 | } else { 1304 | max_w_button.position(bx, TopSz); 1305 | max_w_button.show(); 1306 | bx -= ButtonSz; 1307 | } 1308 | 1309 | if (label_x != bx && shown()) 1310 | //ML Buttons look garbled after expanding, so let's just clear the whole area 1311 | XClearArea(fl_display,fl_xid(this), LftSz, TopSz, w() - LftSz, TitleSz, 1); 1312 | label_x = LftSz + ButtonSz + 2*RgtSz; 1313 | if (label_w > (w() - (LftSz + 3*ButtonSz + 2*RgtSz))) { 1314 | label_x = LftSz + RgtSz; // move to where iconize button would be 1315 | } 1316 | 1317 | } 1318 | 1319 | #else // have a left-side titlebar version 1320 | 1321 | void Frame::show_hide_buttons() { 1322 | if (flag(THIN_BORDER|NO_BORDER)) { 1323 | iconize_button.hide(); 1324 | max_w_button.hide(); 1325 | min_w_button.hide(); 1326 | max_h_button.hide(); 1327 | close_button.hide(); 1328 | return; 1329 | } 1330 | int by = top; 1331 | 1332 | // ML CLOSE BUTTON IS NOW TOP LEFT -- 1333 | #if CLOSE_BOX 1334 | if (flag(NO_CLOSE)) { 1335 | #endif 1336 | close_button.hide(); 1337 | #if CLOSE_BOX 1338 | } else { 1339 | close_button.show(); 1340 | close_button.position(LftSz,by); 1341 | by += ButtonSz; 1342 | } 1343 | #endif 1344 | int lbl_pix = fl_width(label()); 1345 | label_h = lbl_pix + TopSz + BtmSz; 1346 | if (label_h > (h() - (ButtonSz + TopSz + 2*BtmSz))) { 1347 | label_h = h() - (ButtonSz + TopSz + 2*BtmSz); // clip to whatever we can still squeeze in 1348 | } 1349 | if (transient_for() || 1350 | (h() < (label_h + 2*ButtonSz + TopSz + 2*BtmSz))) { 1351 | min_w_button.hide(); 1352 | } 1353 | else { 1354 | min_w_button.position(LftSz,by); 1355 | min_w_button.show(); 1356 | by += ButtonSz; 1357 | } 1358 | if ((min_h == max_h) || flag(KEEP_ASPECT|NO_RESIZE) || 1359 | (h() < (label_h + 4*ButtonSz + TopSz + 2*BtmSz)) || 1360 | (!max_h_button.value() && ((by+label_w+2*BUTTON_H) > (h()-BUTTON_BOTTOM)))) { 1361 | max_h_button.hide(); 1362 | } else { 1363 | max_h_button.position(LftSz,by); 1364 | max_h_button.show(); 1365 | by += ButtonSz; 1366 | } 1367 | if ( (min_w == max_w) || flag(KEEP_ASPECT|NO_RESIZE) || 1368 | (h() < (label_h + 5*ButtonSz + TopSz + 2*BtmSz)) || 1369 | (!max_w_button.value() && ((by+label_w+2*BUTTON_H > h()-BUTTON_BOTTOM)))) { 1370 | max_w_button.hide(); 1371 | } else { 1372 | max_w_button.position(LftSz,by); 1373 | max_w_button.show(); 1374 | by += ButtonSz; 1375 | } 1376 | if (label_y != by && shown()) 1377 | //ML Buttons look garbled after expanding, so let's just clear the whole area 1378 | XClearArea(fl_display,fl_xid(this), 1, 1, left-1, h()-1, 1); 1379 | label_y = by; 1380 | 1381 | //ML MOVED ICONIZE BUTTON TO BOTTOM -- 1382 | if (by+ButtonSz > h()-BtmSz || transient_for() || (h() < (label_h + 3*ButtonSz + TopSz + 2*BtmSz))) { 1383 | label_y = h()-TopSz-2*BtmSz-label_h; 1384 | iconize_button.hide(); 1385 | } else { 1386 | iconize_button.show(); 1387 | iconize_button.position(LftSz,h()-(BtmSz+ButtonSz+TopSz)); 1388 | } 1389 | // -- END ML 1390 | 1391 | } 1392 | #endif 1393 | 1394 | // make sure fltk does not try to set the window size: 1395 | void Frame::resize(int, int, int, int) {} 1396 | // For fltk2.0: 1397 | void Frame::layout() { 1398 | } 1399 | 1400 | //////////////////////////////////////////////////////////////// 1401 | 1402 | void Frame::close() { 1403 | if (flag(DELETE_WINDOW_PROTOCOL)) 1404 | sendMessage(wm_protocols, wm_delete_window); 1405 | else if (flag(QUIT_PROTOCOL)) 1406 | sendMessage(wm_protocols, _wm_quit_app); 1407 | else 1408 | kill(); 1409 | } 1410 | 1411 | void Frame::kill() { 1412 | XKillClient(fl_display, window_); 1413 | } 1414 | 1415 | // this is called when window manager exits: 1416 | void Frame::save_protocol() { 1417 | Frame* f; 1418 | for (f = first; f; f = f->next) if (f->flag(SAVE_PROTOCOL)) { 1419 | f->set_state_flag(SAVE_PROTOCOL_WAIT); 1420 | f->sendMessage(wm_protocols, wm_save_yourself); 1421 | } 1422 | double t = 10.0; // number of seconds to wait before giving up 1423 | while (t > 0.0) { 1424 | for (f = first; ; f = f->next) { 1425 | if (!f) return; 1426 | if (f->flag(SAVE_PROTOCOL) && f->state_flags_&SAVE_PROTOCOL_WAIT) break; 1427 | } 1428 | t = Fl::wait(t); 1429 | } 1430 | } 1431 | 1432 | //////////////////////////////////////////////////////////////// 1433 | // Drawing code: 1434 | 1435 | #ifdef TOPSIDE 1436 | void Frame::draw() { 1437 | if (flag(NO_BORDER)) return; 1438 | //ML--------------------- Paint opaque titlebar background 1439 | labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); 1440 | if (active()) { 1441 | fl_rectf(2, 2, w() - 4, h()-4, 1442 | fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6) 1443 | ); 1444 | } 1445 | else { 1446 | fl_rectf(2, 2, w() - 4, h()-4, 1447 | FL_BACKGROUND2_COLOR 1448 | ); 1449 | } 1450 | // ------------------ML 1451 | 1452 | if (damage() != FL_DAMAGE_CHILD) { 1453 | 1454 | #ifdef ACTIVE_COLOR 1455 | fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h()); 1456 | if (active()) { 1457 | fl_color(FL_GRAY_RAMP+('N'-'A')); 1458 | fl_xyline(2, h()-3, w()-3, 2); 1459 | } 1460 | #else 1461 | fl_frame("AAAAWWJJTTNN",0,0,w(),h()); 1462 | #endif 1463 | if (!flag(THIN_BORDER) && label_h > 3) { 1464 | fl_color(labelcolor()); 1465 | fl_font(TITLE_FONT_SLOT, TitleFontSz); 1466 | fl_draw(label(), label_x, TopSz, label_w, ButtonSz, 1467 | Fl_Align(FL_ALIGN_LEFT|FL_ALIGN_CLIP), 0, 0); 1468 | } 1469 | } 1470 | if (!flag(THIN_BORDER)) Fl_Window::draw(); 1471 | } 1472 | #else 1473 | void Frame::draw() { 1474 | if (flag(NO_BORDER)) return; 1475 | //ML--------------------- Paint opaque titlebar background 1476 | labelcolor(fl_contrast(FL_FOREGROUND_COLOR, FL_BACKGROUND2_COLOR)); 1477 | if (active()) { 1478 | fl_rectf(2, 2, w() - 4, h()-4, 1479 | fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6) ); // ML 1480 | } 1481 | else { 1482 | fl_rectf(2, 2, w() - 4, h()-4, FL_BACKGROUND2_COLOR); // ML 1483 | } 1484 | // ------------------ML 1485 | 1486 | if (damage() != FL_DAMAGE_CHILD) { 1487 | #ifdef ACTIVE_COLOR 1488 | fl_frame2(active() ? "AAAAJJWW" : "AAAAJJWWNNTT",0,0,w(),h()); 1489 | if (active()) { 1490 | fl_color(FL_GRAY_RAMP+('N'-'A')); 1491 | fl_xyline(2, h()-3, w()-3, 2); 1492 | } 1493 | #else 1494 | fl_frame("AAAAWWJJTTNN",0,0,w(),h()); 1495 | #endif 1496 | if (!flag(THIN_BORDER) && label_h > 3) { 1497 | fl_push_clip(1, label_y, left, label_h); 1498 | #ifdef SHOW_CLOCK 1499 | if (active()) { 1500 | int clkw = int(fl_width(clock_buf)); 1501 | if (clock_alarm_on) { 1502 | fl_font(TITLE_FONT_SLOT, TitleFontSz); 1503 | fl_rectf(LftSz-1, label_y + label_h - 3 - clkw, TitleSz, clkw, 1504 | (ALARM_BG_COLOR>>16)&0xff, 1505 | (ALARM_BG_COLOR>>8)&0xff, 1506 | ALARM_BG_COLOR&0xff); 1507 | fl_color((ALARM_FG_COLOR>>16)&0xff, 1508 | (ALARM_FG_COLOR>>8)&0xff, 1509 | ALARM_FG_COLOR&0xff); 1510 | } else 1511 | fl_font(MENU_FONT_SLOT, TitleFontSz); 1512 | 1513 | #ifndef HAVE_XFT 1514 | // This might overlay the label if the label is long enough 1515 | // and the window height is short enough. For now, we'll 1516 | // assume this is not enough of a problem to be concerned 1517 | // about. 1518 | draw_rotated90(clock_buf, 1, label_y+3, left-1, label_h-6, 1519 | Fl_Align(FL_ALIGN_BOTTOM|FL_ALIGN_CLIP)); 1520 | #else 1521 | fl_draw(90, clock_buf, (left + fl_height() + 1)/2 - fl_descent(), 1522 | label_y + label_h - 3); 1523 | #endif 1524 | } else 1525 | // Only show the clock on the active frame. 1526 | XClearArea(fl_display, fl_xid(this), 1, label_y+3, 1527 | left-1, label_h-3, 0); 1528 | #endif 1529 | fl_color(labelcolor()); 1530 | fl_font(TITLE_FONT_SLOT, TitleFontSz); 1531 | if(label() && *label()) 1532 | #ifndef HAVE_XFT 1533 | draw_rotated90(label(), 1, label_y+3, left-1, label_h-3, 1534 | Fl_Align(FL_ALIGN_TOP|FL_ALIGN_CLIP)); 1535 | #else 1536 | fl_draw(90, label(), (left + fl_height() + 1)/2 - fl_descent(), 1537 | label_y + +3 + fl_width(label())); 1538 | #endif 1539 | fl_pop_clip(); 1540 | } 1541 | } 1542 | if (!flag(THIN_BORDER)) Fl_Window::draw(); 1543 | } 1544 | #endif 1545 | 1546 | #ifdef SHOW_CLOCK 1547 | void Frame::redraw_clock() { 1548 | double clkw = fl_width(clock_buf); 1549 | XClearArea(fl_display, fl_xid(this), 1550 | 1, label_y+label_h-3-(int)clkw, 1551 | left-1, (int)clkw, 1); 1552 | } 1553 | #endif 1554 | 1555 | void FrameButton::draw() { 1556 | const int x = this->x(); 1557 | const int y = this->y(); 1558 | Fl_Widget::draw_box(value() ? FL_DOWN_FRAME : FL_UP_FRAME, 1559 | value() ? fl_darker(FL_BACKGROUND2_COLOR) 1560 | : fl_color_average(FL_BACKGROUND2_COLOR, FL_WHITE, 0.6)); // ML 1561 | fl_line_style(FL_SOLID, (2.0 * sf + 0.6)); 1562 | fl_color(parent()->labelcolor()); 1563 | int m = (min(w(),h()) + 1) / 2; 1564 | int d = min(w(),h()) * 3 / 10; 1565 | switch (label()[0]) { 1566 | case 'W': 1567 | #ifdef TOPSIDE 1568 | fl_line (x+3, y+5, x+w()-5, y+5); 1569 | #else 1570 | fl_rect(x+(h()-7)/2,y+3,2,h()-6); 1571 | #endif 1572 | break; 1573 | case 'w': 1574 | fl_rect(x+3,y+(h()-7)/2,w()-5,7); 1575 | break; 1576 | case 'h': 1577 | fl_rect(x+(h()-7)/2,y+3,7,h()-5); 1578 | break; 1579 | case 'X': 1580 | #if CLOSE_X 1581 | fl_line(x+m-d-1,y+m-d-1,x+m+d,y+m+d-1); 1582 | fl_line(x+m-d-1,y+m+d-1,x+m+d,y+m-d-1); 1583 | 1584 | #endif 1585 | #if CLOSE_HITTITE_LIGHTNING 1586 | fl_arc(x+3,y+3,w()-6,h()-6,0,360); 1587 | fl_line(x+7,y+3, x+7,y+11); 1588 | #endif 1589 | break; 1590 | case 'i': 1591 | #if ICONIZE_BOX 1592 | fl_line (x+3, y+(h()-4), x+w()-4, y+h()-4); 1593 | #endif 1594 | break; 1595 | } 1596 | fl_line_style(FL_SOLID, 1); 1597 | } 1598 | 1599 | //////////////////////////////////////////////////////////////// 1600 | // User interface code: 1601 | 1602 | // this is called when user clicks the buttons: 1603 | void Frame::button_cb(Fl_Button* b) { 1604 | switch (b->label()[0]) { 1605 | case 'W': // minimize button 1606 | if (b->value()) { 1607 | #ifdef TOPSIDE 1608 | if (!max_h_button.value()) { 1609 | #else 1610 | if (!max_w_button.value()) { 1611 | #endif 1612 | restore_x = x(); //+left; 1613 | restore_y = y(); // +top; 1614 | #if MINIMIZE_HEIGHT 1615 | restore_w = w(); //-dwidth; 1616 | restore_h = h(); //-dheight; 1617 | #endif 1618 | } 1619 | 1620 | #ifdef TOPSIDE 1621 | #if MINIMIZE_HEIGHT // minimize rollup/windowblind size 1622 | set_size(x(), y(), min(w(),label_w+5*ButtonSz+2*LftSz+2*RgtSz), dheight-3, 1); 1623 | #else // don't minimize rollup/windowblind size 1624 | set_size(x(), y(), w(), dheight-3, 1); 1625 | #endif 1626 | #else // not TOPSIDE 1627 | #if MINIMIZE_HEIGHT // minimize rollup/windowblind size 1628 | set_size(x(), y(), dwidth-1, min(h(),label_h+5*ButtonSz+2*TopSz+2*BtmSz), 1); 1629 | #else // don't minimize rollup/windowblind size 1630 | set_size(x(), y(), dwidth-1, h(), 1); 1631 | #endif 1632 | #endif 1633 | } else { 1634 | #if MINIMIZE_HEIGHT 1635 | set_size(restore_x, restore_y, restore_w+dwidth, restore_h+dwidth, 1); 1636 | #else 1637 | #ifdef TOPSIDE 1638 | set_size(restore_x, restore_y, w(), h(), 1); 1639 | #else // not TOPSIDE 1640 | set_size(restore_x, restore_y, restore_w+dwidth, h(), 1); 1641 | #endif 1642 | #endif 1643 | } 1644 | show_hide_buttons(); 1645 | break; 1646 | case 'w': // max-width button 1647 | ToggleHorzMax(); 1648 | show_hide_buttons(); 1649 | break; 1650 | case 'h': // max-height button 1651 | ToggleVertMax(); 1652 | show_hide_buttons(); 1653 | break; 1654 | case 'X': 1655 | close(); 1656 | break; 1657 | default: // iconize button 1658 | iconize(); 1659 | break; 1660 | } 1661 | } 1662 | 1663 | // static callback for fltk: 1664 | void Frame::button_cb_static(Fl_Widget* w, void*) { 1665 | ((Frame*)(w->parent()))->button_cb((Fl_Button*)w); 1666 | } 1667 | 1668 | // This method figures out what way the mouse will resize the window. 1669 | // It is used to set the cursor and to actually control what you grab. 1670 | // If the window cannot be resized in some direction this should not 1671 | // return that direction. 1672 | int Frame::mouse_location() { 1673 | int x = Fl::event_x(); 1674 | int y = Fl::event_y(); 1675 | int r = 0; 1676 | if (flag(NO_RESIZE)) return 0; 1677 | if (min_h != max_h) { 1678 | if (y < RESIZE_EDGE) r |= FL_ALIGN_TOP; 1679 | else if (y >= h()-RESIZE_EDGE) r |= FL_ALIGN_BOTTOM; 1680 | } 1681 | if (min_w != max_w) { 1682 | #if RESIZE_LEFT 1683 | if (x < RESIZE_EDGE) r |= FL_ALIGN_LEFT; 1684 | #else 1685 | if (x < RESIZE_EDGE && r) r |= FL_ALIGN_LEFT; 1686 | #endif 1687 | else if (x >= w()-RESIZE_EDGE) r |= FL_ALIGN_RIGHT; 1688 | } 1689 | return r; 1690 | } 1691 | 1692 | // set the cursor correctly for a return value from mouse_location(): 1693 | void Frame::set_cursor(int r) { 1694 | // Fl_Cursor c = r ? FL_CURSOR_ARROW : FL_CURSOR_MOVE; 1695 | Fl_Cursor c = r ? FL_CURSOR_ARROW : FL_CURSOR_ARROW; 1696 | switch (r) { 1697 | case FL_ALIGN_TOP: 1698 | case FL_ALIGN_BOTTOM: 1699 | c = FL_CURSOR_NS; 1700 | break; 1701 | case FL_ALIGN_LEFT: 1702 | case FL_ALIGN_RIGHT: 1703 | c = FL_CURSOR_WE; 1704 | break; 1705 | case FL_ALIGN_LEFT|FL_ALIGN_TOP: 1706 | case FL_ALIGN_RIGHT|FL_ALIGN_BOTTOM: 1707 | c = FL_CURSOR_NWSE; 1708 | break; 1709 | case FL_ALIGN_LEFT|FL_ALIGN_BOTTOM: 1710 | case FL_ALIGN_RIGHT|FL_ALIGN_TOP: 1711 | c = FL_CURSOR_NESW; 1712 | break; 1713 | } 1714 | cursor(c); 1715 | } 1716 | 1717 | #ifdef AUTO_RAISE 1718 | // timeout callback to cause autoraise: 1719 | void auto_raise(void*) { 1720 | if (Frame::activeFrame() && !Fl::grab() && !Fl::pushed()) 1721 | Frame::activeFrame()->raise(); 1722 | } 1723 | #endif 1724 | 1725 | extern void ShowMenu(); 1726 | 1727 | // If cursor is in the contents of a window this is set to that window. 1728 | // This is only used to force the cursor to an arrow even though X keeps 1729 | // sending mysterious erroneous move events: 1730 | static Frame* cursor_inside = 0; 1731 | 1732 | // Handle an fltk event. 1733 | int Frame::handle(int e) { 1734 | static int what, dx, dy, ix, iy, iw, ih; 1735 | // see if child widget handles event: 1736 | if (Fl_Group::handle(e) && e != FL_ENTER && e != FL_MOVE) { 1737 | if (e == FL_PUSH) set_cursor(-1); 1738 | return 1; 1739 | } 1740 | switch (e) { 1741 | 1742 | case FL_SHOW: 1743 | case FL_HIDE: 1744 | return 0; // prevent fltk from messing things up 1745 | 1746 | case FL_ENTER: 1747 | #if !CLICK_TO_TYPE 1748 | if (Fl::pushed() || Fl::grab()) return 1; 1749 | if (activate()) { 1750 | #ifdef AUTO_RAISE 1751 | Fl::remove_timeout(auto_raise); 1752 | Fl::add_timeout(AUTO_RAISE, auto_raise); 1753 | #endif 1754 | } 1755 | #endif 1756 | goto GET_CROSSINGS; 1757 | 1758 | case 0: 1759 | GET_CROSSINGS: 1760 | // set cursor_inside to true when the mouse is inside a window 1761 | // set it false when mouse is on a frame or outside a window. 1762 | // fltk mangles the X enter/leave events, we need the original ones: 1763 | 1764 | switch (fl_xevent->type) { 1765 | case EnterNotify: 1766 | 1767 | // see if cursor skipped over frame and directly to interior: 1768 | if (fl_xevent->xcrossing.detail == NotifyVirtual || 1769 | fl_xevent->xcrossing.detail == NotifyNonlinearVirtual) 1770 | cursor_inside = this; 1771 | 1772 | else { 1773 | // cursor is now pointing at frame: 1774 | cursor_inside = 0; 1775 | } 1776 | 1777 | // fall through to FL_MOVE: 1778 | break; 1779 | 1780 | case LeaveNotify: 1781 | if (fl_xevent->xcrossing.detail == NotifyInferior) { 1782 | // cursor moved from frame to interior 1783 | cursor_inside = this; 1784 | set_cursor(-1); 1785 | return 1; 1786 | } 1787 | return 1; 1788 | 1789 | default: 1790 | return 0; // other X event we don't understand 1791 | } 1792 | case FL_MOVE: 1793 | if (Fl::belowmouse() != this || cursor_inside == this) 1794 | set_cursor(-1); 1795 | else 1796 | set_cursor(mouse_location()); 1797 | return 1; 1798 | 1799 | case FL_PUSH: 1800 | // See if user double clicked on titlebar (or window frame). 1801 | if(!cursor_inside && (Fl::event_button() == 1) && Fl::event_clicks()) 1802 | { 1803 | set_cursor(-1); 1804 | ToggleWinMax(); // Toggles window size between normal and maximized. 1805 | return 1; 1806 | } 1807 | 1808 | if (Fl::event_button() > 2) { 1809 | set_cursor(-1); 1810 | ShowMenu(); 1811 | return 1; 1812 | } 1813 | ix = x(); iy = y(); iw = w(); ih = h(); 1814 | if (!max_w_button.value() && !min_w_button.value()) { 1815 | restore_x = ix+left; restore_w = iw-dwidth; 1816 | } 1817 | #if MINIMIZE_HEIGHT 1818 | if (!min_w_button.value()) 1819 | #endif 1820 | if (!max_h_button.value()) { 1821 | restore_y = iy+top; restore_h = ih-dwidth; 1822 | } 1823 | what = mouse_location(); 1824 | if (Fl::event_button() > 1) what = 0; // middle button does drag 1825 | dx = Fl::event_x_root()-ix; 1826 | if (what & FL_ALIGN_RIGHT) dx -= iw; 1827 | dy = Fl::event_y_root()-iy; 1828 | if (what & FL_ALIGN_BOTTOM) dy -= ih; 1829 | set_cursor(what); 1830 | return 1; 1831 | case FL_DRAG: 1832 | if (Fl::event_is_click()) return 1; // don't drag yet 1833 | case FL_RELEASE: 1834 | if (Fl::event_is_click()) { 1835 | if (Fl::grab()) return 1; 1836 | #if CLICK_TO_TYPE 1837 | if (activate()) { 1838 | if (Fl::event_button() <= 1) raise(); 1839 | return 1; 1840 | } 1841 | #endif 1842 | if (Fl::event_button() > 1) lower(); else raise(); 1843 | } else if (!what) { 1844 | int nx = Fl::event_x_root()-dx; 1845 | int W = Root->x()+Root->w(); 1846 | if (nx+iw > W && nx+iw < W+SCREEN_SNAP) { 1847 | int t = W+1-iw; 1848 | if (iw >= Root->w() || x() > t || nx+iw >= W+EDGE_SNAP) 1849 | t = W+(dwidth-left)-iw; 1850 | if (t >= x() && t < nx) nx = t; 1851 | } 1852 | int X = Root->x(); 1853 | if (nx < X && nx > X-SCREEN_SNAP) { 1854 | int t = X-1; 1855 | if (iw >= Root->w() || x() < t || nx <= X-EDGE_SNAP) t = X-BUTTON_LEFT; 1856 | if (t <= x() && t > nx) nx = t; 1857 | } 1858 | int ny = Fl::event_y_root()-dy; 1859 | int H = Root->y()+Root->h(); 1860 | if (ny+ih > H && ny+ih < H+SCREEN_SNAP) { 1861 | int t = H+1-ih; 1862 | if (ih >= Root->h() || y() > t || ny+ih >= H+EDGE_SNAP) 1863 | t = H+(dheight-top)-ih; 1864 | if (t >= y() && t < ny) ny = t; 1865 | } 1866 | int Y = Root->y(); 1867 | if (ny < Y && ny > Y-SCREEN_SNAP) { 1868 | int t = Y-1; 1869 | if (ih >= H || y() < t || ny <= Y-EDGE_SNAP) t = Y-top; 1870 | if (t <= y() && t > ny) ny = t; 1871 | } 1872 | set_size(nx, ny, iw, ih); 1873 | } else { 1874 | int nx = ix; 1875 | int ny = iy; 1876 | int nw = iw; 1877 | int nh = ih; 1878 | if (what & FL_ALIGN_RIGHT) 1879 | nw = Fl::event_x_root()-dx-nx; 1880 | else if (what & FL_ALIGN_LEFT) 1881 | nw = ix+iw-(Fl::event_x_root()-dx); 1882 | else {nx = x(); nw = w();} 1883 | if (what & FL_ALIGN_BOTTOM) 1884 | nh = Fl::event_y_root()-dy-ny; 1885 | else if (what & FL_ALIGN_TOP) 1886 | nh = iy+ih-(Fl::event_y_root()-dy); 1887 | else {ny = y(); nh = h();} 1888 | if (flag(KEEP_ASPECT)) { 1889 | if ((nw-dwidth > nh-dwidth) 1890 | && (what&(FL_ALIGN_LEFT|FL_ALIGN_RIGHT) 1891 | || !(what&(FL_ALIGN_TOP|FL_ALIGN_BOTTOM)))) 1892 | nh = nw-dwidth+dheight; 1893 | else 1894 | nw = nh-dheight+dwidth; 1895 | } 1896 | int MINW = min_w+dwidth; 1897 | if (nw <= dwidth && dwidth > TITLE_WIDTH) { 1898 | nw = dwidth-1; 1899 | #if MINIMIZE_HEIGHT 1900 | restore_h = nh; 1901 | #endif 1902 | } else { 1903 | if (inc_w > 1) nw = ((nw-MINW+inc_w/2)/inc_w)*inc_w+MINW; 1904 | if (nw < MINW) nw = MINW; 1905 | else if (max_w && nw > max_w+dwidth) nw = max_w+dwidth; 1906 | } 1907 | int MINH = min_h+dheight; 1908 | const int MINH_B = BUTTON_H+BUTTON_TOP+BOTTOM; 1909 | if (MINH_B > MINH) MINH = MINH_B; 1910 | if (inc_h > 1) nh = ((nh-MINH+inc_h/2)/inc_h)*inc_h+MINH; 1911 | if (nh < MINH) nh = MINH; 1912 | else if (max_h && nh > max_h+dheight) nh = max_h+dheight; 1913 | if (what & FL_ALIGN_LEFT) nx = ix+iw-nw; 1914 | if (what & FL_ALIGN_TOP) ny = iy+ih-nh; 1915 | set_size(nx,ny,nw,nh); 1916 | } 1917 | return 1; 1918 | } 1919 | return 0; 1920 | } 1921 | 1922 | // Handle events that fltk did not recognize (mostly ones directed 1923 | // at the desktop): 1924 | 1925 | int Frame::handle(const XEvent* ei) { 1926 | 1927 | switch (ei->type) { 1928 | 1929 | case ConfigureRequest: { 1930 | const XConfigureRequestEvent* e = &(ei->xconfigurerequest); 1931 | unsigned long mask = e->value_mask; 1932 | if (mask & CWBorderWidth) app_border_width = e->border_width; 1933 | // Try to detect if the application is really trying to move the 1934 | // window, or is simply echoing it's postion, possibly with some 1935 | // variation (such as echoing the parent window position), and 1936 | // dont' move it in that case: 1937 | int X = (mask & CWX && e->x != x()) ? e->x+app_border_width-left : x(); 1938 | int Y = (mask & CWY && e->y != y()) ? e->y+app_border_width-top : y(); 1939 | int W = (mask & CWWidth) ? e->width+dwidth : w(); 1940 | int H = (mask & CWHeight) ? e->height+dheight : h(); 1941 | // Generally we want to obey any application positioning of the 1942 | // window, except when it appears the app is trying to position 1943 | // the window "at the edge". 1944 | if (!(mask & CWX) || (X >= -2*left && X < 0)) X = force_x_onscreen(X,W); 1945 | if (!(mask & CWY) || (Y >= -2*top && Y < 0)) Y = force_y_onscreen(Y,H); 1946 | // Fix Rick Sayre's program that resizes it's windows bigger than the 1947 | // maximum size: 1948 | if (W > max_w+dwidth) max_w = 0; 1949 | if (H > max_h+dheight) max_h = 0; 1950 | set_size(X, Y, W, H, 2); 1951 | if (e->value_mask & CWStackMode && e->detail == Above && state()==NORMAL) 1952 | raise(); 1953 | return 1;} 1954 | 1955 | case MapRequest: { 1956 | //const XMapRequestEvent* e = &(ei->xmaprequest); 1957 | raise(); 1958 | return 1;} 1959 | 1960 | case UnmapNotify: { 1961 | const XUnmapEvent* e = &(ei->xunmap); 1962 | if (e->window == window_ && !e->from_configure) { 1963 | if (state_flags_&IGNORE_UNMAP) clear_state_flag(IGNORE_UNMAP); 1964 | else state(UNMAPPED); 1965 | } 1966 | return 1;} 1967 | 1968 | case DestroyNotify: { 1969 | //const XDestroyWindowEvent* e = &(ei->xdestroywindow); 1970 | delete this; 1971 | return 1;} 1972 | 1973 | case ReparentNotify: { 1974 | const XReparentEvent* e = &(ei->xreparent); 1975 | if (e->parent==fl_xid(this)) return 1; // echo 1976 | if (e->parent==fl_xid(Root)) return 1; // app is trying to tear-off again? 1977 | delete this; // guess they are trying to paste tear-off thing back? 1978 | return 1;} 1979 | 1980 | case ClientMessage: { 1981 | const XClientMessageEvent* e = &(ei->xclient); 1982 | if (e->message_type == wm_change_state && e->format == 32) { 1983 | if (e->data.l[0] == NormalState) raise(); 1984 | else if (e->data.l[0] == IconicState) iconize(); 1985 | } else 1986 | // we may want to ignore _WIN_LAYER from xmms? 1987 | Fl::warning("flwm: unexpected XClientMessageEvent, type 0x%lx, " 1988 | "window 0x%lx\n", e->message_type, e->window); 1989 | return 1;} 1990 | 1991 | case ColormapNotify: { 1992 | const XColormapEvent* e = &(ei->xcolormap); 1993 | if (e->c_new) { // this field is called "new" in the old C++-unaware Xlib 1994 | colormap = e->colormap; 1995 | if (active()) installColormap(); 1996 | } 1997 | return 1;} 1998 | 1999 | case PropertyNotify: { 2000 | const XPropertyEvent* e = &(ei->xproperty); 2001 | Atom a = e->atom; 2002 | 2003 | if (a == XA_WM_NAME) { 2004 | getLabel(e->state == PropertyDelete); 2005 | 2006 | } else if (a == wm_state) { 2007 | // it's not clear if I really need to look at this. Need to make 2008 | // sure it is not seeing the state echoed by the application by 2009 | // checking for it being different... 2010 | switch (getIntProperty(wm_state, wm_state, state())) { 2011 | case IconicState: 2012 | if (state() == NORMAL || state() == OTHER_DESKTOP) iconize(); 2013 | break; 2014 | case NormalState: 2015 | if (state() != NORMAL && state() != OTHER_DESKTOP) raise(); 2016 | break; 2017 | } 2018 | 2019 | } else if (a == wm_colormap_windows) { 2020 | getColormaps(); 2021 | if (active()) installColormap(); 2022 | 2023 | } else if (a == _motif_wm_hints) { 2024 | // some #%&%$# SGI Motif programs change this after mapping the window! 2025 | // :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( :-( :=( 2026 | if (getMotifHints()) { // returns true if any flags changed 2027 | fix_transient_for(); 2028 | updateBorder(); 2029 | show_hide_buttons(); 2030 | } 2031 | 2032 | } else if (a == wm_protocols) { 2033 | getProtocols(); 2034 | // get Motif hints since they may do something with QUIT: 2035 | getMotifHints(); 2036 | 2037 | } else if (a == XA_WM_NORMAL_HINTS || a == XA_WM_SIZE_HINTS) { 2038 | getSizes(); 2039 | show_hide_buttons(); 2040 | 2041 | } else if (a == XA_WM_TRANSIENT_FOR) { 2042 | XGetTransientForHint(fl_display, window_, &transient_for_xid); 2043 | fix_transient_for(); 2044 | show_hide_buttons(); 2045 | 2046 | } else if (a == XA_WM_COMMAND) { 2047 | clear_state_flag(SAVE_PROTOCOL_WAIT); 2048 | 2049 | } 2050 | return 1;} 2051 | 2052 | } 2053 | return 0; 2054 | } 2055 | 2056 | //////////////////////////////////////////////////////////////// 2057 | // X utility routines: 2058 | 2059 | void* Frame::getProperty(Atom a, Atom type, int* np) const { 2060 | return ::getProperty(window_, a, type, np); 2061 | } 2062 | 2063 | void* getProperty(XWindow w, Atom a, Atom type, int* np) { 2064 | Atom realType; 2065 | int format; 2066 | unsigned long n, extra; 2067 | int status; 2068 | uchar* prop; 2069 | status = XGetWindowProperty(fl_display, w, 2070 | a, 0L, 256L, False, type, &realType, 2071 | &format, &n, &extra, &prop); 2072 | if (status != Success) return 0; 2073 | if (!prop) return 0; 2074 | if (!n) {XFree(prop); return 0;} 2075 | if (np) *np = (int)n; 2076 | return (void*)prop; 2077 | } 2078 | 2079 | int Frame::getIntProperty(Atom a, Atom type, int deflt) const { 2080 | return ::getIntProperty(window_, a, type, deflt); 2081 | } 2082 | 2083 | int getIntProperty(XWindow w, Atom a, Atom type, int deflt) { 2084 | void* prop = getProperty(w, a, type); 2085 | if (!prop) return deflt; 2086 | int r = int(*(long*)prop); 2087 | XFree(prop); 2088 | return r; 2089 | } 2090 | 2091 | void setProperty(XWindow w, Atom a, Atom type, int v) { 2092 | long prop = v; 2093 | XChangeProperty(fl_display, w, a, type, 32, PropModeReplace, (uchar*)&prop,1); 2094 | } 2095 | 2096 | void Frame::setProperty(Atom a, Atom type, int v) const { 2097 | ::setProperty(window_, a, type, v); 2098 | } 2099 | 2100 | void Frame::sendMessage(Atom a, Atom l) const { 2101 | XEvent ev; 2102 | long mask; 2103 | memset(&ev, 0, sizeof(ev)); 2104 | ev.xclient.type = ClientMessage; 2105 | ev.xclient.window = window_; 2106 | ev.xclient.message_type = a; 2107 | ev.xclient.format = 32; 2108 | ev.xclient.data.l[0] = long(l); 2109 | ev.xclient.data.l[1] = long(fl_event_time); 2110 | mask = 0L; 2111 | XSendEvent(fl_display, window_, False, mask, &ev); 2112 | } 2113 | -------------------------------------------------------------------------------- /Frame.H: -------------------------------------------------------------------------------- 1 | // Frame.H 2 | // Some modifications by Michael A. Losh, tagged "ML" below, 3 | // or bracketed by TOPSIDE manifest constant 4 | 5 | // Each X window being managed by fltk has one of these 6 | 7 | #ifndef Frame_H 8 | #define Frame_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #if FL_MAJOR_VERSION<2 15 | # define XWindow Window 16 | #endif 17 | 18 | // The state is an enumeration of reasons why the window may be invisible. 19 | // Only if it is NORMAL is the window visible. 20 | enum { 21 | UNMAPPED = 0, // unmap command from app (X calls this WithdrawnState) 22 | NORMAL = 1, // window is visible 23 | //SHADED = 2, // acts like NORMAL 24 | ICONIC = 3, // hidden/iconized 25 | OTHER_DESKTOP = 4 // normal but on another desktop 26 | }; 27 | 28 | // values for flags: 29 | // The flags are constant and are turned on by information learned 30 | // from the Gnome, KDE, and/or Motif window manager hints. Flwm will 31 | // ignore attempts to change these hints after the window is mapped. 32 | enum { 33 | NO_FOCUS = 0x0001, // does not take focus 34 | CLICK_TO_FOCUS = 0x0002, // must click on window to give it focus 35 | NO_BORDER = 0x0004, // raw window with no border 36 | THIN_BORDER = 0x0008, // just resize border 37 | NO_RESIZE = 0x0010, // don't resize even if sizehints say its ok 38 | NO_CLOSE = 0x0040, // don't put a close box on it 39 | TAKE_FOCUS_PROTOCOL = 0x0080, // send junk when giving window focus 40 | DELETE_WINDOW_PROTOCOL= 0x0100, // close box sends a message 41 | KEEP_ASPECT = 0x0200, // aspect ratio from sizeHints 42 | MODAL = 0x0400, // grabs focus from transient_for window 43 | ICONIZE = 0x0800, // transient_for_ actually means group :-( 44 | QUIT_PROTOCOL = 0x1000, // Irix 4DWM "quit" menu item 45 | SAVE_PROTOCOL = 0x2000 // "WM_SAVE_YOURSELF" stuff 46 | }; 47 | 48 | // values for state_flags: 49 | // These change over time 50 | enum { 51 | IGNORE_UNMAP = 0x01, // we did something that echos an UnmapNotify 52 | SAVE_PROTOCOL_WAIT = 0x02 53 | }; 54 | 55 | class FrameButton : public Fl_Button { 56 | void draw(); 57 | public: 58 | FrameButton(int X, int Y, int W, int H, const char* L=0) 59 | : Fl_Button(X,Y,W,H,L) {} 60 | }; 61 | 62 | class Desktop; 63 | 64 | class Frame : public Fl_Window { 65 | 66 | XWindow window_; 67 | 68 | short state_; // X server state: iconic, withdrawn, normal 69 | short state_flags_; // above state flags 70 | void set_state_flag(short i) {state_flags_ |= i;} 71 | void clear_state_flag(short i) {state_flags_&=~i;} 72 | 73 | int flags_; // above constant flags 74 | void set_flag(int i) {flags_ |= i;} 75 | void clear_flag(int i) {flags_&=~i;} 76 | 77 | int restore_x, restore_w; // saved size when min/max width is set 78 | int restore_y, restore_h; // saved size when max height is set 79 | int min_w, max_w, inc_w; // size range and increment 80 | int min_h, max_h, inc_h; // size range and increment 81 | int app_border_width; // value of border_width application tried to set 82 | 83 | int left, top, dwidth, dheight; // current thickness of border 84 | #ifdef TOPSIDE 85 | int label_x, label_w; // location of label 86 | int label_h; // measured height of printed label 87 | #else 88 | int label_y, label_h; // location of label 89 | int label_w; // measured width of printed label 90 | #endif 91 | 92 | XWindow transient_for_xid; // value from X 93 | Frame* transient_for_; // the frame for that xid, if found 94 | 95 | Frame* revert_to; // probably the xterm this was run from 96 | 97 | Colormap colormap; // this window's colormap 98 | int colormapWinCount; // list of other windows to install colormaps for 99 | XWindow *colormapWindows; 100 | Colormap *window_Colormaps; // their colormaps 101 | 102 | Desktop* desktop_; 103 | 104 | FrameButton close_button; 105 | FrameButton max_w_button; 106 | FrameButton min_w_button; 107 | FrameButton max_h_button; 108 | FrameButton iconize_button; 109 | 110 | int maximize_width(); 111 | int maximize_height(); 112 | int force_x_onscreen(int X, int W); 113 | int force_y_onscreen(int Y, int H); 114 | void place_window(); 115 | 116 | void sendMessage(Atom, Atom) const; 117 | void sendConfigureNotify() const; 118 | void setStateProperty() const; 119 | 120 | void* getProperty(Atom, Atom = AnyPropertyType, int* length = 0) const; 121 | int getIntProperty(Atom, Atom = AnyPropertyType, int deflt = 0) const; 122 | void setProperty(Atom, Atom, int) const; 123 | void getLabel(int del = 0); 124 | void getColormaps(); 125 | int getSizes(); 126 | int getGnomeState(int&); 127 | void getProtocols(); 128 | int getMotifHints(); 129 | void updateBorder(); 130 | void fix_transient_for(); // called when transient_for_xid changes 131 | 132 | void installColormap() const; 133 | 134 | void resize(int,int,int,int); 135 | void layout(); 136 | void show_hide_buttons(); 137 | 138 | int handle(int); // handle fltk events 139 | void set_cursor(int); 140 | int mouse_location(); 141 | 142 | void draw(); 143 | 144 | static Frame* active_; 145 | static void button_cb_static(Fl_Widget*, void*); 146 | void button_cb(Fl_Button*); 147 | 148 | void deactivate(); 149 | int activate_if_transient(); 150 | void _desktop(Desktop*); 151 | 152 | int border() const {return !(flags_&NO_BORDER);} 153 | int flags() const {return flags_;} 154 | int flag(int i) const {return flags_&i;} 155 | void throw_focus(int destructor = 0); 156 | void warp_pointer(); 157 | 158 | public: 159 | 160 | void set_size(int,int,int,int, int warp=0); // ML: made public 161 | 162 | int handle(const XEvent*); 163 | 164 | static Frame* first; 165 | Frame* next; // stacking order, top to bottom 166 | 167 | Frame(XWindow, XWindowAttributes* = 0); 168 | ~Frame(); 169 | 170 | XWindow window() const {return window_;} 171 | Frame* transient_for() const {return transient_for_;} 172 | int is_transient_for(const Frame*) const; 173 | 174 | Desktop* desktop() const {return desktop_;} 175 | void desktop(Desktop*); 176 | 177 | void raise(); // also does map 178 | void lower(); 179 | void iconize(); 180 | void close(); 181 | void kill(); 182 | int activate(int warp = 0); // returns true if it actually sets active state 183 | 184 | short state() const {return state_;} 185 | void state(short); // don't call this unless you know what you are doing! 186 | 187 | int active() const {return active_==this;} 188 | static Frame* activeFrame() {return active_;} 189 | 190 | static void save_protocol(); // called when window manager exits 191 | 192 | // The following should be conditionally defined based on the 193 | // SHOW_CLOCK definition in config.h but that definition is not 194 | // available at the time we are evaluating this; it does no harm 195 | // to be present even if not SHOW_CLOCK. 196 | void redraw_clock(); 197 | 198 | }; 199 | 200 | // handy wrappers for those ugly X routines: 201 | void* getProperty(XWindow, Atom, Atom = AnyPropertyType, int* length = 0); 202 | int getIntProperty(XWindow, Atom, Atom = AnyPropertyType, int deflt = 0); 203 | void setProperty(XWindow, Atom, Atom, int); 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /FrameWindow.C: -------------------------------------------------------------------------------- 1 | // FrameWindow.C 2 | 3 | // X does not echo back the window-map events (it probably should when 4 | // override_redirect is off). Unfortunately this means you have to use 5 | // this subclass if you want a "normal" fltk window, it will force a 6 | // Frame to be created and destroy it upon hide. 7 | 8 | // Warning: modal() does not work! Don't turn it on as it screws up the 9 | // interface with the window borders. You can use set_non_modal() to 10 | // disable the iconize box but the window manager must be written to 11 | // not be modal. 12 | 13 | #include 14 | #include "FrameWindow.H" 15 | #include "Frame.H" 16 | 17 | extern int dont_set_event_mask; 18 | 19 | void FrameWindow::show() { 20 | if (shown()) {Fl_Window::show(); return;} 21 | Fl_Window::show(); 22 | dont_set_event_mask = 1; 23 | frame = new Frame(fl_xid(this)); 24 | dont_set_event_mask = 0; 25 | } 26 | 27 | void FrameWindow::hide() { 28 | if (shown()) { 29 | Fl_Window::hide(); 30 | delete frame; 31 | } 32 | } 33 | 34 | int FrameWindow::handle(int e) { 35 | if (Fl_Window::handle(e)) return 1; 36 | // make Esc close the window: 37 | if (e == FL_SHORTCUT && Fl::event_key()==FL_Escape) { 38 | do_callback(); 39 | return 1; 40 | } 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /FrameWindow.H: -------------------------------------------------------------------------------- 1 | // FrameWindow.H 2 | 3 | // X does not echo back the window-map events (it probably should when 4 | // override_redirect is off). Unfortunately this means you have to use 5 | // this subclass if you want a "normal" fltk window, it will force a 6 | // Frame to be created and destroy it upon hide. 7 | 8 | // Warning: modal() does not work! Don't turn it on as it screws up the 9 | // interface with the window borders. You can use set_non_modal() to 10 | // disable the iconize box but the window manager must be written to 11 | // not be modal. 12 | 13 | #ifndef FrameWindow_H 14 | #define FrameWindow_H 15 | 16 | #include 17 | class Frame; 18 | 19 | class FrameWindow : public Fl_Window { 20 | Frame* frame; 21 | public: 22 | void hide(); 23 | void show(); 24 | int handle(int); 25 | FrameWindow(int X, int Y, int W, int H, const char* L = 0) : 26 | Fl_Window(X,Y,W,H,L) {} 27 | FrameWindow(int W, int H, const char* L = 0) : 28 | Fl_Window(W,H,L) {} 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Hotkeys.C: -------------------------------------------------------------------------------- 1 | // Hotkeys.C 2 | // If you want to change what the hotkeys are, see the table at the bottom! 3 | // 4 | // CHANGES 5 | // 20160402: clarify tests in Handle_Hotkey() (add parentheses); dentonlt 6 | // 20160410: add FL_META for PreviousDesk/NextDesk, reverse desktop 7 | // browse order - newer desktops are 'next'; dentonlt 8 | // 20160411: GrowFrame(): stay below y = 0, add anchor top left, 9 | // grow at any size; dentonlt 10 | // 20160506: Add EXTENDED_MOVEMENT_KEYS; dentonlt 11 | // 20190303: Made ToggleWinMax public for use in Frame.C. Rich 12 | 13 | #include "config.h" 14 | #include "Frame.H" 15 | #include "Desktop.H" 16 | #include 17 | 18 | extern void ShowMenu(); 19 | extern void ShowTabMenu(int tab); 20 | 21 | void ToggleWinMax(void); 22 | 23 | #if STANDARD_HOTKEYS 24 | 25 | static void NextWindow() { // Alt+Tab 26 | ShowTabMenu(1); 27 | } 28 | 29 | static void PreviousWindow() { // Alt+Shift+Tab 30 | ShowTabMenu(-1); 31 | } 32 | 33 | #endif 34 | 35 | #if DESKTOPS && ( WMX_DESK_HOTKEYS || WMX_DESK_METAKEYS ) 36 | 37 | static void PreviousDesk() { 38 | if (Desktop::current()) { 39 | Desktop::current(Desktop::current()->next? 40 | Desktop::current()->next:Desktop::first); 41 | } else { 42 | Desktop::current(Desktop::first); 43 | } 44 | } 45 | 46 | static void NextDesk() { 47 | Desktop* search=Desktop::first; 48 | while (search->next && search->next!=Desktop::current()){ 49 | search=search->next; 50 | } 51 | Desktop::current(search); 52 | } 53 | 54 | #endif 55 | 56 | #if DESKTOPS && (DESKTOP_HOTKEYS || KWM_HOTKEYS) 57 | 58 | // warning: this assummes it is bound to Fn key: 59 | static void DeskNumber() { 60 | Desktop::current(Desktop::number(Fl::event_key()-FL_F, 1)); 61 | } 62 | #endif 63 | 64 | #if WMX_HOTKEYS || CDE_HOTKEYS 65 | 66 | static void Raise() { // Alt+Up 67 | Frame* f = Frame::activeFrame(); 68 | if (f) f->raise(); 69 | } 70 | 71 | static void Lower() { // Alt+Down 72 | Frame* f = Frame::activeFrame(); 73 | if (f) f->lower(); 74 | } 75 | 76 | static void Iconize() { // Alt+F1 77 | Frame* f = Frame::activeFrame(); 78 | if (f) f->iconize(); 79 | else ShowMenu(); // so they can deiconize stuff 80 | } 81 | 82 | static void Close() { // Alt+Delete 83 | Frame* f = Frame::activeFrame(); 84 | if (f) f->close(); 85 | } 86 | 87 | #endif 88 | 89 | #ifdef ML_HOTKEYS 90 | static void MoveFrame(int xbump, int ybump) { 91 | int xincr, yincr, nx, ny, xspace, yspace; 92 | Frame* f = Frame::activeFrame(); 93 | if (f) { 94 | xspace = Fl::w() - f->w(); 95 | xincr = xspace / 20; 96 | if (xincr < 4) { 97 | xincr = 4; 98 | } 99 | nx = f->x() + (xbump * xincr); 100 | if (nx < 0) nx = 0; 101 | if (nx > xspace) nx = xspace; 102 | 103 | yspace = Fl::h() - f->h(); 104 | yincr = yspace / 20; 105 | if (yincr < 4) { 106 | yincr = 4; 107 | } 108 | ny = f->y() + (ybump * yincr); 109 | if (ny < 0) ny = 0; 110 | if (ny > yspace) ny = yspace; 111 | 112 | f->set_size(nx, ny, f->w(), f->h()); 113 | } 114 | } 115 | 116 | static void MoveLeft(void) { // Ctrl+Alt+Left 117 | MoveFrame(-1, 0); 118 | } 119 | 120 | static void MoveUp(void) { // Ctrl+Alt+Up 121 | MoveFrame(0, -1); 122 | } 123 | 124 | static void MoveRight(void) { // Ctrl+Alt+Right 125 | MoveFrame(+1, 0); 126 | } 127 | 128 | static void MoveDown(void) { // Ctrl+Alt+Down 129 | MoveFrame(0, +1); 130 | } 131 | 132 | #ifdef EXTENDED_MOVEMENT_KEYS 133 | static void MoveMaxLeft(void) { 134 | Frame* f = Frame::activeFrame(); 135 | f->set_size(0, f->y(), f->w(), f->h()); 136 | } 137 | 138 | static void MoveMaxRight(void) { 139 | Frame* f = Frame::activeFrame(); 140 | f->set_size(Fl::w() - f->w(), f->y(), f->w(), f->h()); 141 | } 142 | 143 | static void MoveMaxUp(void) { 144 | Frame* f = Frame::activeFrame(); 145 | f->set_size(f->x(), 0, f->w(), f->h()); 146 | } 147 | 148 | static void MoveMaxDown(void) { 149 | Frame* f = Frame::activeFrame(); 150 | f->set_size(f->x(), Fl::h() - f->h(), f->w(), f->h()); 151 | } 152 | 153 | static void MoveCenter(void) { 154 | Frame* f = Frame::activeFrame(); 155 | f->set_size(Fl::w()/2 - f->w()/2, Fl::h()/2 - f->h()/2, f->w(), f->h()); 156 | } 157 | 158 | #endif 159 | 160 | static void GrowFrame(int wbump, int hbump) { 161 | int nx, ny, nw, nh; 162 | Frame* f = Frame::activeFrame(); 163 | if (f) { 164 | int minw = 8 * BUTTON_W; 165 | int minh = 4 * BUTTON_H; 166 | nx = f->x(); 167 | ny = f->y(); 168 | nw = f->w(); 169 | nh = f->h(); 170 | 171 | if (wbump) { 172 | // grow & clip 173 | nw += wbump * 32; 174 | if (nw < minw) nw = minw; 175 | if (nw > Fl::w()) nw = Fl::w(); 176 | 177 | // reposition if off-screen 178 | if (nx + nw > Fl::w()) 179 | nx = Fl::w() - nw; 180 | } 181 | 182 | if (hbump) { 183 | // grow & clip 184 | nh += hbump * 32; 185 | if (nh < minh) nh = minh; 186 | if (nh > Fl::h()) nh = Fl::h(); 187 | 188 | #ifndef RESIZE_ANCHOR_TOPLEFT 189 | // grow with bottom left corner anchored 190 | if (nh != f->h()) 191 | ny = f->y() - hbump * 32; 192 | #endif 193 | 194 | // reposition if off-screen 195 | if (ny < 0) ny = 0; 196 | if (ny + nh > Fl::h()) 197 | ny = Fl::h() - nh; 198 | } 199 | 200 | f->set_size(nx, ny, nw, nh); 201 | } 202 | } 203 | 204 | static void GrowBigger(void) { // Ctrl+Alt+Plus 205 | GrowFrame(+1, +1); 206 | } 207 | 208 | static void GrowSmaller(void) { // Ctrl+Alt+Minus 209 | GrowFrame(-1, -1); 210 | } 211 | 212 | static void GrowWider(void) { // Ctrl+Alt+Gt.Than 213 | GrowFrame(+1, 0); 214 | } 215 | 216 | static void GrowNarrower(void) { // Ctrl+Alt+LessThan 217 | GrowFrame(-1, 0); 218 | } 219 | 220 | static void GrowTaller(void) { // Ctrl+Alt+PageUp 221 | GrowFrame(0, +1); 222 | } 223 | 224 | static void GrowShorter(void) { // Ctrl+Alt+PageDn 225 | GrowFrame(0, -1); 226 | } 227 | 228 | void ToggleVertMax(void) {// Ctrl+Alt+V 229 | static int nonmax_h = Fl::h() - 64; 230 | static int nonmax_y = 32; 231 | Frame* f = Frame::activeFrame(); 232 | 233 | if (f->h() < Fl::h() - 16) { 234 | nonmax_h = f->h(); 235 | nonmax_y = f->y(); 236 | f->set_size(f->x(), 0, f->w(), Fl::h()); 237 | } 238 | else { 239 | f->set_size(f->x(), nonmax_y, f->w(), nonmax_h); 240 | } 241 | } 242 | 243 | void ToggleHorzMax(void) {// Ctrl+Alt+H 244 | static int nonmax_w = Fl::w() - 64; 245 | static int nonmax_x = 32; 246 | Frame* f = Frame::activeFrame(); 247 | 248 | if (f->w() < Fl::w() - 16) { 249 | nonmax_w = f->w(); 250 | nonmax_x = f->x(); 251 | f->set_size(0, f->y(), Fl::w(), f->h()); 252 | } 253 | else { 254 | f->set_size(nonmax_x, f->y(), nonmax_w, f->h()); 255 | } 256 | } 257 | 258 | void ToggleWinMax(void) {// Ctrl+Alt+M 259 | Frame* f = Frame::activeFrame(); 260 | int is_hmax = -1; 261 | int is_vmax = -1; 262 | if (f->w() > Fl::w() - 16) is_hmax = 1; 263 | if (f->h() > Fl::h() - 16) is_vmax = 1; 264 | 265 | if ((is_hmax * is_vmax) > 0 || is_hmax > 0) ToggleVertMax(); 266 | if ((is_hmax * is_vmax) > 0 || is_vmax > 0) ToggleHorzMax(); 267 | } 268 | #endif 269 | 270 | //////////////////////////////////////////////////////////////// 271 | 272 | static struct {int key; void (*func)();} keybindings[] = { 273 | #if STANDARD_HOTKEYS || MINIMUM_HOTKEYS 274 | // these are very common and tend not to conflict, due to Windoze: 275 | {FL_ALT+FL_Escape, ShowMenu}, 276 | {FL_ALT+FL_Menu, ShowMenu}, 277 | #endif 278 | #if STANDARD_HOTKEYS 279 | {FL_ALT+FL_Tab, NextWindow}, 280 | {FL_ALT+FL_SHIFT+FL_Tab,PreviousWindow}, 281 | {FL_ALT+FL_SHIFT+0xfe20,PreviousWindow}, // XK_ISO_Left_Tab 282 | #endif 283 | #if KWM_HOTKEYS && DESKTOPS // KWM uses these to switch desktops 284 | // {FL_CTRL+FL_Tab, NextDesk}, 285 | // {FL_CTRL+FL_SHIFT+FL_Tab,PreviousDesk}, 286 | // {FL_CTRL+FL_SHIFT+0xfe20,PreviousDesk}, // XK_ISO_Left_Tab 287 | {FL_CTRL+FL_F+1, DeskNumber}, 288 | {FL_CTRL+FL_F+2, DeskNumber}, 289 | {FL_CTRL+FL_F+3, DeskNumber}, 290 | {FL_CTRL+FL_F+4, DeskNumber}, 291 | {FL_CTRL+FL_F+5, DeskNumber}, 292 | {FL_CTRL+FL_F+6, DeskNumber}, 293 | {FL_CTRL+FL_F+7, DeskNumber}, 294 | {FL_CTRL+FL_F+8, DeskNumber}, 295 | {FL_CTRL+FL_F+9, DeskNumber}, 296 | {FL_CTRL+FL_F+10, DeskNumber}, 297 | {FL_CTRL+FL_F+11, DeskNumber}, 298 | {FL_CTRL+FL_F+12, DeskNumber}, 299 | #endif 300 | #if WMX_HOTKEYS 301 | // wmx also sets all these, they seem pretty useful: 302 | {FL_ALT+FL_Up, Raise}, 303 | {FL_ALT+FL_Down, Lower}, 304 | //{FL_ALT+FL_Enter, Iconize}, 305 | {FL_ALT+FL_F+1, Iconize}, 306 | {FL_ALT+FL_Delete, Close}, 307 | //{FL_ALT+FL_Page_Up, ToggleMaxH}, 308 | //{FL_ALT+FL_Page_Down, ToggleMaxW}, 309 | #endif 310 | #if WMX_DESK_HOTKEYS && DESKTOPS 311 | // these wmx keys are not set by default as they break NetScape: 312 | {FL_ALT+FL_Left, PreviousDesk}, 313 | {FL_ALT+FL_Right, NextDesk}, 314 | #elif WMX_DESK_METAKEYS && DESKTOPS 315 | {FL_META+FL_Left, PreviousDesk}, 316 | {FL_META+FL_Right, NextDesk}, 317 | #endif 318 | #if CDE_HOTKEYS 319 | // CDE hotkeys (or at least what SGI's 4DWM uses): 320 | //{FL_ALT+FL_F+1, Raise}, // used above to iconize 321 | //{FL_ALT+FL_F+2, unknown}, // KWM uses this to run a typed-in command 322 | {FL_ALT+FL_F+3, Lower}, 323 | {FL_ALT+FL_F+4, Close}, // this matches KWM 324 | //{FL_ALT+FL_F+5, Restore}, // useless because no icons visible 325 | //{FL_ALT+FL_F+6, unknown}, // ? 326 | //{FL_ALT+FL_F+7, Move}, // grabs the window for movement 327 | //{FL_ALT+FL_F+8, Resize}, // grabs the window for resizing 328 | {FL_ALT+FL_F+9, Iconize}, 329 | //{FL_ALT+FL_F+10, Maximize}, 330 | //{FL_ALT+FL_F+11, unknown}, // ? 331 | {FL_ALT+FL_F+12, Close}, // actually does "quit" 332 | #else 333 | #if DESKTOPS && DESKTOP_HOTKEYS 334 | // seem to be common to Linux window managers 335 | {FL_ALT+FL_F+1, DeskNumber}, 336 | {FL_ALT+FL_F+2, DeskNumber}, 337 | {FL_ALT+FL_F+3, DeskNumber}, 338 | {FL_ALT+FL_F+4, DeskNumber}, 339 | {FL_ALT+FL_F+5, DeskNumber}, 340 | {FL_ALT+FL_F+6, DeskNumber}, 341 | {FL_ALT+FL_F+7, DeskNumber}, 342 | {FL_ALT+FL_F+8, DeskNumber}, 343 | {FL_ALT+FL_F+9, DeskNumber}, 344 | {FL_ALT+FL_F+10, DeskNumber}, 345 | {FL_ALT+FL_F+11, DeskNumber}, 346 | {FL_ALT+FL_F+12, DeskNumber}, 347 | #endif 348 | #endif 349 | #ifdef ML_HOTKEYS 350 | {FL_CTRL+FL_ALT+FL_Left, MoveLeft}, 351 | {FL_CTRL+FL_ALT+FL_Up, MoveUp}, 352 | {FL_CTRL+FL_ALT+FL_Right, MoveRight}, 353 | {FL_CTRL+FL_ALT+FL_Down, MoveDown}, 354 | {FL_CTRL+FL_ALT+'=', GrowBigger}, // = is also + key 355 | {FL_CTRL+FL_ALT+'-', GrowSmaller}, 356 | {FL_CTRL+FL_ALT+'.', GrowWider}, // . is also > key 357 | {FL_CTRL+FL_ALT+',', GrowNarrower}, // , is also < key 358 | {FL_CTRL+FL_ALT+FL_Page_Up, GrowTaller}, 359 | {FL_CTRL+FL_ALT+FL_Page_Down, GrowShorter}, 360 | {FL_CTRL+FL_ALT+'t', GrowTaller}, 361 | {FL_CTRL+FL_ALT+'s', GrowShorter}, 362 | {FL_CTRL+FL_ALT+'v', ToggleVertMax}, 363 | {FL_CTRL+FL_ALT+'h', ToggleHorzMax}, 364 | {FL_CTRL+FL_ALT+'m', ToggleWinMax}, 365 | #endif 366 | #ifdef EXTENDED_MOVEMENT_KEYS 367 | {FL_CTRL+FL_ALT+FL_SHIFT+FL_Left, MoveMaxLeft}, 368 | {FL_CTRL+FL_ALT+FL_SHIFT+FL_Up, MoveMaxUp}, 369 | {FL_CTRL+FL_ALT+FL_SHIFT+FL_Right, MoveMaxRight}, 370 | {FL_CTRL+FL_ALT+FL_SHIFT+FL_Down, MoveMaxDown}, 371 | {FL_CTRL+FL_ALT+FL_SHIFT+FL_Enter, MoveCenter}, 372 | #endif 373 | {0}}; 374 | 375 | int Handle_Hotkey() { 376 | for (int i = 0; keybindings[i].key; i++) { 377 | if ( Fl::test_shortcut(keybindings[i].key) || 378 | (((keybindings[i].key & 0xFFFF) == FL_Delete) 379 | && (Fl::event_key() == FL_BackSpace)) // fltk bug? 380 | ) { 381 | keybindings[i].func(); 382 | return 1; 383 | } 384 | } 385 | return 0; 386 | } 387 | 388 | extern Fl_Window* Root; 389 | 390 | void Grab_Hotkeys() { 391 | XWindow root = fl_xid(Root); 392 | for (int i = 0; keybindings[i].key; i++) { 393 | int k = keybindings[i].key; 394 | int keycode = XKeysymToKeycode(fl_display, k & 0xFFFF); 395 | if (!keycode) continue; 396 | // Silly X! we need to ignore caps lock & numlock keys by grabbing 397 | // all the combinations: 398 | XGrabKey(fl_display, keycode, k>>16, root, 0, 1, 1); 399 | XGrabKey(fl_display, keycode, (k>>16)|2, root, 0, 1, 1); // CapsLock 400 | XGrabKey(fl_display, keycode, (k>>16)|16, root, 0, 1, 1); // NumLock 401 | XGrabKey(fl_display, keycode, (k>>16)|18, root, 0, 1, 1); // both 402 | } 403 | } 404 | 405 | -------------------------------------------------------------------------------- /Menu.C: -------------------------------------------------------------------------------- 1 | // Menu.cxx 2 | 3 | #include "config.h" 4 | #include "Frame.H" 5 | #if DESKTOPS 6 | #include "Desktop.H" 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "FrameWindow.H" 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | // it is possible for the window to be deleted or withdrawn while 24 | // the menu is up. This will detect that case (with reasonable 25 | // reliability): 26 | static int 27 | window_deleted(Frame* c) 28 | { 29 | return c->state() != NORMAL 30 | && c->state() != ICONIC 31 | && c->state() != OTHER_DESKTOP; 32 | } 33 | 34 | static void 35 | frame_callback(Fl_Widget*, void*d) 36 | { 37 | Frame* c = (Frame*)d; 38 | if (window_deleted(c)) return; 39 | c->raise(); 40 | c->activate(2); 41 | } 42 | 43 | #if DESKTOPS 44 | // raise it but also put it on the current desktop: 45 | static void 46 | move_frame_callback(Fl_Widget*, void*d) 47 | { 48 | Frame* c = (Frame*)d; 49 | if (window_deleted(c)) return; 50 | c->desktop(Desktop::current()); 51 | c->raise(); 52 | c->activate(2); 53 | } 54 | #endif 55 | 56 | #define SCREEN_DX 1 // offset to corner of contents area 57 | #define SCREEN_W (MENU_ICON_W-2) // size of area to draw contents in 58 | #define SCREEN_H (MENU_ICON_H-2) // size of area to draw contents in 59 | 60 | #define MAX_NESTING_DEPTH 32 61 | 62 | extern Fl_Window* Root; 63 | 64 | static void 65 | frame_label_draw(const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) 66 | { 67 | Frame* f = (Frame*)(o->value); 68 | if (window_deleted(f)) return; 69 | fl_draw_box(FL_THIN_DOWN_BOX, X, Y, MENU_ICON_W, MENU_ICON_H, FL_GRAY); 70 | for (Frame* c = Frame::first; c; c = c->next) { 71 | if (c->state() != UNMAPPED && (c==f || c->is_transient_for(f))) { 72 | int x = ((c->x()-Root->x())*SCREEN_W+Root->w()/2)/Root->w(); 73 | int w = (c->w()*SCREEN_W+Root->w()-1)/Root->w(); 74 | if (w > SCREEN_W) w = SCREEN_W; 75 | if (w < 3) w = 3; 76 | if (x+w > SCREEN_W) x = SCREEN_W-w; 77 | if (x < 0) x = 0; 78 | int y = ((c->y()-Root->y())*SCREEN_H+Root->h()/2)/Root->h(); 79 | int h = (c->h()*SCREEN_H+Root->h()-1)/Root->h(); 80 | if (h > SCREEN_H) h = SCREEN_H; 81 | if (h < 3) h = 3; 82 | if (y+h > SCREEN_H) y = SCREEN_H-h; 83 | if (y < 0) y = 0; 84 | fl_color(FL_FOREGROUND_COLOR); 85 | if (c->state() == ICONIC) 86 | fl_rect(X+x+SCREEN_DX, Y+y+SCREEN_DX, w, h); 87 | else 88 | fl_rectf(X+x+SCREEN_DX, Y+y+SCREEN_DX, w, h); 89 | } 90 | } 91 | fl_font(o->font, TitleFontSz); // o->size); 92 | fl_color((Fl_Color)o->color); 93 | const char* l = f->label(); if (!l) l = "unnamed"; 94 | // double any ampersands to turn off the underscores: 95 | char buf[256]; 96 | if (strchr(l,'&')) { 97 | char* t = buf; 98 | while (t < buf+254 && *l) { 99 | if (*l=='&') *t++ = *l; 100 | *t++ = *l++; 101 | } 102 | *t = 0; 103 | l = buf; 104 | } 105 | fl_draw(l, X+MENU_ICON_W+3, Y, W-MENU_ICON_W-3, H, align); 106 | } 107 | 108 | static void 109 | frame_label_measure(const Fl_Label* o, int& W, int& H) 110 | { 111 | Frame* f = (Frame*)(o->value); 112 | if (window_deleted(f)) {W = MENU_ICON_W+3; H = MENU_ICON_H; return;} 113 | const char* l = f->label(); if (!l) l = "unnamed"; 114 | fl_font(o->font, TitleFontSz); // o->size); 115 | fl_measure(l, W, H); 116 | W += MENU_ICON_W+3; 117 | if (W > MAX_MENU_WIDTH) W = MAX_MENU_WIDTH; 118 | if (H < MENU_ICON_H) H = MENU_ICON_H; 119 | } 120 | 121 | // This labeltype is used for non-frame items so the text can line 122 | // up with the icons: 123 | 124 | static void 125 | label_draw(const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align) 126 | { 127 | fl_font(o->font, TitleFontSz); // o->size); 128 | fl_color((Fl_Color)o->color); 129 | fl_draw(o->value, X+MENU_ICON_W+3, Y, W-MENU_ICON_W-3, H, align); 130 | } 131 | 132 | static void 133 | label_measure(const Fl_Label* o, int& W, int& H) 134 | { 135 | fl_font(o->font, TitleFontSz); // o->size); 136 | fl_measure(o->value, W, H); 137 | W += MENU_ICON_W+3; 138 | if (W > MAX_MENU_WIDTH) W = MAX_MENU_WIDTH; 139 | if (H < MENU_ICON_H) H = MENU_ICON_H; 140 | } 141 | 142 | #define FRAME_LABEL FL_FREE_LABELTYPE 143 | #define TEXT_LABEL Fl_Labeltype(FL_FREE_LABELTYPE+1) 144 | 145 | 146 | //////////////////////////////////////////////////////////////// 147 | 148 | #if DESKTOPS 149 | 150 | static void 151 | desktop_cb(Fl_Widget*, void* v) 152 | { 153 | Desktop::current((Desktop*)v); 154 | } 155 | 156 | static void 157 | delete_desktop_cb(Fl_Widget*, void* v) 158 | { 159 | delete (Desktop*)v; 160 | } 161 | 162 | #if ASK_FOR_NEW_DESKTOP_NAME 163 | 164 | static Fl_Input* new_desktop_input = 0; 165 | 166 | static void 167 | cancel_cb(Fl_Widget* w, void*) 168 | { 169 | w->window()->hide(); 170 | } 171 | 172 | static void 173 | new_desktop_ok_cb(Fl_Widget* w, void*) 174 | { 175 | w->window()->hide(); 176 | Desktop::current(new Desktop(new_desktop_input->value(), Desktop::available_number())); 177 | } 178 | 179 | static void 180 | new_desktop_cb(Fl_Widget*, void*) 181 | { 182 | if (!new_desktop_input) { 183 | FrameWindow* w = new FrameWindow(190,90); 184 | new_desktop_input = new Fl_Input(10,30,170,25,"New desktop name:"); 185 | new_desktop_input->align(FL_ALIGN_TOP_LEFT); 186 | new_desktop_input->labelfont(FL_BOLD); 187 | Fl_Return_Button* b = new Fl_Return_Button(100,60,80,20,"OK"); 188 | b->callback(new_desktop_ok_cb); 189 | Fl_Button* b2 = new Fl_Button(10,60,80,20,"Cancel"); 190 | b2->callback(cancel_cb); 191 | w->set_non_modal(); 192 | w->end(); 193 | } 194 | char buf[120]; 195 | sprintf(buf, "Desktop %d", Desktop::available_number()); 196 | new_desktop_input->value(buf); 197 | new_desktop_input->window()->hotspot(new_desktop_input); 198 | new_desktop_input->window()->show(); 199 | } 200 | 201 | #else // !ASK_FOR_NEW_DESKTOP_NAME 202 | 203 | static void 204 | new_desktop_cb(Fl_Widget*, void*) 205 | { 206 | char buf[120]; 207 | int i = Desktop::available_number(); 208 | sprintf(buf, "Desktop %d", i); 209 | Desktop::current(new Desktop(buf, i)); 210 | } 211 | 212 | #endif 213 | 214 | #endif 215 | //////////////////////////////////////////////////////////////// 216 | 217 | static void 218 | exit_cb(Fl_Widget*, void*) 219 | { 220 | printf("exit_cb\n"); 221 | Frame::save_protocol(); 222 | exit(0); 223 | } 224 | 225 | #include 226 | #include 227 | #include 228 | 229 | static void 230 | logout_cb(Fl_Widget*, void*) 231 | { 232 | int pid=0; 233 | if (( pid=fork()) == 0) { 234 | execlp("exittc","exittc", NULL); 235 | } 236 | } 237 | 238 | //////////////////////////////////////////////////////////////// 239 | 240 | #include 241 | #include 242 | #include 243 | 244 | #if XTERM_MENU_ITEM || WMX_MENU_ITEMS 245 | 246 | static const char* xtermname = "xterm"; 247 | 248 | static void 249 | spawn_cb(Fl_Widget*, void*n) 250 | { 251 | char* name = (char*)n; 252 | // strange code thieved from 9wm to avoid leaving zombies 253 | if (fork() == 0) { 254 | if (fork() == 0) { 255 | close(ConnectionNumber(fl_display)); 256 | if (name == xtermname) execlp(name, name, "-ut", (void*)0); 257 | else execl(name, name, (void*)0); 258 | fprintf(stderr, "flwm: can't run %s, %s\n", name, strerror(errno)); 259 | XBell(fl_display, 70); 260 | exit(1); 261 | } 262 | exit(0); 263 | } 264 | wait((int *) 0); 265 | } 266 | 267 | #endif 268 | 269 | static Fl_Menu_Item other_menu_items[] = { 270 | #if XTERM_MENU_ITEM 271 | {"New xterm", 0, spawn_cb, (void*)xtermname, 0, 0, 0, MENU_FONT_SIZE}, 272 | #endif 273 | #if DESKTOPS 274 | {"New desktop", 0, new_desktop_cb, 0, 0, 0, 0, MENU_FONT_SIZE}, 275 | #endif 276 | {"Exit", 0, logout_cb, 0, 0, 0, 0, MENU_FONT_SIZE}, 277 | {0}}; 278 | #define num_other_items (sizeof(other_menu_items)/sizeof(Fl_Menu_Item)) 279 | 280 | // use this to fill in a menu location: 281 | static void 282 | init(Fl_Menu_Item& m, const char* data) 283 | { 284 | #ifdef HAVE_STYLES 285 | m.style = 0; 286 | #endif 287 | m.label(data); 288 | m.flags = 0; 289 | m.labeltype(FL_NORMAL_LABEL); 290 | m.shortcut(0); 291 | m.labelfont(MENU_FONT_SLOT); 292 | m.labelsize(TitleFontSz); // MENU_FONT_SIZE); 293 | m.labelcolor(FL_FOREGROUND_COLOR); 294 | } 295 | 296 | #if WMX_MENU_ITEMS 297 | 298 | // wmxlist is an array of char* pointers (for efficient sorting purposes), 299 | // which are stored in wmxbuffer (for memory efficiency and to avoid 300 | // freeing and fragmentation) 301 | static char** wmxlist = NULL; 302 | static int wmxlistsize = 0; 303 | // wmx commands are read from ~/.wmx, 304 | // they are stored null-separated here: 305 | static char* wmxbuffer = NULL; 306 | static int wmxbufsize = 0; 307 | static int num_wmx = 0; 308 | // ML ---------------------- 309 | // static time_t wmx_time = 0; 310 | time_t wmx_time = 0; // ML: made global 311 | // ------------------------ML 312 | static int wmx_pathlen = 0; 313 | 314 | static int 315 | scan_wmx_dir (char *path, int bufindex, int nest) 316 | { 317 | DIR* dir = opendir(path); 318 | struct stat st; 319 | int pathlen = strlen (path); 320 | if (dir) { 321 | struct dirent* ent; 322 | while ((ent=readdir(dir))) { 323 | if (ent->d_name[0] == '.') 324 | continue; 325 | strcpy(path+pathlen, ent->d_name); 326 | if (stat(path, &st) < 0) continue; 327 | int len = pathlen+strlen(ent->d_name); 328 | // worst-case alloc needs 329 | if (bufindex+len+nest+1 > wmxbufsize) 330 | wmxbuffer = (char*)realloc(wmxbuffer, (wmxbufsize+=1024)); 331 | for (int i=0; i toupper(*pB)) 366 | return(1); 367 | if (toupper(*pA) < toupper(*pB)) 368 | return(-1); 369 | pA++; 370 | pB++; 371 | } 372 | if (*pA) 373 | return(1); 374 | if (*pB) 375 | return(-1); 376 | return(0); 377 | } 378 | 379 | static void 380 | load_wmx() 381 | { 382 | const char* home=getenv("HOME"); if (!home) home = "."; 383 | char path[1024]; 384 | strcpy(path, home); 385 | if (path[strlen(path)-1] != '/') strcat(path, "/"); 386 | strcat(path, ".wmx/"); 387 | struct stat st; if (stat(path, &st) < 0) return; 388 | if (st.st_mtime == wmx_time) return; 389 | wmx_time = st.st_mtime; 390 | num_wmx = 0; 391 | wmx_pathlen = strlen(path); 392 | scan_wmx_dir(path, 0, 0); 393 | 394 | // Build wmxlist 395 | if (num_wmx > wmxlistsize) { 396 | if (wmxlist) 397 | delete [] wmxlist; 398 | wmxlist = new char *[num_wmx]; 399 | wmxlistsize = num_wmx; 400 | } 401 | for (int i=0; itransient_for()) 421 | if (a == c) return 1; 422 | return 0; 423 | } 424 | 425 | void 426 | ShowTabMenu(int tab) 427 | { 428 | 429 | static char beenhere; 430 | if (!beenhere) { 431 | beenhere = 1; 432 | Fl::set_labeltype(FRAME_LABEL, frame_label_draw, frame_label_measure); 433 | Fl::set_labeltype(TEXT_LABEL, label_draw, label_measure); 434 | if (exit_flag) { 435 | Fl_Menu_Item* m = other_menu_items+num_other_items-2; 436 | m->label("Exit"); 437 | m->callback(exit_cb); 438 | } 439 | } 440 | 441 | static Fl_Menu_Item* menu = 0; 442 | static int arraysize = 0; 443 | 444 | #if DESKTOPS 445 | int one_desktop = !Desktop::first->next; 446 | #endif 447 | 448 | // count up how many items are on the menu: 449 | 450 | int n = num_other_items; 451 | #if WMX_MENU_ITEMS 452 | load_wmx(); 453 | if (num_wmx) { 454 | n -= 1; // delete "new xterm" 455 | // add wmx items 456 | int level = 0; 457 | for (int i=0; i level) 463 | level++; 464 | n++; 465 | } 466 | } 467 | #endif 468 | 469 | #if DESKTOPS 470 | // count number of items per desktop in these variables: 471 | int numsticky = 0; 472 | Desktop* d; 473 | for (d = Desktop::first; d; d = d->next) d->junk = 0; 474 | #endif 475 | 476 | // every frame contributes 1 item: 477 | Frame* c; 478 | for (c = Frame::first; c; c = c->next) { 479 | if (c->state() == UNMAPPED || c->transient_for()) continue; 480 | #if DESKTOPS 481 | if (!c->desktop()) { 482 | numsticky++; 483 | } else { 484 | c->desktop()->junk++; 485 | } 486 | #endif 487 | n++; 488 | } 489 | 490 | #if DESKTOPS 491 | if (!one_desktop) { 492 | // add the sticky "desktop": 493 | n += 2; if (!numsticky) n++; 494 | if (Desktop::current()) { 495 | n += numsticky; 496 | Desktop::current()->junk += numsticky; 497 | } 498 | // every desktop contributes menu title, null terminator, 499 | // and possible delete: 500 | for (d = Desktop::first; d; d = d->next) { 501 | n += 2; if (!d->junk) n++; 502 | } 503 | } 504 | #endif 505 | 506 | if (n > arraysize) { 507 | delete[] menu; 508 | menu = new Fl_Menu_Item[arraysize = n]; 509 | memset(menu, 0, n * sizeof(Fl_Menu_Item)); 510 | } 511 | 512 | // build the menu: 513 | n = 0; 514 | const Fl_Menu_Item* preset = 0; 515 | const Fl_Menu_Item* first_on_desk = 0; 516 | #if DESKTOPS 517 | if (one_desktop) { 518 | #endif 519 | for (c = Frame::first; c; c = c->next) { 520 | if (c->state() == UNMAPPED || c->transient_for()) continue; 521 | init(menu[n],(char*)c); 522 | menu[n].labeltype(FRAME_LABEL); 523 | menu[n].callback(frame_callback, c); 524 | if (is_active_frame(c)) preset = menu+n; 525 | n++; 526 | } 527 | if (n > 0) first_on_desk = menu; 528 | #if DESKTOPS 529 | } else for (d = Desktop::first; ; d = d->next) { 530 | // this loop adds the "sticky" desktop last, when d==0 531 | if (d == Desktop::current()) preset = menu+n; 532 | init(menu[n], d ? d->name() : "Sticky"); 533 | menu[n].callback(desktop_cb, d); 534 | menu[n].flags = FL_SUBMENU; 535 | n++; 536 | if (d && !d->junk) { 537 | init(menu[n],"delete this desktop"); 538 | menu[n].callback(delete_desktop_cb, d); 539 | n++; 540 | } else if (!d && !numsticky) { 541 | init(menu[n],"(empty)"); 542 | menu[n].callback_ = 0; 543 | menu[n].deactivate(); 544 | n++; 545 | } else { 546 | if (d == Desktop::current()) first_on_desk = menu+n; 547 | for (c = Frame::first; c; c = c->next) { 548 | if (c->state() == UNMAPPED || c->transient_for()) continue; 549 | if ((c->desktop() == d) || (!c->desktop() && (d == Desktop::current()))) { 550 | init(menu[n],(char*)c); 551 | init(menu[n],(char*)c); 552 | menu[n].labeltype(FRAME_LABEL); 553 | menu[n].callback(d == Desktop::current() ? 554 | frame_callback : move_frame_callback, c); 555 | if (d == Desktop::current() && is_active_frame(c)) preset = menu+n; 556 | n++; 557 | } 558 | } 559 | } 560 | menu[n].label(0); n++; // terminator for submenu 561 | if (!d) break; 562 | } 563 | #endif 564 | 565 | // For ALT+Tab, move the selection forward or backward: 566 | if (tab > 0 && first_on_desk) { 567 | if (!preset) 568 | preset = first_on_desk; 569 | else { 570 | preset++; 571 | if (!preset->label() || preset->callback_ != frame_callback) 572 | preset = first_on_desk; 573 | } 574 | } else if (tab < 0 && first_on_desk) { 575 | if (preset && preset != first_on_desk) 576 | preset--; 577 | else { 578 | // go to end of menu 579 | preset = first_on_desk; 580 | while (preset[1].label() && preset[1].callback_ == frame_callback) 581 | preset++; 582 | } 583 | } 584 | 585 | #if WMX_MENU_ITEMS 586 | // put wmx-style commands above that: 587 | if (num_wmx > 0) { 588 | char* cmd; 589 | int pathlen[MAX_NESTING_DEPTH]; 590 | int level = 0; 591 | pathlen[0] = wmx_pathlen; 592 | for (int i = 0; i < num_wmx; i++) { 593 | cmd = wmxlist[i]; 594 | cmd += strspn(cmd, "/")-1; 595 | init(menu[n], cmd+pathlen[level]); 596 | #if DESKTOPS 597 | if (one_desktop) 598 | #endif 599 | if (!level) 600 | menu[n].labeltype(TEXT_LABEL); 601 | int nextlev = (i==num_wmx-1)?0:strspn(wmxlist[i+1], "/")-1; 602 | if (nextlev < level) { 603 | menu[n].callback(spawn_cb, cmd); 604 | // Close 'em off 605 | for (; level>nextlev; level--) 606 | init(menu[++n], 0); 607 | } else if (nextlev > level) { 608 | // This should be made a submenu 609 | pathlen[++level] = strlen(cmd)+1; // extra for next trailing / 610 | menu[n].flags = FL_SUBMENU; 611 | menu[n].callback((Fl_Callback*)0); 612 | } else { 613 | menu[n].callback(spawn_cb, cmd); 614 | } 615 | n++; 616 | } 617 | } 618 | 619 | // put the fixed menu items at the bottom: 620 | #if XTERM_MENU_ITEM 621 | if (num_wmx) // if wmx commands, delete the built-in xterm item: 622 | memcpy(menu+n, other_menu_items+1, sizeof(other_menu_items)-sizeof(Fl_Menu_Item)); 623 | else 624 | #endif 625 | #endif 626 | memcpy(menu+n, other_menu_items, sizeof(other_menu_items)); 627 | #if DESKTOPS 628 | if (one_desktop) 629 | #endif 630 | // fix the menus items so they are indented to align with window names: 631 | while (menu[n].label()) menu[n++].labeltype(TEXT_LABEL); 632 | 633 | const Fl_Menu_Item* picked = 634 | menu->popup(Fl::event_x(), Fl::event_y(), 0, preset); 635 | if (picked && picked->callback()) picked->do_callback(0); 636 | } 637 | 638 | void ShowMenu() {ShowTabMenu(0);} 639 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | flwm Version 0.25 2 | 3 | ---------------------------------------------------------------- 4 | How to compile flwm: 5 | ---------------------------------------------------------------- 6 | 7 | You need fltk. If you do not have it yet, download it from 8 | http://www.fltk.org, and compile and install it. 9 | 10 | To customize flwm (for instance to turn on click-to-type), edit the 11 | config.h file. 12 | 13 | Type "./configure" (not necessary if you have gmake) 14 | 15 | Type "make" 16 | 17 | Become superuser and type "make install" 18 | 19 | If you wish to edit the code, type "make depend" 20 | 21 | ---------------------------------------------------------------- 22 | How to run flwm: 23 | ---------------------------------------------------------------- 24 | 25 | To run flwm as your login script, you need to create or replace 26 | ~/.xinitrc or ~/.xsession (or both). Newer Linux systems with a login 27 | panel use .xsession, older systems where X was started after login 28 | use .xinitrc. You may also have to pick "default" from the "type of 29 | session" popup in your login window. 30 | 31 | The .xinitrc or .xsession file should look like this: 32 | 33 | #!/bin/sh 34 | xsetroot -solid \#006060 35 | xrdb .Xresources 36 | # xset, xmodmap, other configuration programs 37 | flwm & 38 | WindowManager=$! 39 | # xterm, other automatically-launched programs 40 | wait $WindowManager 41 | 42 | ALLOWING THE WINDOW MANAGER TO EXIT W/O LOGOUT: 43 | 44 | That is the most user-friendly but it logs you out when flwm exits, 45 | which means it logs out if flwm crashes (:-)) and you cannot switch 46 | window managers. Another possibility is to run another program last 47 | so flwm can exit, by putting lines like this at the end: 48 | 49 | /usr/local/bin/flwm -x & 50 | exec rxvt -geometry 80x11+8-8 -C -T "Ctrl-D_to_logout" 51 | 52 | The -x tells flwm to put "exit" on the menu rather than "logout". 53 | 54 | REDHAT USERS: 55 | 56 | You may want to run the program "./flwm_wmconfig". This will read 57 | RedHat's window manager menu configuration files and build an initial 58 | .wmx directory so you have a large set of menu items that run 59 | programs. 60 | 61 | SGI IRIX: 62 | 63 | You need to edit the file ~/.xsession instead of ~/.xinitrc. 64 | 65 | SGI's version of XDM has a nice feature so that the window manager can 66 | still have a logout command, but you are not logged out if it 67 | crashes. This is done by running the programs "reaper" and 68 | "endsession", as in this sample .xsession file: 69 | 70 | #! /bin/sh 71 | xsetroot -solid \#004040 72 | xrdb .Xresources 73 | reaper 74 | flwm -x & 75 | xwsh -console -t console -iconic & 76 | 77 | Also create the file "~/.wmx/Logout" with these contents: 78 | 79 | #! /bin/sh 80 | endsession 81 | 82 | The result will be that flwm has a menu itme "Logout" that logs you 83 | out. 84 | 85 | ---------------------------------------------------------------- 86 | Usage: 87 | ---------------------------------------------------------------- 88 | 89 | Type "man flwm" for the manual page. 90 | 91 | ---------------------------------------------------------------- 92 | Acknoledgements 93 | ---------------------------------------------------------------- 94 | 95 | This program was inspired by and much code copied from the "wm2" 96 | window manager by Chris Cannam 97 | 98 | Code contributions by Steve );Hara-Smith 99 | 100 | ---------------------------------------------------------------- 101 | Copyright (C) 1998-1999 Bill Spitzak 102 | ---------------------------------------------------------------- 103 | This program is free software; you can redistribute it and/or modify 104 | it under the terms of the GNU General Public License as published by 105 | the Free Software Foundation; either version 2 of the License, or (at 106 | your option) any later version. 107 | 108 | This program is distributed in the hope that it will be useful, but 109 | WITHOUT ANY WARRANTY; without even the implied warranty of 110 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 111 | General Public License for more details. 112 | 113 | You should have received a copy of the GNU General Public License 114 | along with this library; if not, write to the Free Software 115 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 116 | USA. 117 | 118 | Written by Bill Spitzak spitzak@d2.com 119 | ---------------------------------------------------------------- 120 | END 121 | ---------------------------------------------------------------- 122 | -------------------------------------------------------------------------------- /compileit: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | PROCESSOR_TYPE=`uname -m` 4 | echo "$PROCESSOR_TYPE detected." 5 | 6 | case "$PROCESSOR_TYPE" in 7 | i686) 8 | CXXFLAGS="-flto -fuse-linker-plugin -march=i486 -mtune=i686 -Os -pipe -fno-exceptions -fno-rtti" 9 | LDFLAGS="-Wl,-O1" 10 | ;; 11 | 12 | x86_64) 13 | CXXFLAGS="-flto -fuse-linker-plugin -mtune=generic -Os -pipe -fno-exceptions -fno-rtti" 14 | LDFLAGS="-Wl,-O1" 15 | ;; 16 | 17 | armv*) 18 | CXXFLAGS="-march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -Os -pipe -fno-exceptions -fno-rtti" 19 | LDFLAGS="-Wl,-O1" 20 | ;; 21 | 22 | aarch64) 23 | CXXFLAGS="-march=armv8-a+crc -mtune=cortex-a72 -Os -pipe -fno-exceptions -fno-rtti" 24 | LDFLAGS="-Wl,-O1" 25 | ;; 26 | 27 | *) 28 | echo "$PROCESSOR_TYPE: Unknown processor type. CXXFLAGS and LDFLAGS may need to be adjusted." 29 | ;; 30 | esac 31 | 32 | if [ "$(fltk-config --ldflags | grep Xft)" ]; then 33 | CPPFLAGS="-DHAVE_XFT" 34 | fi 35 | 36 | CXXFLAGS="$CXXFLAGS `fltk-config --cxxflags`" 37 | CXXFLAGS="$CXXFLAGS -Wall -ffunction-sections -fdata-sections -Wno-strict-aliasing" 38 | LDFLAGS="$LDFLAGS `fltk-config --ldflags --use-images`" 39 | LDFLAGS="$LDFLAGS -Wl,-gc-sections" 40 | 41 | echo -e "\nCXXFLAGS=$CXXFLAGS\n" 42 | echo -e "LDFLAGS=$LDFLAGS\n" 43 | echo -e "CPPFLAGS=$CPPFLAGS\n" 44 | 45 | echo Building flwm... 46 | g++ -o flwm *.C $CXXFLAGS $LDFLAGS $CPPFLAGS 47 | sstrip flwm 48 | 49 | echo Building flwm_topside... 50 | g++ -o flwm_topside -DTOPSIDE *.C $CXXFLAGS $LDFLAGS $CPPFLAGS 51 | sstrip flwm_topside 52 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* flwm - config.h 2 | 3 | You can edit these symbols to change the behavior & appearance of flwm. 4 | Turning off features will make flwm smaller too! 5 | 6 | CHANGES 7 | 20160410 - add WMX_DESK_METAKEYS as altenative to WMX_DESK_HOTKEYS; 8 | dentonlt 9 | 20160411 - add RESIZE_ANCHOR_TOPLEFT; dentonlt 10 | 20160506 - add EXTENDED_MOVEMENT_KEYS; dentonlt 11 | */ 12 | 13 | //ML Start--- 14 | 15 | // ML: My hotkeys (comment out to disable): 16 | // CTRL+ALT+arrow keys: move window that direction 4 or more pixels 17 | #define ML_HOTKEYS 1 18 | 19 | // ML: Use environment variable to set titlebar color: 20 | #define ML_TITLEBAR_COLOR 1 21 | 22 | //-- End ML 23 | 24 | // BEHAVIOR: 25 | 26 | // Turn this on for click-to-type (rather than point-to-type). 27 | // On: clicking on the window gives it focus 28 | // Off: pointing at the window gives it the focus. 29 | #define CLICK_TO_TYPE 1 30 | 31 | // Most window managers consider this and click-to-type equivalent. 32 | // On: clicking anywhere on window raises it 33 | // Off: you have to click on the border to raise window 34 | #define CLICK_RAISES CLICK_TO_TYPE 35 | 36 | // For point-to-type, sticky focus means you don't lose the focus 37 | // until you move the cursor to another window that wants focus. 38 | // If this is off you lose focus as soon as the cursor goes outside 39 | // the window (such as to the desktop): 40 | // #define STICKY_FOCUS 1 41 | 42 | // For point-to-type, after this many seconds the window is raised, 43 | // nothing is done if this is not defined: 44 | // #define AUTO_RAISE 0.5 45 | 46 | // Perform "smart" autoplacement. 47 | // New windows are put at positions where they cover as few existing windows 48 | // as possible. A brute force algorithm is used, so it consumes quite a bit 49 | // of CPU time. 50 | #define SMART_PLACEMENT 1 51 | 52 | // set this to zero to remove the multiple-desktop code. This will 53 | // make flwm about 20K smaller 54 | #define DESKTOPS 1 55 | 56 | // set this to zero for "new desktop" to just create one without asking 57 | // for a name. This saves 12K or so of fltk input field code: 58 | #define ASK_FOR_NEW_DESKTOP_NAME 0 59 | 60 | // wm2 has no close box, for good reasons. Unfortunately too many programs 61 | // assume there is one and have no other way to close a window. For a more 62 | // "pure" implementation set this to zero: 63 | #define CLOSE_BOX 1 64 | 65 | // set this to zero to remove the minimize/window-shade button: 66 | #define MINIMIZE_BOX 1 67 | 68 | // If this is false the minimize button only changes the width of the 69 | // window, so it acts like a Mac window shade. This was the original 70 | // behavior, the new version makes it smaller vertically so it is just 71 | // big enough to show the window title: 72 | #define MINIMIZE_HEIGHT 1 73 | 74 | // When using keystrokes to adjusting window width, flwm extends the 75 | // window on its right side. By default, vertical changes stretch the 76 | // window top (bottom left corner of window is anchored). To make flwm 77 | // stretch downward instead (anchor top left corner), set this value: 78 | // #define RESIZE_ANCHOR_TOPLEFT 1 79 | 80 | // Read links from ~/.wmx to make menu items to run programs: 81 | #define WMX_MENU_ITEMS 1 82 | 83 | // Menu item to run a new xterm (if no wmx items found): 84 | #define XTERM_MENU_ITEM 1 85 | 86 | // Hotkeys (see Hotkeys.C for exactly what they do): 87 | #define STANDARD_HOTKEYS 1 // alt+esc, alt+tab, alt+shift+tab 88 | #define KWM_HOTKEYS 1 // ctrl+tab and ctrl+Fn for desktop switching 89 | #define CDE_HOTKEYS defined(__sgi) // alt+fn do actions like raise/lower/close 90 | #define WMX_HOTKEYS 1 // alt+up/down/enter/delete 91 | 92 | // Extended Movement Keys: press ctrl+alt+shift+[arrow/enter] to send 93 | // window to max left/right/up/down or center 94 | // #define EXTENDED_MOVEMENT_KEYS 1 95 | 96 | // enable one of these two for prev/next desktop switching. If both are 97 | // set true here, only WMX_DESK_HOTKEYS will be implemented. 98 | #define WMX_DESK_HOTKEYS 0 // alt+left/right (conflict with Netscape) 99 | #define WMX_DESK_METAKEYS 0 // meta+left/right for prev/next desktop 100 | 101 | #define DESKTOP_HOTKEYS 0 // alt+fn goes to desktop n 102 | 103 | //////////////////////////////////////////////////////////////// 104 | // APPEARANCE: 105 | 106 | // Color for active window title bar (also for selected menu items): 107 | // If not defined, no active window title highlighting is done. 108 | #if CLICK_TO_TYPE 109 | #define ACTIVE_COLOR 0xF0F0F0 110 | #endif 111 | //#define ACTIVE_COLOR 0x000008 112 | 113 | // thickness of the border edge on each side (includes XBORDER): 114 | #define LEFT 3 115 | #define RIGHT 4 116 | #define TOP 2 117 | #define BOTTOM 4 118 | 119 | // font for titles (if not set, helvetica bold is used): 120 | // If this name is specific enough the font size is ignored. 121 | //#define TITLE_FONT "-*-helvetica-medium-r-normal--*" 122 | #define TITLE_FONT_SIZE 14 123 | // thickness of title bar (frame border thickness is added to it): 124 | #define TITLE_WIDTH (TITLE_FONT_SIZE+1) 125 | #ifdef TOPSIDE 126 | #define TITLE_HEIGHT TITLE_WIDTH //ML 127 | #endif 128 | 129 | // size & position of buttons (must fit in title bar): 130 | #ifdef TOPSIDE 131 | #define BUTTON_W TITLE_HEIGHT 132 | #define BUTTON_H BUTTON_W 133 | #define BUTTON_LEFT LEFT 134 | #define BUTTON_TOP TOP 135 | #define BUTTON_RIGHT RIGHT 136 | #else 137 | #define BUTTON_W TITLE_WIDTH 138 | #define BUTTON_H BUTTON_W 139 | #define BUTTON_LEFT LEFT 140 | #define BUTTON_TOP TOP 141 | #define BUTTON_BOTTOM BOTTOM 142 | #endif 143 | 144 | extern int TitleFontSz; 145 | extern int TitleSz; 146 | extern int ButtonSz; 147 | extern int LftSz; 148 | extern int RgtSz; 149 | extern int TopSz; 150 | extern int BtmSz; 151 | extern float sf; //scale factor from Xft.dpi 152 | 153 | // how many pixels from edge for resize handle: 154 | #define RESIZE_EDGE 5 155 | // set this to zero to disable resizing by grabbing left/top edge: 156 | #define RESIZE_TITLE_EDGE 1 // ML 157 | 158 | // must drag window this far off screen to snap the border off the screen: 159 | #define EDGE_SNAP 50 160 | // must drag window this far off screen to actually move it off screen: 161 | #define SCREEN_SNAP 100 162 | 163 | // button decorations: 164 | #define CLOSE_X 1 // windoze-style X in close button 165 | #define CLOSE_HITTITE_LIGHTNING 0 // The ancient Hittite symbol for lightning 166 | #define ICONIZE_BOX 1 // small box in iconize button 167 | #define MINIMIZE_ARROW 1 // minimize button draws a <- rather than | 168 | 169 | // default colors for cursor: 170 | #ifdef __sgi 171 | #define CURSOR_FG_COLOR 0xff0000 172 | #else 173 | #define CURSOR_FG_COLOR 0x000000 174 | #endif 175 | #define CURSOR_BG_COLOR 0xffffff 176 | 177 | // "Clock in the title bar" code contributed by Kevin Quick 178 | // : 179 | 180 | // Add a clock to the active window's title bar using specified 181 | // strftime fmt Note: in keeping with the minimalistic, fast, and 182 | // small philosophy of the flwm, the clock will only be updated 183 | // once/minute so any display of seconds is frivolous. 184 | //#define SHOW_CLOCK "%I:%M %p %Z" 185 | 186 | // We also support the concept of a clock alarm. The alarm is 187 | // triggered by delivering SIGALRM to flwm and cleared by delivering 188 | // SIGCONT to flwm. When the alarm is active, the foreground and 189 | // background colors of the clock display are determined by the 190 | // following settings. (The following are unused if SHOW_CLOCK is not 191 | // defined). 192 | #define ALARM_FG_COLOR 0x00ffff 193 | #define ALARM_BG_COLOR 0xff0000 194 | 195 | //////////////////////////////////////////////////////////////// 196 | // MENU APPEARANCE: 197 | 198 | #define MAX_MENU_WIDTH 300 199 | 200 | // size of the little pictures in the menu: 201 | #define MENU_ICON_W 18 202 | #define MENU_ICON_H 15 203 | 204 | // font to use in menus (if not set helvetica is used): 205 | //#define MENU_FONT "-*-helvetica-medium-r-normal--*" 206 | #define MENU_FONT_SIZE 14 207 | 208 | //////////////////////////////////////////////////////////////// 209 | // You probably don't want to change any of these: 210 | 211 | #ifdef TITLE_FONT 212 | #define TITLE_FONT_SLOT FL_FREE_FONT 213 | #else 214 | #define TITLE_FONT_SLOT FL_BOLD 215 | #endif 216 | 217 | #ifdef MENU_FONT 218 | #define MENU_FONT_SLOT Fl_Font(FL_FREE_FONT+1) 219 | #else 220 | #define MENU_FONT_SLOT FL_HELVETICA 221 | #endif 222 | 223 | #define CURSOR_BG_SLOT Fl_Color(30) 224 | #define CURSOR_FG_SLOT Fl_Color(31) 225 | -------------------------------------------------------------------------------- /flwm.1: -------------------------------------------------------------------------------- 1 | .\"Man page for flwm, by Bill Spitzak. 2 | .TH flwm 1 "15 May 1999" 3 | .SH NAME 4 | \fIflwm\fR - The Fast Light Window Manager 5 | .SH SYNOPSIS 6 | .B flwm 7 | [-d[isplay] host:n.n] [-g[eometry] WxH+X+Y] 8 | [-fg color] [-bg color] [-bg2 color] 9 | .SH DESCRIPTION 10 | .I flwm 11 | is a very small and fast X window manager, featuring 12 | .I no 13 | icons and "sideways" title bars. 14 | 15 | .SH .xinitrc / .xsession 16 | 17 | To run flwm as your login script, you need to create or replace 18 | ~/.xinitrc or ~/.xsession (or both). Newer Linux systems with a login 19 | panel use .xsession, older systems where X was started after login 20 | use .xinitrc. You may also have to pick "default" from the "type of 21 | session" popup in your login window. 22 | 23 | The .xinitrc or .xsession file should look like this: 24 | 25 | .nf 26 | #!/bin/sh 27 | xsetroot -solid \\#006060 28 | xrdb .Xresources 29 | # xset, xmodmap, other configuration programs 30 | flwm & 31 | WindowManager=$! 32 | # xterm, other automatically-launched programs 33 | wait $WindowManager 34 | .fi 35 | 36 | .SH SWITCHES 37 | 38 | .B -d[isplay] host:#.# 39 | Sets the display and screen for flwm to manage 40 | 41 | .B -v[isual] # 42 | Visual number to use (probably only works for non-color-mapped ones) 43 | 44 | .B -g[eometry] WxH+X+Y 45 | Flwm will act as though the screen is only the specified area. It 46 | will constrain initial window positions to this area and stop them at 47 | the edges when dragging them around. This can be used to surround the 48 | screen with fixed "toolbars" that are never covered by windows. These 49 | toolbars must be created by a program using override-redirect so that 50 | flwm does not try to move them. 51 | 52 | .B -m[aximum] WxH 53 | Set the size of windows when the maximize buttons are pushed. 54 | Normally this is the size of the screen. This is useful for 55 | XFree86 servers that are run with a smaller screen than display 56 | memory. 57 | 58 | .B -x 59 | The menu will say "Exit" instead of "Logout" and will not ask for 60 | confirmation. This is a good idea if you are running flwm in some 61 | other way than with exec at the end of .xinitrc, since it won't log 62 | you out then. 63 | 64 | .B -fg color, -bg color 65 | Set the label color and the color of the window frames and the 66 | menu. 67 | 68 | .B -c[ursor] # 69 | What cursor to use on the desktop (you will have to experiment to find 70 | out what each number means) 71 | 72 | .B -cfg color, -cbg color 73 | Colors for the desktop and window resizing cursors 74 | 75 | In addition to these switches there is much customization that can be 76 | done by editing the config.h file in the source code and recompiling. 77 | GCC is your friend. 78 | 79 | .SH MENU ITEMS 80 | 81 | Flwm can launch programs from its menu. This is controlled by files 82 | in the directory 83 | .B ~/.wmx 84 | (this was chosen to be compatible with wmx and wm2). 85 | 86 | Each executable file in ~/.wmx is a program to run. Usually these are 87 | symbolic links to the real program or very short shell scripts. 88 | 89 | Each subdirectory creates a child menu so you can build a hierarchy 90 | (up to 10 deep). 91 | 92 | Cut and paste the following lines you your shell to create some 93 | example files: 94 | 95 | .nf 96 | mkdir ~/.wmx 97 | ln -s /usr/bin/gimp ~/.wmx/"The Gimp" 98 | cat << EOF > ~/.wmx/"Terminal" 99 | #! /bin/sh 100 | /usr/local/bin/rxvt -ut 101 | EOF 102 | chmod +x !* 103 | .fi 104 | 105 | RedHat users can run the program 106 | .B flwm_wmconfig 107 | to read the /etc/X11/wmconfig directory and produce an initial set of 108 | menu items. 109 | 110 | .SH MOUSE USAGE 111 | 112 | .B Left-click 113 | on a window border raises window. 114 | 115 | .B Left-drag 116 | will move the window when in the title bar, and will resize it in the 117 | edges. If the window cannot be resized then it will always move the 118 | window. What it will do is indicated by the cursor shape. 119 | 120 | .B Middle-click 121 | on a window border lowers it to bottom. 122 | 123 | .B Middle-drag 124 | anywhere on window border will move the window. 125 | 126 | When you move a window it will stop at the edges of the screen. 127 | Dragging about 150 pixels further will unstick it and let you drag it 128 | off the screen. 129 | 130 | .B Right-click 131 | on a window border pops up the menu. 132 | 133 | .B Any button 134 | on the desktop will pop up the menu. 135 | 136 | .SH BUTTONS 137 | 138 | The empty button "iconizes" the window: it will completely vanish. To 139 | get it back use the menu. 140 | 141 | The vertical-bar button "shades" (or "Venetian blinds"?) the window. 142 | Click it again to restore the window. You can also resize the shaded 143 | window to a new height or "open" it by resizing horizontally. 144 | 145 | The two buttons below it toggle maximum height and/or maximum width. 146 | 147 | The X button at the bottom closes the window. 148 | 149 | .SH MENU 150 | 151 | .B Right-click 152 | on window border, or 153 | .B any-click 154 | on the desktop, or typing 155 | .B Alt+Esc 156 | or 157 | .B Alt+Tab 158 | or 159 | .B Alt+Shift+Tab 160 | will pop up the menu. 161 | 162 | Releasing Alt will pick the current menu item. This makes flwm work 163 | very much (exactly?) like the Windows 95 shortcuts. 164 | 165 | Each main window is a menu item. If the window is "iconized" the 166 | little picture shows an open rectangle, otherwise it shows a filled 167 | rectangle. Picking a menu item deiconizes and raises that window and 168 | warps the pointer so it is current. 169 | 170 | .B New desktop 171 | asks for a name of a new desktop and makes it current. The desktop 172 | will initially be empty (except for sticky items). 173 | 174 | To move windows to the current desktop, pop up the menu and pick 175 | windows off of other desktops (if using the keyboard, use left 176 | arrow to go to the desktop names, move up and down to the other 177 | desktop, and use right arrow to enter that desktop). The window will 178 | be moved from the other desktop to the current one. 179 | 180 | To switch to another desktop, pick the title of the desktop (if using 181 | the keyboard, use left arrow to go to the desktop names, move up and 182 | down to the other desktop). 183 | 184 | If a desktop is empty you can delete it. Its sub menu will show 185 | .B delete this desktop. 186 | Pick that and the desktop is gone. 187 | 188 | .B Sticky 189 | is a special "desktop": windows on it appear on all desktops. To make 190 | a window "sticky" switch to the Sticky desktop and pick the window off 191 | its current desktop (thus "moving" it to the Sticky desktop). To 192 | "unstick" a window go to another desktop and pick the window off the 193 | sticky desktop menu. 194 | 195 | .B New xterm 196 | will run a new xterm on the current desktop. Useful if 197 | you accidentally close everything. This item does not appear if a 198 | ~/.wmx directory exists. 199 | 200 | .B Logout 201 | will ask for confirmation and if so flwm will exit. 202 | 203 | .B Exit 204 | will exit flwm without confirmation. This item will appear if flwm 205 | was run with the -x switch. 206 | 207 | .SH HOT KEYS 208 | 209 | These are the defaults, the hot keys may be different depending on how 210 | flwm was compiled: 211 | 212 | .B Alt+Escape 213 | Pops up the menu with the current window preselected 214 | 215 | .B Alt+Tab 216 | Pops up the menu with the next window preselected 217 | 218 | .B Alt+Shift+Tab 219 | Pops up the menu with the previous window preselected 220 | 221 | .B Ctrl+Tab 222 | Switch to the next desktop. 223 | 224 | .B Ctrl+Shift+Tab 225 | Switch to the previous desktop. 226 | 227 | .B Ctrl+Function key 228 | Switch to desktop N. 229 | 230 | .B Alt+Up 231 | Raise the current window. 232 | 233 | .B Alt+Down 234 | Lower the current window. 235 | 236 | .B Alt+Delete 237 | Close the current window (same as clicking close box). 238 | 239 | .B Alt+Enter 240 | "Iconizes" (hides) the current window. 241 | 242 | .SH BUGS 243 | 244 | It is impossible to move windows smaller than 100 pixels off 245 | the screen. 246 | 247 | Only obeys "keep aspect" if the aspect ratio is 1x1. 248 | 249 | .SH ACKNOWLEDGEMENTS 250 | 251 | This program was inspired by and much code copied from the "wm2" 252 | window manager by Chris Cannam 253 | 254 | Thanks to Ron Koerner for the recursive .wmx directory reading code. 255 | 256 | .SH COPYRIGHT 257 | 258 | Copyright (C) 1999 Bill Spitzak 259 | 260 | This program is free software; you can redistribute it and/or modify 261 | it under the terms of the GNU General Public License as published by 262 | the Free Software Foundation; either version 2 of the License, or (at 263 | your option) any later version. 264 | 265 | This program is distributed in the hope that it will be useful, but 266 | WITHOUT ANY WARRANTY; without even the implied warranty of 267 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 268 | General Public License for more details. 269 | 270 | You should have received a copy of the GNU General Public License 271 | along with this library; if not, write to the Free Software 272 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 273 | USA. 274 | 275 | .SH AUTHORS 276 | 277 | Written by Bill Spitzak spitzak@d2.com 278 | -------------------------------------------------------------------------------- /logo.fl: -------------------------------------------------------------------------------- 1 | # data file for the FLTK User Interface Designer (FLUID) 2 | version 2.0100 3 | header_name {.h} 4 | code_name {.cxx} 5 | gridx 5 6 | gridy 5 7 | snap 3 8 | Function {make_window()} {open 9 | } { 10 | {fltk::Window} {} { 11 | label flwm open 12 | xywh {990 285 265 115} visible 13 | } { 14 | {fltk::Group} {} { 15 | label {The Fast Light Window Manager} open selected 16 | xywh {0 0 265 115} align 128 box PLASTIC_UP_BOX labelfont 1 labeltype ENGRAVED_LABEL color 0x7d9dae00 textcolor 0x979b9700 labelcolor 0x393a3900 labelsize 27 17 | } {} 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /main.C: -------------------------------------------------------------------------------- 1 | // flwm: main.cpp 2 | // 3 | // CHANGES 4 | // 20160402: update XkbKeycodeToKeysym() to use XKBlib.h; some 5 | // tests & blocks clarified (add parentheses); dentonlt 6 | // 20190303: Added DoNotWarp variable. 7 | // Added program_version define. Rich 8 | // 9 | // Define "TEST" and it will compile to make a single fake window so 10 | // you can test the window controls. 11 | //#define TEST 1 12 | 13 | #define FL_INTERNALS 1 14 | 15 | #include "Frame.H" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "config.h" 23 | #ifdef SHOW_CLOCK 24 | #include 25 | //#include // ML 26 | #endif 27 | 28 | //ML 29 | #include 30 | #include 31 | int TitleFontSz = TITLE_FONT_SIZE; 32 | int TitleSz = TITLE_FONT_SIZE + 4; 33 | int ButtonSz = TitleSz; 34 | int LftSz = LEFT; 35 | int RgtSz = RIGHT; 36 | int TopSz = TOP; 37 | int BtmSz = BOTTOM; 38 | float sf = 1.0; 39 | 40 | 41 | //////////////////////////////////////////////////////////////// 42 | 43 | static const char* program_name; 44 | static int initializing; 45 | 46 | #define program_version "Version 2.00" 47 | int DoNotWarp=0; // Used to override mouse pointer warping if environmental variable NOWARP exists. 48 | 49 | static int xerror_handler(Display* d, XErrorEvent* e) { 50 | if (initializing && (e->request_code == X_ChangeWindowAttributes) && 51 | e->error_code == BadAccess) 52 | Fl::fatal("Another window manager is running. You must exit it before running %s %s.", program_name, program_version); 53 | #ifndef DEBUG 54 | if (e->error_code == BadWindow) return 0; 55 | if (e->error_code == BadColor) return 0; 56 | #endif 57 | char buf1[128], buf2[128]; 58 | sprintf(buf1, "XRequest.%d", e->request_code); 59 | XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128); 60 | XGetErrorText(d, e->error_code, buf1, 128); 61 | Fl::warning("%s: %s: %s 0x%lx", program_name, buf2, buf1, e->resourceid); 62 | return 0; 63 | } 64 | 65 | //////////////////////////////////////////////////////////////// 66 | // The Fl_Root class looks like a window to fltk but is actually the 67 | // screen's root window. This is done by using set_xid to "show" it 68 | // rather than have fltk create the window. 69 | 70 | class Fl_Root : public Fl_Window { 71 | int handle(int); 72 | public: 73 | Fl_Root() : Fl_Window(0,0,Fl::w(),Fl::h()) { 74 | } 75 | void show() { 76 | if (!shown()) Fl_X::set_xid(this, RootWindow(fl_display, fl_screen)); 77 | } 78 | void flush() {} 79 | }; 80 | Fl_Window *Root; 81 | 82 | extern void ShowMenu(); 83 | extern int Handle_Hotkey(); 84 | extern void Grab_Hotkeys(); 85 | 86 | int Fl_Root::handle(int e) { 87 | if (e == FL_PUSH) { 88 | ShowMenu(); 89 | return 1; 90 | } 91 | return 0; 92 | } 93 | 94 | #if CLICK_RAISES || CLICK_TO_TYPE 95 | extern void click_raise(Frame*); 96 | #endif 97 | 98 | // fltk calls this for any events it does not understand: 99 | static int flwm_event_handler(int e) { 100 | 101 | if (Fl::event_key()==FL_Escape) { 102 | return 1; 103 | } 104 | 105 | if (!e) { // XEvent that fltk did not understand. 106 | XWindow window = fl_xevent->xany.window; 107 | // unfortunately most of the redirect events put the interesting 108 | // window id in a different place: 109 | switch (fl_xevent->type) { 110 | case CirculateNotify: 111 | case CirculateRequest: 112 | case ConfigureNotify: 113 | case ConfigureRequest: 114 | case CreateNotify: 115 | case DestroyNotify: 116 | case GravityNotify: 117 | case MapNotify: 118 | case MapRequest: 119 | case ReparentNotify: 120 | case UnmapNotify: 121 | window = fl_xevent->xmaprequest.window; 122 | } 123 | 124 | for (Frame* c = Frame::first; c; c = c->next) { 125 | if (c->window() == window || fl_xid(c) == window) { 126 | #if CLICK_RAISES || CLICK_TO_TYPE 127 | if (fl_xevent->type == ButtonPress) {click_raise(c); return 1;} 128 | else 129 | #endif 130 | return c->handle(fl_xevent); 131 | } 132 | } // end for each child window 133 | 134 | switch (fl_xevent->type) { 135 | case ButtonPress: 136 | printf("got a button press in main\n"); 137 | return 0; 138 | case ConfigureRequest: { 139 | const XConfigureRequestEvent *e = &(fl_xevent->xconfigurerequest); 140 | XConfigureWindow(fl_display, e->window, 141 | e->value_mask&~(CWSibling|CWStackMode), 142 | (XWindowChanges*)&(e->x)); 143 | return 1;} 144 | case MapRequest: { 145 | const XMapRequestEvent* e = &(fl_xevent->xmaprequest); 146 | (void)new Frame(e->window); 147 | return 1;} 148 | // this was needed for *some* earlier versions of fltk 149 | case KeyRelease: 150 | if (!Fl::grab()) return 0; 151 | Fl::e_keysym = 152 | XkbKeycodeToKeysym(fl_display, fl_xevent->xkey.keycode, 0, 0); 153 | goto KEYUP; 154 | } // end switch(fl_xevent->type) 155 | 156 | } // end if (!e) 157 | else if (e == FL_KEYUP) { 158 | KEYUP: 159 | if (!Fl::grab()) return 0; 160 | // when alt key released, pretend they hit enter & pick menu item 161 | if (Fl::event_key()==FL_Alt_L || Fl::event_key()==FL_Alt_R) { 162 | Fl::e_keysym = FL_Enter; 163 | return Fl::grab()->handle(FL_KEYBOARD); 164 | } 165 | return 0; 166 | } else if (e == FL_SHORTCUT || e == FL_KEYBOARD) { 167 | #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 0 && FL_PATCH_VERSION < 3 168 | // make the tab keys work in the menus in older fltk's: 169 | // (they do not cycle around however, so a new fltk is a good idea) 170 | if (Fl::grab()) { 171 | // make fltk's menus resond to tab + shift+tab: 172 | if (Fl::event_key() == FL_Tab) { 173 | if (Fl::event_state() & FL_SHIFT) goto J1; 174 | Fl::e_keysym = FL_Down; 175 | } else if (Fl::event_key() == 0xFE20) { 176 | J1: Fl::e_keysym = FL_Up; 177 | } else return 0; 178 | return Fl::grab()->handle(FL_KEYBOARD); 179 | } 180 | #endif 181 | return Handle_Hotkey(); 182 | } 183 | return 0; 184 | } 185 | 186 | #if DESKTOPS 187 | extern void init_desktops(); 188 | extern Atom _win_workspace; 189 | extern Atom _win_workspace_count; 190 | extern Atom _win_workspace_names; 191 | #endif 192 | 193 | extern Atom _win_state; 194 | extern Atom _win_hints; 195 | 196 | #ifdef SHOW_CLOCK 197 | int clock_period = 1; 198 | int clock_oldmin = 61; 199 | int clock_alarm_on = 0; 200 | char clock_buf[80]; 201 | 202 | struct sigaction flwm_clock_alarm_start = {0,}, flwm_clock_alarm_stop = {0,}; 203 | 204 | void flwm_update_clock(void*) { 205 | time_t newtime; 206 | struct tm *tm_p; 207 | 208 | // get current time 209 | time(&newtime); 210 | tm_p = localtime(&newtime); 211 | 212 | // Update a window frame if necessary 213 | if (Frame::activeFrame() && tm_p->tm_min != clock_oldmin) { 214 | if (clock_oldmin != 61) 215 | clock_period = 60; // now that we're in sync, only update 1/minute 216 | clock_oldmin = tm_p->tm_min; 217 | strftime(clock_buf, 80, SHOW_CLOCK, tm_p); 218 | Frame::activeFrame()->redraw_clock(); 219 | } 220 | // Now reschedule the timeout 221 | Fl::remove_timeout(flwm_update_clock); 222 | Fl::add_timeout(clock_period, flwm_update_clock); 223 | } 224 | 225 | void flwm_clock_alarm_on(int) { 226 | clock_alarm_on = 1; 227 | Frame::activeFrame()->redraw_clock(); 228 | } 229 | 230 | void flwm_clock_alarm_off(int) { 231 | clock_alarm_on = 0; 232 | Frame::activeFrame()->redraw_clock(); 233 | } 234 | #endif 235 | 236 | static const char* cfg, *cbg; 237 | static int cursor = FL_CURSOR_ARROW; 238 | 239 | // ML ------------ 240 | extern time_t wmx_time; 241 | void request_menu_refresh(int signum) { 242 | if (signum == SIGUSR2) { 243 | wmx_time = 42; // arbitrary value so it won't match st_mtime fire/dir status field 244 | } 245 | } 246 | // -------------ML 247 | static void initialize() { 248 | 249 | Display* d = fl_display; 250 | 251 | #ifdef TEST 252 | XWindow w = XCreateSimpleWindow(d, RootWindow(d, fl_screen), 253 | 100, 100, 200, 300, 10, 254 | BlackPixel(fl_display, 0), 255 | // WhitePixel(fl_display, 0)); 256 | 0x1234); 257 | Frame* frame = new Frame(w); 258 | frame->label("flwm test window"); 259 | XSelectInput(d, w, 260 | ExposureMask | StructureNotifyMask | 261 | KeyPressMask | KeyReleaseMask | FocusChangeMask | 262 | KeymapStateMask | 263 | ButtonPressMask | ButtonReleaseMask | 264 | EnterWindowMask | LeaveWindowMask /*|PointerMotionMask*/ 265 | ); 266 | #else 267 | 268 | Fl::add_handler(flwm_event_handler); 269 | 270 | // setting attributes on root window makes me the window manager: 271 | initializing = 1; 272 | XSelectInput(d, fl_xid(Root), 273 | SubstructureRedirectMask | SubstructureNotifyMask | 274 | ColormapChangeMask | PropertyChangeMask | 275 | ButtonPressMask | ButtonReleaseMask | 276 | EnterWindowMask | LeaveWindowMask | 277 | KeyPressMask | KeyReleaseMask | KeymapStateMask); 278 | Root->cursor((Fl_Cursor)cursor, CURSOR_FG_SLOT, CURSOR_BG_SLOT); 279 | Fl::visible_focus(0); 280 | 281 | #ifdef TITLE_FONT 282 | Fl::set_font(TITLE_FONT_SLOT, TITLE_FONT); 283 | #endif 284 | #ifdef MENU_FONT 285 | Fl::set_font(MENU_FONT_SLOT, MENU_FONT); 286 | #endif 287 | #ifdef ACTIVE_COLOR 288 | Fl::set_color(FL_SELECTION_COLOR, ACTIVE_COLOR<<8); 289 | #endif 290 | 291 | // Gnome crap: 292 | // First create a window that can be watched to see if wm dies: 293 | Atom a = XInternAtom(d, "_WIN_SUPPORTING_WM_CHECK", False); 294 | XWindow win = XCreateSimpleWindow(d, fl_xid(Root), -200, -200, 5, 5, 0, 0, 0); 295 | CARD32 val = win; 296 | XChangeProperty(d, fl_xid(Root), a, XA_CARDINAL, 32, PropModeReplace, (uchar*)&val, 1); 297 | XChangeProperty(d, win, a, XA_CARDINAL, 32, PropModeReplace, (uchar*)&val, 1); 298 | // Next send a list of Gnome stuff we understand: 299 | a = XInternAtom(d, "_WIN_PROTOCOLS", 0); 300 | Atom list[10]; unsigned int i = 0; 301 | //list[i++] = XInternAtom(d, "_WIN_LAYER", 0); 302 | list[i++] = _win_state = XInternAtom(d, "_WIN_STATE", 0); 303 | list[i++] = _win_hints = XInternAtom(d, "_WIN_HINTS", 0); 304 | //list[i++] = XInternAtom(d, "_WIN_APP_STATE", 0); 305 | //list[i++] = XInternAtom(d, "_WIN_EXPANDED_SIZE", 0); 306 | //list[i++] = XInternAtom(d, "_WIN_ICONS", 0); 307 | #if DESKTOPS 308 | list[i++] = _win_workspace = XInternAtom(d, "_WIN_WORKSPACE", 0); 309 | list[i++] = _win_workspace_count = XInternAtom(d, "_WIN_WORKSPACE_COUNT", 0); 310 | list[i++] = _win_workspace_names = XInternAtom(d, "_WIN_WORKSPACE_NAMES", 0); 311 | #endif 312 | //list[i++] = XInternAtom(d, "_WIN_FRAME_LIST", 0); 313 | XChangeProperty(d, fl_xid(Root), a, XA_ATOM, 32, PropModeReplace, (uchar*)list, i); 314 | 315 | Grab_Hotkeys(); 316 | 317 | #ifdef SHOW_CLOCK 318 | Fl::add_timeout(clock_period, flwm_update_clock); 319 | flwm_clock_alarm_start.sa_handler = &flwm_clock_alarm_on; 320 | flwm_clock_alarm_stop.sa_handler = &flwm_clock_alarm_off; 321 | sigaction(SIGALRM, &flwm_clock_alarm_start, NULL); 322 | sigaction(SIGCONT, &flwm_clock_alarm_stop, NULL); 323 | #endif 324 | 325 | // ML ----------- 326 | signal(SIGUSR2, request_menu_refresh); 327 | 328 | // ------------ML 329 | XSync(d, 0); 330 | initializing = 0; 331 | 332 | #if DESKTOPS 333 | init_desktops(); 334 | #endif 335 | 336 | // find all the windows and create a Frame for each: 337 | unsigned int n; 338 | XWindow w1, w2, *wins; 339 | XWindowAttributes attr; 340 | XQueryTree(d, fl_xid(Root), &w1, &w2, &wins, &n); 341 | for (i = 0; i < n; ++i) { 342 | XGetWindowAttributes(d, wins[i], &attr); 343 | if (attr.override_redirect || !attr.map_state) continue; 344 | (void)new Frame(wins[i],&attr); 345 | } 346 | XFree((void *)wins); 347 | 348 | #endif 349 | } 350 | 351 | //////////////////////////////////////////////////////////////// 352 | 353 | extern int exit_flag; 354 | extern int max_w_switch; 355 | extern int max_h_switch; 356 | 357 | // consume a switch from argv. Returns number of words eaten, 0 on error: 358 | int arg(int argc, char **argv, int &i) { 359 | const char *s = argv[i]; 360 | if (s[0] != '-') return 0; 361 | s++; 362 | 363 | // do single-word switches: 364 | if (!strcmp(s,"x")) { 365 | exit_flag = 1; 366 | i++; 367 | return 1; 368 | } 369 | 370 | // do switches with a value: 371 | const char *v = argv[i+1]; 372 | if (i >= argc-1 || !v) 373 | return 0; // all the rest need an argument, so if missing it is an error 374 | 375 | if (!strcmp(s, "cfg")) { 376 | cfg = v; 377 | } else if (!strcmp(s, "cbg")) { 378 | cbg = v; 379 | } else if (*s == 'c') { 380 | cursor = atoi(v); 381 | } else if (*s == 'v') { 382 | int visid = atoi(v); 383 | fl_open_display(); 384 | XVisualInfo templt; int num; 385 | templt.visualid = visid; 386 | fl_visual = XGetVisualInfo(fl_display, VisualIDMask, &templt, &num); 387 | if (!fl_visual) Fl::fatal("No visual with id %d",visid); 388 | fl_colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), 389 | fl_visual->visual, AllocNone); 390 | } else if (*s == 'm') { 391 | max_w_switch = atoi(v); 392 | while (*v && *v++ != 'x'); 393 | max_h_switch = atoi(v); 394 | } else 395 | return 0; // unrecognized 396 | // return the fact that we consumed 2 switches: 397 | i += 2; 398 | return 2; 399 | } 400 | 401 | static void color_setup(Fl_Color slot, const char* arg, ulong value) { 402 | if (arg) { 403 | XColor x; 404 | if (XParseColor(fl_display, fl_colormap, arg, &x)) 405 | value = ((x.red>>8)<<24)|((x.green>>8)<<16)|((x.blue)); 406 | } 407 | Fl::set_color(slot, value); 408 | } 409 | 410 | int main(int argc, char** argv) { 411 | program_name = fl_filename_name(argv[0]); 412 | int i; if (Fl::args(argc, argv, i, arg) < argc) Fl::error( 413 | "%s\n\n" 414 | "options are:\n" 415 | " -d[isplay] host:#.#\tX display & screen to use\n" 416 | " -v[isual] #\t\tvisual to use\n" 417 | " -g[eometry] WxH+X+Y\tlimits windows to this area\n" 418 | " -m[aximum] WxH\t\tsize of maximized windows\n" 419 | " -x\t\t\tmenu says Exit instead of logout\n" 420 | " -bg color\t\tFrame color\n" 421 | " -fg color\t\tLabel color\n" 422 | " -bg2 color\t\tText field color\n" 423 | " -c[ursor] #\t\tCursor number for root\n" 424 | " -cfg color\t\tCursor color\n" 425 | " -cbg color\t\tCursor outline color", program_version 426 | ); 427 | 428 | if(getenv("NOWARP")) // If environmental variable NOWARP exists (value does not matter) 429 | DoNotWarp=1; // Then keep your hands off of my mouse pointer. 430 | 431 | #ifndef FL_NORMAL_SIZE // detect new versions of fltk where this is a variable 432 | FL_NORMAL_SIZE = 14; 433 | #endif 434 | 435 | fl_open_display(); 436 | sf = Fl::screen_scale(0); 437 | TitleFontSz = (int) ((float)TITLE_FONT_SIZE * sf); 438 | TitleSz = (int) ((float)(TITLE_FONT_SIZE + 4) * sf); 439 | if ((TitleSz % 2) == 0) TitleSz++; // make odd! 440 | ButtonSz = TitleSz; 441 | LftSz = (int) ((float)LEFT * sf); 442 | RgtSz = (int) ((float)RIGHT * sf); 443 | TopSz = (int) ((float)TOP * sf); 444 | BtmSz = (int) ((float)BOTTOM * sf); 445 | 446 | for (int i = 0; i < Fl::screen_count(); i++) Fl::screen_scale(i, 1.0); 447 | 448 | color_setup(CURSOR_FG_SLOT, cfg, CURSOR_FG_COLOR<<8); 449 | color_setup(CURSOR_BG_SLOT, cbg, CURSOR_BG_COLOR<<8); 450 | Fl::set_color(FL_SELECTION_COLOR,0,0,128); 451 | Fl_Root root; 452 | Root = &root; 453 | Root->show(argc,argv); // fools fltk into using -geometry to set the size 454 | XSetErrorHandler(xerror_handler); 455 | initialize(); 456 | return Fl::run(); 457 | } 458 | -------------------------------------------------------------------------------- /old/rotated_test.C: -------------------------------------------------------------------------------- 1 | // Test the xvertext routines for rotated text 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | //////////////////////////////////////////////////////////////// 13 | 14 | #include "Rotated.H" 15 | 16 | class RotText : public Fl_Box { 17 | void draw(); 18 | public: 19 | RotText(int X, int Y, int W, int H, const char* L = 0) : 20 | Fl_Box(X,Y,W,H,L) {} 21 | }; 22 | 23 | void RotText::draw() { 24 | draw_box(); 25 | fl_color(FL_BLACK); 26 | fl_font(labelfont(), labelsize()); 27 | draw_rotated90(label(), x(), y(), w(), h(), align()); 28 | } 29 | 30 | //////////////////////////////////////////////////////////////// 31 | 32 | Fl_Toggle_Button *leftb,*rightb,*topb,*bottomb,*insideb,*clipb,*wrapb; 33 | RotText *text; 34 | Fl_Input *input; 35 | Fl_Hor_Value_Slider *fonts; 36 | Fl_Hor_Value_Slider *sizes; 37 | Fl_Double_Window *window; 38 | 39 | void button_cb(Fl_Widget *,void *) { 40 | int i = 0; 41 | if (leftb->value()) i |= FL_ALIGN_LEFT; 42 | if (rightb->value()) i |= FL_ALIGN_RIGHT; 43 | if (topb->value()) i |= FL_ALIGN_TOP; 44 | if (bottomb->value()) i |= FL_ALIGN_BOTTOM; 45 | if (insideb->value()) i |= FL_ALIGN_INSIDE; 46 | if (clipb->value()) i |= FL_ALIGN_CLIP; 47 | if (wrapb->value()) i |= FL_ALIGN_WRAP; 48 | text->align(i); 49 | window->redraw(); 50 | } 51 | 52 | void font_cb(Fl_Widget *,void *) { 53 | text->labelfont(int(fonts->value())); 54 | window->redraw(); 55 | } 56 | 57 | void size_cb(Fl_Widget *,void *) { 58 | text->labelsize(int(sizes->value())); 59 | window->redraw(); 60 | } 61 | 62 | void input_cb(Fl_Widget *,void *) { 63 | text->label(input->value()); 64 | window->redraw(); 65 | } 66 | 67 | int main(int argc, char **argv) { 68 | window = new Fl_Double_Window(400,400); 69 | 70 | input = new Fl_Input(50,0,350,25); 71 | input->static_value("The quick brown fox jumped over the lazy dog."); 72 | input->when(FL_WHEN_CHANGED); 73 | input->callback(input_cb); 74 | 75 | sizes= new Fl_Hor_Value_Slider(50,25,350,25,"Size:"); 76 | sizes->align(FL_ALIGN_LEFT); 77 | sizes->bounds(1,64); 78 | sizes->step(1); 79 | sizes->value(14); 80 | sizes->callback(size_cb); 81 | 82 | fonts=new Fl_Hor_Value_Slider(50,50,350,25,"Font:"); 83 | fonts->align(FL_ALIGN_LEFT); 84 | fonts->bounds(0,15); 85 | fonts->step(1); 86 | fonts->value(0); 87 | fonts->callback(font_cb); 88 | 89 | Fl_Group *g = new Fl_Group(0,0,0,0); 90 | leftb = new Fl_Toggle_Button(50,75,50,25,"left"); 91 | leftb->callback(button_cb); 92 | rightb = new Fl_Toggle_Button(100,75,50,25,"right"); 93 | rightb->callback(button_cb); 94 | topb = new Fl_Toggle_Button(150,75,50,25,"top"); 95 | topb->callback(button_cb); 96 | bottomb = new Fl_Toggle_Button(200,75,50,25,"bottom"); 97 | bottomb->callback(button_cb); 98 | insideb = new Fl_Toggle_Button(250,75,50,25,"inside"); 99 | insideb->callback(button_cb); 100 | wrapb = new Fl_Toggle_Button(300,75,50,25,"wrap"); 101 | wrapb->callback(button_cb); 102 | clipb = new Fl_Toggle_Button(350,75,50,25,"clip"); 103 | clipb->callback(button_cb); 104 | g->resizable(insideb); 105 | g->end(); 106 | 107 | text= new RotText(100,225,200,100,input->value()); 108 | text->box(FL_FRAME_BOX); 109 | text->align(FL_ALIGN_CENTER); 110 | window->resizable(text); 111 | window->end(); 112 | window->show(argc,argv); 113 | return Fl::run(); 114 | } 115 | 116 | // 117 | // End of "$Id: rotated_test.C,v 1.1 2000/01/18 01:05:49 spitzak Exp $". 118 | // 119 | --------------------------------------------------------------------------------