mirror of
https://git.code.sf.net/p/flwm/flwm
synced 2025-12-11 23:06:56 -05:00
428 lines
12 KiB
C
428 lines
12 KiB
C
#define FL_INTERNALS 1
|
|
#include "Frame.H"
|
|
#include <X11/Xproto.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <FL/filename.H>
|
|
#include "config.h"
|
|
#ifdef SHOW_CLOCK
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#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();
|
|
}
|