#define FL_INTERNALS 1 #include "Frame.H" #include #include #include #include #include #include "config.h" #ifdef SHOW_CLOCK #include #include #endif //////////////////////////////////////////////////////////////// static const char* program_name; static int initializing; static int xerror_handler(Display* d, XErrorEvent* e) { if (initializing && (e->request_code == X_ChangeWindowAttributes) && e->error_code == BadAccess) Fl::fatal("Another window manager is running. You must exit it before running %s.", program_name); #ifndef DEBUG if (e->error_code == BadWindow) return 0; if (e->error_code == BadColor) return 0; #endif char buf1[128], buf2[128]; sprintf(buf1, "XRequest.%d", e->request_code); XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128); XGetErrorText(d, e->error_code, buf1, 128); Fl::warning("%s: %s: %s 0x%lx", program_name, buf2, buf1, e->resourceid); return 0; } //////////////////////////////////////////////////////////////// // The Fl_Root class looks like a window to fltk but is actually the // screen's root window. This is done by using set_xid to "show" it // rather than have fltk create the window. class Fl_Root : public Fl_Window { int handle(int); public: Fl_Root() : Fl_Window(0,0,Fl::w(),Fl::h()) { #if FL_MAJOR_VERSION > 1 clear_double_buffer(); #endif } void show() { if (!shown()) Fl_X::set_xid(this, RootWindow(fl_display, fl_screen)); } void flush() {} }; Fl_Window *Root; extern void ShowMenu(); extern int Handle_Hotkey(); extern void Grab_Hotkeys(); int Fl_Root::handle(int e) { if (e == FL_PUSH) { ShowMenu(); return 1; } return 0; } #if CLICK_RAISES || CLICK_TO_TYPE extern void click_raise(Frame*); #endif // fltk calls this for any events it does not understand: static int flwm_event_handler(int e) { if (!e) { // XEvent that fltk did not understand. XWindow window = fl_xevent->xany.window; // unfortunately most of the redirect events put the interesting // window id in a different place: switch (fl_xevent->type) { case CirculateNotify: case CirculateRequest: case ConfigureNotify: case ConfigureRequest: case CreateNotify: case DestroyNotify: case GravityNotify: case MapNotify: case MapRequest: case ReparentNotify: case UnmapNotify: window = fl_xevent->xmaprequest.window; } for (Frame* c = Frame::first; c; c = c->next) if (c->window() == window || fl_xid(c) == window) #if CLICK_RAISES || CLICK_TO_TYPE if (fl_xevent->type == ButtonPress) {click_raise(c); return 1;} else #endif return c->handle(fl_xevent); switch (fl_xevent->type) { case ButtonPress: printf("got a button press in main\n"); return 0; case ConfigureRequest: { const XConfigureRequestEvent *e = &(fl_xevent->xconfigurerequest); XConfigureWindow(fl_display, e->window, e->value_mask&~(CWSibling|CWStackMode), (XWindowChanges*)&(e->x)); return 1;} case MapRequest: { const XMapRequestEvent* e = &(fl_xevent->xmaprequest); (void)new Frame(e->window); return 1;} #if FL_MAJOR_VERSION<2 // this was needed for *some* earlier versions of fltk case KeyRelease: if (!Fl::grab()) return 0; Fl::e_keysym = XKeycodeToKeysym(fl_display, fl_xevent->xkey.keycode, 0); goto KEYUP; #endif } } else if (e == FL_KEYUP) { #if FL_MAJOR_VERSION<2 KEYUP: #endif if (!Fl::grab()) return 0; // when alt key released, pretend they hit enter & pick menu item if (Fl::event_key()==FL_Alt_L || Fl::event_key()==FL_Alt_R) { Fl::e_keysym = FL_Enter; #if FL_MAJOR_VERSION>1 return Fl::modal()->handle(FL_KEYBOARD); #else return Fl::grab()->handle(FL_KEYBOARD); #endif } return 0; } else if (e == FL_SHORTCUT || e == FL_KEYBOARD) { #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 0 && FL_PATCH_VERSION < 3 // make the tab keys work in the menus in older fltk's: // (they do not cycle around however, so a new fltk is a good idea) if (Fl::grab()) { // make fltk's menus resond to tab + shift+tab: if (Fl::event_key() == FL_Tab) { if (Fl::event_state() & FL_SHIFT) goto J1; Fl::e_keysym = FL_Down; } else if (Fl::event_key() == 0xFE20) { J1: Fl::e_keysym = FL_Up; } else return 0; return Fl::grab()->handle(FL_KEYBOARD); } #endif return Handle_Hotkey(); } return 0; } #if DESKTOPS extern void init_desktops(); extern Atom _win_workspace; extern Atom _win_workspace_count; extern Atom _win_workspace_names; #endif extern Atom _win_state; extern Atom _win_hints; #ifdef SHOW_CLOCK int clock_period = 1; int clock_oldmin = 61; int clock_alarm_on = 0; char clock_buf[80]; struct sigaction flwm_clock_alarm_start = {0,}, flwm_clock_alarm_stop = {0,}; void flwm_update_clock(void*) { time_t newtime; struct tm *tm_p; // get current time time(&newtime); tm_p = localtime(&newtime); // Update a window frame if necessary if (Frame::activeFrame() && tm_p->tm_min != clock_oldmin) { if (clock_oldmin != 61) clock_period = 60; // now that we're in sync, only update 1/minute clock_oldmin = tm_p->tm_min; strftime(clock_buf, 80, SHOW_CLOCK, tm_p); Frame::activeFrame()->redraw_clock(); } // Now reschedule the timeout Fl::remove_timeout(flwm_update_clock); Fl::add_timeout(clock_period, flwm_update_clock); } void flwm_clock_alarm_on(int) { clock_alarm_on = 1; Frame::activeFrame()->redraw_clock(); } void flwm_clock_alarm_off(int) { clock_alarm_on = 0; Frame::activeFrame()->redraw_clock(); } #endif static const char* cfg, *cbg; #if FL_MAJOR_VERSION>1 static fltk::Cursor* cursor = FL_CURSOR_ARROW; extern FL_API fltk::Color fl_cursor_fg; extern FL_API fltk::Color fl_cursor_bg; #else static int cursor = FL_CURSOR_ARROW; #endif bool test_mode = false; static void initialize() { Display* d = fl_display; if (test_mode) { XWindow w = XCreateSimpleWindow(d, RootWindow(d, fl_screen), 100, 100, 200, 300, 10, BlackPixel(fl_display, 0), // WhitePixel(fl_display, 0)); 0x1234); Frame* frame = new Frame(w); frame->label("flwm test window"); XSelectInput(d, w, ExposureMask | StructureNotifyMask | KeyPressMask | KeyReleaseMask | FocusChangeMask | KeymapStateMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask /*|PointerMotionMask*/ ); return; } Fl::add_handler(flwm_event_handler); // setting attributes on root window makes me the window manager: initializing = 1; XSelectInput(d, fl_xid(Root), SubstructureRedirectMask | SubstructureNotifyMask | ColormapChangeMask | PropertyChangeMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | KeymapStateMask); #if FL_MAJOR_VERSION>1 Root->cursor(cursor); #else Root->cursor((Fl_Cursor)cursor, CURSOR_FG_SLOT, CURSOR_BG_SLOT); #endif Fl::visible_focus(0); #ifdef TITLE_FONT Fl::set_font(TITLE_FONT_SLOT, TITLE_FONT); #endif #ifdef MENU_FONT Fl::set_font(MENU_FONT_SLOT, MENU_FONT); #endif #ifdef ACTIVE_COLOR Fl::set_color(FL_SELECTION_COLOR, ACTIVE_COLOR<<8); #endif // Gnome crap: // First create a window that can be watched to see if wm dies: Atom a = XInternAtom(d, "_WIN_SUPPORTING_WM_CHECK", False); XWindow win = XCreateSimpleWindow(d, fl_xid(Root), -200, -200, 5, 5, 0, 0, 0); CARD32 val = win; XChangeProperty(d, fl_xid(Root), a, XA_CARDINAL, 32, PropModeReplace, (uchar*)&val, 1); XChangeProperty(d, win, a, XA_CARDINAL, 32, PropModeReplace, (uchar*)&val, 1); // Next send a list of Gnome stuff we understand: a = XInternAtom(d, "_WIN_PROTOCOLS", 0); Atom list[10]; unsigned int i = 0; //list[i++] = XInternAtom(d, "_WIN_LAYER", 0); list[i++] = _win_state = XInternAtom(d, "_WIN_STATE", 0); list[i++] = _win_hints = XInternAtom(d, "_WIN_HINTS", 0); //list[i++] = XInternAtom(d, "_WIN_APP_STATE", 0); //list[i++] = XInternAtom(d, "_WIN_EXPANDED_SIZE", 0); //list[i++] = XInternAtom(d, "_WIN_ICONS", 0); #if DESKTOPS list[i++] = _win_workspace = XInternAtom(d, "_WIN_WORKSPACE", 0); list[i++] = _win_workspace_count = XInternAtom(d, "_WIN_WORKSPACE_COUNT", 0); list[i++] = _win_workspace_names = XInternAtom(d, "_WIN_WORKSPACE_NAMES", 0); #endif //list[i++] = XInternAtom(d, "_WIN_FRAME_LIST", 0); XChangeProperty(d, fl_xid(Root), a, XA_ATOM, 32, PropModeReplace, (uchar*)list, i); Grab_Hotkeys(); #ifdef SHOW_CLOCK Fl::add_timeout(clock_period, flwm_update_clock); flwm_clock_alarm_start.sa_handler = &flwm_clock_alarm_on; flwm_clock_alarm_stop.sa_handler = &flwm_clock_alarm_off; sigaction(SIGALRM, &flwm_clock_alarm_start, NULL); sigaction(SIGCONT, &flwm_clock_alarm_stop, NULL); #endif XSync(d, 0); initializing = 0; #if DESKTOPS init_desktops(); #endif // find all the windows and create a Frame for each: unsigned int n; XWindow w1, w2, *wins; XWindowAttributes attr; XQueryTree(d, fl_xid(Root), &w1, &w2, &wins, &n); for (i = 0; i < n; ++i) { XGetWindowAttributes(d, wins[i], &attr); if (attr.override_redirect || !attr.map_state) continue; (void)new Frame(wins[i],&attr); } XFree((void *)wins); } //////////////////////////////////////////////////////////////// extern int exit_flag; extern int max_w_switch; extern int max_h_switch; // consume a switch from argv. Returns number of words eaten, 0 on error: int arg(int argc, char **argv, int &i) { const char *s = argv[i]; if (s[0] != '-') return 0; s++; // do single-word switches: if (!strcmp(s,"x")) { exit_flag = 1; i++; return 1; } else if (!strcmp(s, "test")) { test_mode = true; i++; return 1; } // do switches with a value: const char *v = argv[i+1]; if (i >= argc-1 || !v) return 0; // all the rest need an argument, so if missing it is an error if (!strcmp(s, "cfg")) { cfg = v; } else if (!strcmp(s, "cbg")) { cbg = v; #if FL_MAJOR_VERSION < 2 } else if (*s == 'c') { cursor = atoi(v); #endif } else if (*s == 'v') { int visid = atoi(v); fl_open_display(); XVisualInfo templt; int num; templt.visualid = visid; fl_visual = XGetVisualInfo(fl_display, VisualIDMask, &templt, &num); if (!fl_visual) Fl::fatal("No visual with id %d",visid); fl_colormap = XCreateColormap(fl_display, RootWindow(fl_display,fl_screen), fl_visual->visual, AllocNone); } else if (*s == 'm') { max_w_switch = atoi(v); while (*v && *v++ != 'x'); max_h_switch = atoi(v); } else return 0; // unrecognized // return the fact that we consumed 2 switches: i += 2; return 2; } #if FL_MAJOR_VERSION<2 static void color_setup(Fl_Color slot, const char* arg, ulong value) { if (arg) { XColor x; if (XParseColor(fl_display, fl_colormap, arg, &x)) value = ((x.red>>8)<<24)|((x.green>>8)<<16)|((x.blue)); } Fl::set_color(slot, value); } #endif int main(int argc, char** argv) { program_name = fl_filename_name(argv[0]); int i; if (Fl::args(argc, argv, i, arg) < argc) Fl::error( "options are:\n" " -d[isplay] host:#.#\tX display & screen to use\n" " -v[isual] #\t\tvisual to use\n" " -g[eometry] WxH+X+Y\tlimits windows to this area\n" " -m[aximum] WxH\t\tsize of maximized windows\n" " -x\t\t\tmenu says Exit instead of logout\n" " -bg color\t\tFrame color\n" " -fg color\t\tLabel color\n" " -bg2 color\t\tText field color\n" " -c[ursor] #\t\tCursor number for root\n" " -cfg color\t\tCursor color\n" " -cbg color\t\tCursor outline color" " -test\t\t\tTest the window graphics" ); #ifndef FL_NORMAL_SIZE // detect new versions of fltk where this is a variable FL_NORMAL_SIZE = 12; #endif #if FL_MAJOR_VERSION>1 if (cfg) fl_cursor_fg = fltk::color(cfg); if (cbg) fl_cursor_bg = fltk::color(cbg); #else fl_open_display(); color_setup(CURSOR_FG_SLOT, cfg, CURSOR_FG_COLOR<<8); color_setup(CURSOR_BG_SLOT, cbg, CURSOR_BG_COLOR<<8); Fl::set_color(FL_SELECTION_COLOR,0,0,128); #endif Fl_Root root; Root = &root; #if FL_MAJOR_VERSION>1 // show() is not a virtual function in fltk2.0, this fools it: fltk::load_theme(); root.show(); #endif Root->show(argc,argv); // fools fltk into using -geometry to set the size XSetErrorHandler(xerror_handler); initialize(); return Fl::run(); }