mirror of
https://git.code.sf.net/p/flwm/flwm
synced 2025-12-12 07:16:57 -05:00
Initial checkin of the code to sourceforge.
This commit is contained in:
149
Desktop.C
Normal file
149
Desktop.C
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// Desktop.C
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
|
||||||
|
#include "Frame.H"
|
||||||
|
#include "Desktop.H"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
Desktop* Desktop::current_ = 0;
|
||||||
|
Desktop* Desktop::first = 0;
|
||||||
|
|
||||||
|
// return the highest desktop number:
|
||||||
|
int Desktop::max_number() {
|
||||||
|
int n = 0;
|
||||||
|
for (Desktop* d = first; d; d = d->next)
|
||||||
|
if (d->number_ > n) n = d->number_;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return an empty slot number:
|
||||||
|
int Desktop::available_number() {
|
||||||
|
int n = 1;
|
||||||
|
for (Desktop* d = first; d;) {
|
||||||
|
if (d->number_ == n) {n++; d = first;}
|
||||||
|
else d = d->next;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// these are set by main.C:
|
||||||
|
Atom _win_workspace;
|
||||||
|
Atom _win_workspace_count;
|
||||||
|
Atom _win_workspace_names;
|
||||||
|
#ifndef __sgi
|
||||||
|
static Atom kwm_current_desktop;
|
||||||
|
#endif
|
||||||
|
extern Fl_Window* Root;
|
||||||
|
|
||||||
|
static int dont_send;
|
||||||
|
static void send_desktops() {
|
||||||
|
if (dont_send) return;
|
||||||
|
int n = Desktop::max_number();
|
||||||
|
setProperty(fl_xid(Root), _win_workspace_count, XA_CARDINAL, n);
|
||||||
|
char buffer[1025];
|
||||||
|
char* p = buffer;
|
||||||
|
for (int i = 1; i <= n; i++) {
|
||||||
|
Desktop* d = Desktop::number(i);
|
||||||
|
const char* name = d ? d->name() : "<deleted>";
|
||||||
|
while (p < buffer+1024 && *name) *p++ = *name++;
|
||||||
|
*p++ = 0;
|
||||||
|
if (p >= buffer+1024) break;
|
||||||
|
}
|
||||||
|
XChangeProperty(fl_display, fl_xid(Root), _win_workspace_names, XA_STRING,
|
||||||
|
8, PropModeReplace, (unsigned char *)buffer, p-buffer-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Desktop::Desktop(const char* n, int num) {
|
||||||
|
next = first;
|
||||||
|
first = this;
|
||||||
|
name_ = strdup(n);
|
||||||
|
number_ = num;
|
||||||
|
send_desktops();
|
||||||
|
}
|
||||||
|
|
||||||
|
Desktop::~Desktop() {
|
||||||
|
// remove from list:
|
||||||
|
for (Desktop** p = &first; *p; p = &((*p)->next))
|
||||||
|
if (*p == this) {*p = next; break;}
|
||||||
|
send_desktops();
|
||||||
|
if (current_ == this || !first->next) current(first);
|
||||||
|
// put any clients onto another desktop:
|
||||||
|
for (Frame* c = Frame::first; c; c = c->next)
|
||||||
|
if (c->desktop() == this) c->desktop(first);
|
||||||
|
free((void*)name_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Desktop::name(const char* l) {
|
||||||
|
free((void*)name_);
|
||||||
|
name_ = strdup(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Desktop::current(Desktop* n) {
|
||||||
|
if (n == current_) return;
|
||||||
|
current_ = n;
|
||||||
|
for (Frame* c = Frame::first; c; c = c->next) {
|
||||||
|
if (c->desktop() == n) {
|
||||||
|
if (c->state() == OTHER_DESKTOP) c->state(NORMAL);
|
||||||
|
} else if (c->desktop()) {
|
||||||
|
if (c->state() == NORMAL) c->state(OTHER_DESKTOP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (n && !dont_send) {
|
||||||
|
#ifndef __sgi
|
||||||
|
setProperty(fl_xid(Root), kwm_current_desktop, kwm_current_desktop, n->number());
|
||||||
|
#endif
|
||||||
|
setProperty(fl_xid(Root), _win_workspace, XA_CARDINAL, n->number()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return desktop with given number, create it if necessary:
|
||||||
|
Desktop* Desktop::number(int n, int create) {
|
||||||
|
if (!n) return 0;
|
||||||
|
Desktop* d;
|
||||||
|
for (d = first; d; d = d->next) if (d->number() == n) return d;
|
||||||
|
if (create) {
|
||||||
|
char buf[20]; sprintf(buf, "Desktop %d", n);
|
||||||
|
d = new Desktop(buf,n);
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called at startup, read the list of desktops from the root
|
||||||
|
// window properties, or on failure make some default desktops.
|
||||||
|
void init_desktops() {
|
||||||
|
dont_send = 1;
|
||||||
|
int length;
|
||||||
|
char* buffer =
|
||||||
|
(char*)getProperty(fl_xid(Root), _win_workspace_names, XA_STRING, &length);
|
||||||
|
if (buffer) {
|
||||||
|
char* c = buffer;
|
||||||
|
for (int i = 1; c < buffer+length; i++) {
|
||||||
|
char* d = c; while (*d) d++;
|
||||||
|
if (*c != '<') new Desktop(c,i);
|
||||||
|
c = d+1;
|
||||||
|
}
|
||||||
|
XFree(buffer);
|
||||||
|
}
|
||||||
|
int current_num = 0;
|
||||||
|
int p = getIntProperty(fl_xid(Root), _win_workspace, XA_CARDINAL, -1);
|
||||||
|
if (p >= 0 && p < 25) current_num = p+1;
|
||||||
|
#ifndef __sgi
|
||||||
|
// SGI's Xlib barfs when you try to do this XInternAtom!
|
||||||
|
// Maybe somebody there does not like KDE?
|
||||||
|
kwm_current_desktop = XInternAtom(fl_display, "KWM_CURRENT_DESKTOP", 0);
|
||||||
|
if (!current_num) {
|
||||||
|
p = getIntProperty(fl_xid(Root), kwm_current_desktop, kwm_current_desktop);
|
||||||
|
if (p > 0 && p < 25) current_num = p;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!current_num) current_num = 1;
|
||||||
|
Desktop::current(Desktop::number(current_num, 1));
|
||||||
|
dont_send = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
22
Desktop.H
Normal file
22
Desktop.H
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Desktop.H
|
||||||
|
|
||||||
|
class Desktop {
|
||||||
|
const char* name_;
|
||||||
|
int number_;
|
||||||
|
static Desktop* current_;
|
||||||
|
public:
|
||||||
|
static Desktop* first;
|
||||||
|
Desktop* next;
|
||||||
|
const char* name() const {return name_;}
|
||||||
|
void name(const char*);
|
||||||
|
int number() const {return number_;}
|
||||||
|
static Desktop* current() {return current_;}
|
||||||
|
static Desktop* number(int, int create = 0);
|
||||||
|
static void current(Desktop*);
|
||||||
|
static int available_number();
|
||||||
|
static int max_number();
|
||||||
|
Desktop(const char*, int);
|
||||||
|
~Desktop();
|
||||||
|
int junk; // for temporary storage by menu builder
|
||||||
|
};
|
||||||
|
|
||||||
192
Frame.H
Normal file
192
Frame.H
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// Frame.H
|
||||||
|
|
||||||
|
// Each X window being managed by fltk has one of these
|
||||||
|
|
||||||
|
#ifndef Frame_H
|
||||||
|
#define Frame_H
|
||||||
|
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
#include <FL/Fl_Window.H>
|
||||||
|
#include <FL/Fl_Button.H>
|
||||||
|
#include <FL/x.H>
|
||||||
|
|
||||||
|
// The state is an enumeration of reasons why the window may be invisible.
|
||||||
|
// Only if it is NORMAL is the window visible.
|
||||||
|
enum {
|
||||||
|
UNMAPPED = 0, // unmap command from app (X calls this WithdrawnState)
|
||||||
|
NORMAL = 1, // window is visible
|
||||||
|
//SHADED = 2, // acts like NORMAL
|
||||||
|
ICONIC = 3, // hidden/iconized
|
||||||
|
OTHER_DESKTOP = 4 // normal but on another desktop
|
||||||
|
};
|
||||||
|
|
||||||
|
// values for flags:
|
||||||
|
// The flags are constant and are turned on by information learned
|
||||||
|
// from the Gnome, KDE, and/or Motif window manager hints. Flwm will
|
||||||
|
// ignore attempts to change these hints after the window is mapped.
|
||||||
|
enum {
|
||||||
|
NO_FOCUS = 0x0001, // does not take focus
|
||||||
|
CLICK_TO_FOCUS = 0x0002, // must click on window to give it focus
|
||||||
|
NO_BORDER = 0x0004, // raw window with no border
|
||||||
|
THIN_BORDER = 0x0008, // just resize border
|
||||||
|
NO_RESIZE = 0x0010, // don't resize even if sizehints say its ok
|
||||||
|
NO_CLOSE = 0x0040, // don't put a close box on it
|
||||||
|
TAKE_FOCUS_PROTOCOL = 0x0080, // send junk when giving window focus
|
||||||
|
DELETE_WINDOW_PROTOCOL= 0x0100, // close box sends a message
|
||||||
|
KEEP_ASPECT = 0x0200, // aspect ratio from sizeHints
|
||||||
|
MODAL = 0x0400, // grabs focus from transient_for window
|
||||||
|
ICONIZE = 0x0800, // transient_for_ actually means group :-(
|
||||||
|
QUIT_PROTOCOL = 0x1000, // Irix 4DWM "quit" menu item
|
||||||
|
SAVE_PROTOCOL = 0x2000 // "WM_SAVE_YOURSELF" stuff
|
||||||
|
};
|
||||||
|
|
||||||
|
// values for state_flags:
|
||||||
|
// These change over time
|
||||||
|
enum {
|
||||||
|
IGNORE_UNMAP = 0x01, // we did something that echos an UnmapNotify
|
||||||
|
SAVE_PROTOCOL_WAIT = 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameButton : public Fl_Button {
|
||||||
|
void draw();
|
||||||
|
public:
|
||||||
|
FrameButton(int X, int Y, int W, int H, const char* L=0)
|
||||||
|
: Fl_Button(X,Y,W,H,L) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Desktop;
|
||||||
|
|
||||||
|
class Frame : public Fl_Window {
|
||||||
|
|
||||||
|
Window window_;
|
||||||
|
|
||||||
|
short state_; // X server state: iconic, withdrawn, normal
|
||||||
|
short state_flags_; // above state flags
|
||||||
|
void set_state_flag(short i) {state_flags_ |= i;}
|
||||||
|
void clear_state_flag(short i) {state_flags_&=~i;}
|
||||||
|
|
||||||
|
int flags_; // above constant flags
|
||||||
|
void set_flag(int i) {flags_ |= i;}
|
||||||
|
void clear_flag(int i) {flags_&=~i;}
|
||||||
|
|
||||||
|
int restore_x, restore_w; // saved size when min/max width is set
|
||||||
|
int restore_y, restore_h; // saved size when max height is set
|
||||||
|
int min_w, max_w, inc_w; // size range and increment
|
||||||
|
int min_h, max_h, inc_h; // size range and increment
|
||||||
|
int app_border_width; // value of border_width application tried to set
|
||||||
|
|
||||||
|
int left, top, dwidth, dheight; // current thickness of border
|
||||||
|
int label_y, label_h; // location of label
|
||||||
|
int label_w; // measured width of printed label
|
||||||
|
|
||||||
|
Window transient_for_xid; // value from X
|
||||||
|
Frame* transient_for_; // the frame for that xid, if found
|
||||||
|
|
||||||
|
Frame* revert_to; // probably the xterm this was run from
|
||||||
|
|
||||||
|
Colormap colormap; // this window's colormap
|
||||||
|
int colormapWinCount; // list of other windows to install colormaps for
|
||||||
|
Window *colormapWindows;
|
||||||
|
Colormap *window_Colormaps; // their colormaps
|
||||||
|
|
||||||
|
Desktop* desktop_;
|
||||||
|
|
||||||
|
FrameButton close_button;
|
||||||
|
FrameButton iconize_button;
|
||||||
|
FrameButton max_h_button;
|
||||||
|
FrameButton max_w_button;
|
||||||
|
FrameButton min_w_button;
|
||||||
|
|
||||||
|
int maximize_width();
|
||||||
|
int maximize_height();
|
||||||
|
int force_x_onscreen(int X, int W);
|
||||||
|
int force_y_onscreen(int Y, int H);
|
||||||
|
|
||||||
|
void sendMessage(Atom, Atom) const;
|
||||||
|
void sendConfigureNotify() const;
|
||||||
|
void setStateProperty() const;
|
||||||
|
|
||||||
|
void* getProperty(Atom, Atom = AnyPropertyType, int* length = 0) const;
|
||||||
|
int getIntProperty(Atom, Atom = AnyPropertyType, int deflt = 0) const;
|
||||||
|
void setProperty(Atom, Atom, int) const;
|
||||||
|
void getLabel(int del = 0);
|
||||||
|
void getColormaps();
|
||||||
|
int getSizes();
|
||||||
|
int getGnomeState(int&);
|
||||||
|
void getProtocols();
|
||||||
|
int getMotifHints();
|
||||||
|
void updateBorder();
|
||||||
|
void fix_transient_for(); // called when transient_for_xid changes
|
||||||
|
|
||||||
|
void installColormap() const;
|
||||||
|
|
||||||
|
void set_size(int,int,int,int, int warp=0);
|
||||||
|
void resize(int,int,int,int);
|
||||||
|
void show_hide_buttons();
|
||||||
|
|
||||||
|
int handle(int); // handle fltk events
|
||||||
|
void set_cursor(int);
|
||||||
|
int mouse_location();
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
|
||||||
|
static Frame* active_;
|
||||||
|
static void button_cb_static(Fl_Widget*, void*);
|
||||||
|
void button_cb(Fl_Button*);
|
||||||
|
|
||||||
|
void deactivate();
|
||||||
|
int activate_if_transient();
|
||||||
|
void _desktop(Desktop*);
|
||||||
|
|
||||||
|
int border() const {return !(flags_&NO_BORDER);}
|
||||||
|
int flags() const {return flags_;}
|
||||||
|
int flag(int i) const {return flags_&i;}
|
||||||
|
void throw_focus(int destructor = 0);
|
||||||
|
void warp_pointer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
int handle(const XEvent*);
|
||||||
|
|
||||||
|
static Frame* first;
|
||||||
|
Frame* next; // stacking order, top to bottom
|
||||||
|
|
||||||
|
Frame(Window, XWindowAttributes* = 0);
|
||||||
|
~Frame();
|
||||||
|
|
||||||
|
Window window() const {return window_;}
|
||||||
|
Frame* transient_for() const {return transient_for_;}
|
||||||
|
int is_transient_for(const Frame*) const;
|
||||||
|
|
||||||
|
Desktop* desktop() const {return desktop_;}
|
||||||
|
void desktop(Desktop*);
|
||||||
|
|
||||||
|
void raise(); // also does map
|
||||||
|
void lower();
|
||||||
|
void iconize();
|
||||||
|
void close();
|
||||||
|
void kill();
|
||||||
|
int activate(int warp = 0); // returns true if it actually sets active state
|
||||||
|
|
||||||
|
short state() const {return state_;}
|
||||||
|
void state(short); // don't call this unless you know what you are doing!
|
||||||
|
|
||||||
|
int active() const {return active_==this;}
|
||||||
|
static Frame* activeFrame() {return active_;}
|
||||||
|
|
||||||
|
static void save_protocol(); // called when window manager exits
|
||||||
|
|
||||||
|
// The following should be conditionally defined based on the
|
||||||
|
// SHOW_CLOCK definition in config.h but that definition is not
|
||||||
|
// available at the time we are evaluating this; it does no harm
|
||||||
|
// to be present even if not SHOW_CLOCK.
|
||||||
|
void redraw_clock();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// handy wrappers for those ugly X routines:
|
||||||
|
void* getProperty(Window, Atom, Atom = AnyPropertyType, int* length = 0);
|
||||||
|
int getIntProperty(Window, Atom, Atom = AnyPropertyType, int deflt = 0);
|
||||||
|
void setProperty(Window, Atom, Atom, int);
|
||||||
|
|
||||||
|
#endif
|
||||||
42
FrameWindow.C
Normal file
42
FrameWindow.C
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// FrameWindow.C
|
||||||
|
|
||||||
|
// X does not echo back the window-map events (it probably should when
|
||||||
|
// override_redirect is off). Unfortunately this means you have to use
|
||||||
|
// this subclass if you want a "normal" fltk window, it will force a
|
||||||
|
// Frame to be created and destroy it upon hide.
|
||||||
|
|
||||||
|
// Warning: modal() does not work! Don't turn it on as it screws up the
|
||||||
|
// interface with the window borders. You can use set_non_modal() to
|
||||||
|
// disable the iconize box but the window manager must be written to
|
||||||
|
// not be modal.
|
||||||
|
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
#include "FrameWindow.H"
|
||||||
|
#include "Frame.H"
|
||||||
|
|
||||||
|
extern int dont_set_event_mask;
|
||||||
|
|
||||||
|
void FrameWindow::show() {
|
||||||
|
if (shown()) {Fl_Window::show(); return;}
|
||||||
|
Fl_Window::show();
|
||||||
|
dont_set_event_mask = 1;
|
||||||
|
frame = new Frame(fl_xid(this));
|
||||||
|
dont_set_event_mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameWindow::hide() {
|
||||||
|
if (shown()) {
|
||||||
|
Fl_Window::hide();
|
||||||
|
delete frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int FrameWindow::handle(int e) {
|
||||||
|
if (Fl_Window::handle(e)) return 1;
|
||||||
|
// make Esc close the window:
|
||||||
|
if (e == FL_SHORTCUT && Fl::event_key()==FL_Escape) {
|
||||||
|
do_callback();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
31
FrameWindow.H
Normal file
31
FrameWindow.H
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// FrameWindow.H
|
||||||
|
|
||||||
|
// X does not echo back the window-map events (it probably should when
|
||||||
|
// override_redirect is off). Unfortunately this means you have to use
|
||||||
|
// this subclass if you want a "normal" fltk window, it will force a
|
||||||
|
// Frame to be created and destroy it upon hide.
|
||||||
|
|
||||||
|
// Warning: modal() does not work! Don't turn it on as it screws up the
|
||||||
|
// interface with the window borders. You can use set_non_modal() to
|
||||||
|
// disable the iconize box but the window manager must be written to
|
||||||
|
// not be modal.
|
||||||
|
|
||||||
|
#ifndef FrameWindow_H
|
||||||
|
#define FrameWindow_H
|
||||||
|
|
||||||
|
#include <FL/Fl_Window.H>
|
||||||
|
class Frame;
|
||||||
|
|
||||||
|
class FrameWindow : public Fl_Window {
|
||||||
|
Frame* frame;
|
||||||
|
public:
|
||||||
|
void hide();
|
||||||
|
void show();
|
||||||
|
int handle(int);
|
||||||
|
FrameWindow(int X, int Y, int W, int H, const char* L = 0) :
|
||||||
|
Fl_Window(X,Y,W,H,L) {}
|
||||||
|
FrameWindow(int W, int H, const char* L = 0) :
|
||||||
|
Fl_Window(W,H,L) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
181
Hotkeys.C
Normal file
181
Hotkeys.C
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
// Hotkeys.C
|
||||||
|
|
||||||
|
// If you want to change what the hotkeys are, see the table at the bottom!
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "Frame.H"
|
||||||
|
#include "Desktop.H"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern void ShowMenu();
|
||||||
|
extern void ShowTabMenu(int tab);
|
||||||
|
|
||||||
|
#if STANDARD_HOTKEYS
|
||||||
|
|
||||||
|
static void NextWindow() { // Alt+Tab
|
||||||
|
ShowTabMenu(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PreviousWindow() { // Alt+Shift+Tab
|
||||||
|
ShowTabMenu(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
|
||||||
|
static void NextDesk() {
|
||||||
|
if (Desktop::current()) {
|
||||||
|
Desktop::current(Desktop::current()->next?
|
||||||
|
Desktop::current()->next:Desktop::first);
|
||||||
|
} else {
|
||||||
|
Desktop::current(Desktop::first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PreviousDesk() {
|
||||||
|
Desktop* search=Desktop::first;
|
||||||
|
while (search->next && search->next!=Desktop::current()){
|
||||||
|
search=search->next;
|
||||||
|
}
|
||||||
|
Desktop::current(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
// warning: this assummes it is bound to Fn key:
|
||||||
|
static void DeskNumber() {
|
||||||
|
Desktop::current(Desktop::number(Fl::event_key()-FL_F, 1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if WMX_HOTKEYS || CDE_HOTKEYS
|
||||||
|
|
||||||
|
static void Raise() { // Alt+Up
|
||||||
|
Frame* f = Frame::activeFrame();
|
||||||
|
if (f) f->raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Lower() { // Alt+Down
|
||||||
|
Frame* f = Frame::activeFrame();
|
||||||
|
if (f) f->lower();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Iconize() { // Alt+Enter
|
||||||
|
Frame* f = Frame::activeFrame();
|
||||||
|
if (f) f->iconize();
|
||||||
|
else ShowMenu(); // so they can deiconize stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Close() { // Alt+Delete
|
||||||
|
Frame* f = Frame::activeFrame();
|
||||||
|
if (f) f->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static struct {int key; void (*func)();} keybindings[] = {
|
||||||
|
#if STANDARD_HOTKEYS || MINIMUM_HOTKEYS
|
||||||
|
// these are very common and tend not to conflict, due to Windoze:
|
||||||
|
{FL_ALT+FL_Escape, ShowMenu},
|
||||||
|
{FL_ALT+FL_Menu, ShowMenu},
|
||||||
|
#endif
|
||||||
|
#if STANDARD_HOTKEYS
|
||||||
|
{FL_ALT+FL_Tab, NextWindow},
|
||||||
|
{FL_ALT+FL_SHIFT+FL_Tab,PreviousWindow},
|
||||||
|
{FL_ALT+FL_SHIFT+0xfe20,PreviousWindow}, // XK_ISO_Left_Tab
|
||||||
|
#endif
|
||||||
|
#if KWM_HOTKEYS && DESKTOPS // KWM uses these to switch desktops
|
||||||
|
// {FL_CTRL+FL_Tab, NextDesk},
|
||||||
|
// {FL_CTRL+FL_SHIFT+FL_Tab,PreviousDesk},
|
||||||
|
// {FL_CTRL+FL_SHIFT+0xfe20,PreviousDesk}, // XK_ISO_Left_Tab
|
||||||
|
{FL_CTRL+FL_F+1, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+2, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+3, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+4, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+5, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+6, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+7, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+8, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+9, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+10, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+11, DeskNumber},
|
||||||
|
{FL_CTRL+FL_F+12, DeskNumber},
|
||||||
|
#endif
|
||||||
|
#if WMX_HOTKEYS
|
||||||
|
// wmx also sets all these, they seem pretty useful:
|
||||||
|
{FL_ALT+FL_Up, Raise},
|
||||||
|
{FL_ALT+FL_Down, Lower},
|
||||||
|
{FL_ALT+FL_Enter, Iconize},
|
||||||
|
{FL_ALT+FL_Delete, Close},
|
||||||
|
//{FL_ALT+FL_Page_Up, ToggleMaxH},
|
||||||
|
//{FL_ALT+FL_Page_Down,ToggleMaxW},
|
||||||
|
#endif
|
||||||
|
#if WMX_DESK_HOTKEYS && DESKTOPS
|
||||||
|
// these wmx keys are not set by default as they break NetScape:
|
||||||
|
{FL_ALT+FL_Left, PreviousDesk},
|
||||||
|
{FL_ALT+FL_Right, NextDesk},
|
||||||
|
#endif
|
||||||
|
#if CDE_HOTKEYS
|
||||||
|
// CDE hotkeys (or at least what SGI's 4DWM uses):
|
||||||
|
{FL_ALT+FL_F+1, Raise},
|
||||||
|
//{FL_ALT+FL_F+2, unknown}, // KWM uses this to run a typed-in command
|
||||||
|
{FL_ALT+FL_F+3, Lower},
|
||||||
|
{FL_ALT+FL_F+4, Close}, // this matches KWM
|
||||||
|
//{FL_ALT+FL_F+5, Restore}, // useless because no icons visible
|
||||||
|
//{FL_ALT+FL_F+6, unknown}, // ?
|
||||||
|
//{FL_ALT+FL_F+7, Move}, // grabs the window for movement
|
||||||
|
//{FL_ALT+FL_F+8, Resize}, // grabs the window for resizing
|
||||||
|
{FL_ALT+FL_F+9, Iconize},
|
||||||
|
//{FL_ALT+FL_F+10, Maximize},
|
||||||
|
//{FL_ALT+FL_F+11, unknown}, // ?
|
||||||
|
{FL_ALT+FL_F+12, Close}, // actually does "quit"
|
||||||
|
#else
|
||||||
|
#if DESKTOPS && DESKTOP_HOTKEYS
|
||||||
|
// seem to be common to Linux window managers
|
||||||
|
{FL_ALT+FL_F+1, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+2, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+3, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+4, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+5, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+6, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+7, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+8, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+9, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+10, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+11, DeskNumber},
|
||||||
|
{FL_ALT+FL_F+12, DeskNumber},
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
{0}};
|
||||||
|
|
||||||
|
int Handle_Hotkey() {
|
||||||
|
for (int i = 0; keybindings[i].key; i++) {
|
||||||
|
if (Fl::test_shortcut(keybindings[i].key) ||
|
||||||
|
(keybindings[i].key & 0xFFFF) == FL_Delete
|
||||||
|
&& Fl::event_key() == FL_BackSpace// fltk bug?
|
||||||
|
) {
|
||||||
|
keybindings[i].func();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern Fl_Window* Root;
|
||||||
|
|
||||||
|
void Grab_Hotkeys() {
|
||||||
|
Window root = fl_xid(Root);
|
||||||
|
for (int i = 0; keybindings[i].key; i++) {
|
||||||
|
int k = keybindings[i].key;
|
||||||
|
int keycode = XKeysymToKeycode(fl_display, k & 0xFFFF);
|
||||||
|
if (!keycode) continue;
|
||||||
|
// Silly X! we need to ignore caps lock & numlock keys by grabbing
|
||||||
|
// all the combinations:
|
||||||
|
XGrabKey(fl_display, keycode, k>>16, root, 0, 1, 1);
|
||||||
|
XGrabKey(fl_display, keycode, (k>>16)|2, root, 0, 1, 1); // CapsLock
|
||||||
|
XGrabKey(fl_display, keycode, (k>>16)|16, root, 0, 1, 1); // NumLock
|
||||||
|
XGrabKey(fl_display, keycode, (k>>16)|18, root, 0, 1, 1); // both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
84
Makefile
Normal file
84
Makefile
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
SHELL=/bin/sh
|
||||||
|
|
||||||
|
PROGRAM = flwm
|
||||||
|
VERSION = 0.25
|
||||||
|
|
||||||
|
CXXFILES = main.C Frame.C Rotated.C Menu.C FrameWindow.C Desktop.C Hotkeys.C
|
||||||
|
|
||||||
|
LIBS = -lfltk
|
||||||
|
|
||||||
|
MANPAGE = 1
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
OBJECTS = $(CXXFILES:.C=.o)
|
||||||
|
|
||||||
|
all: $(PROGRAM)
|
||||||
|
|
||||||
|
$(PROGRAM) : $(OBJECTS)
|
||||||
|
$(CXX) $(LDFLAGS) -o $(PROGRAM) $(OBJECTS) $(LIBS) $(LDLIBS)
|
||||||
|
|
||||||
|
makeinclude: configure
|
||||||
|
./configure
|
||||||
|
include makeinclude
|
||||||
|
|
||||||
|
.SUFFIXES : .fl .do .C .c .H
|
||||||
|
|
||||||
|
.C.o :
|
||||||
|
$(CXX) $(CXXFLAGS) -c $<
|
||||||
|
.C :
|
||||||
|
$(CXX) $(CXXFLAGS) -c $<
|
||||||
|
.fl.C :
|
||||||
|
-fluid -c $<
|
||||||
|
.fl.H :
|
||||||
|
-fluid -c $<
|
||||||
|
|
||||||
|
clean :
|
||||||
|
-@ rm -f *.o $(PROGRAM) $(CLEAN) core *~ makedepend
|
||||||
|
@touch makedepend
|
||||||
|
|
||||||
|
depend:
|
||||||
|
$(MAKEDEPEND) -I.. $(CXXFLAGS) $(CXXFILES) $(CFILES) > makedepend
|
||||||
|
makedepend:
|
||||||
|
touch makedepend
|
||||||
|
include makedepend
|
||||||
|
|
||||||
|
install: $(PROGRAM)
|
||||||
|
$(INSTALL) -s $(PROGRAM) $(bindir)/$(PROGRAM)
|
||||||
|
$(INSTALL) $(PROGRAM).$(MANPAGE) $(mandir)/man$(MANPAGE)/$(PROGRAM).$(MANPAGE)
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
-@ rm -f $(bindir)/$(PROGRAM)
|
||||||
|
-@ rm -f $(mandir)/man$(MANPAGE)/$(PROGRAM).$(MANPAGE)
|
||||||
|
|
||||||
|
dist:
|
||||||
|
cat /dev/null > makedepend
|
||||||
|
-@mkdir $(PROGRAM)-$(VERSION)
|
||||||
|
-@ln README Makefile configure install-sh makedepend *.C *.H *.h *.in *.fl $(PROGRAM).$(MANPAGE) flwm_wmconfig $(PROGRAM)-$(VERSION)
|
||||||
|
tar -cvzf $(PROGRAM)-$(VERSION).tgz $(PROGRAM)-$(VERSION)
|
||||||
|
-@rm -r $(PROGRAM)-$(VERSION)
|
||||||
|
|
||||||
|
exedist:
|
||||||
|
-@mkdir $(PROGRAM)-$(VERSION)-x86
|
||||||
|
-@ln README $(PROGRAM) $(PROGRAM).$(MANPAGE) flwm_wmconfig $(PROGRAM)-$(VERSION)-x86
|
||||||
|
tar -cvzf $(PROGRAM)-$(VERSION)-x86.tgz $(PROGRAM)-$(VERSION)-x86
|
||||||
|
-@rm -r $(PROGRAM)-$(VERSION)-x86
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
PROGRAM_D = $(PROGRAM)_d
|
||||||
|
|
||||||
|
debug: $(PROGRAM_D)
|
||||||
|
|
||||||
|
OBJECTS_D = $(CXXFILES:.C=.do) $(CFILES:.c=.do)
|
||||||
|
|
||||||
|
.C.do :
|
||||||
|
$(CXX) -I.. $(CXXFLAGS_D) -c -o $@ $<
|
||||||
|
.c.do :
|
||||||
|
$(CC) -I.. $(CFLAGS_D) -c -o $@ $<
|
||||||
|
|
||||||
|
$(PROGRAM_D) : $(OBJECTS_D)
|
||||||
|
$(CXX) $(LDFLAGS) -o $(PROGRAM_D) $(OBJECTS_D) $(LIBS) $(LDLIBS)
|
||||||
|
|
||||||
|
rotated_test: Rotated.o rotated_test.C
|
||||||
|
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o rotated_test rotated_test.C Rotated.o $(LIBS) $(LDLIBS)
|
||||||
627
Menu.C
Normal file
627
Menu.C
Normal file
@ -0,0 +1,627 @@
|
|||||||
|
// Menu.cxx
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "Frame.H"
|
||||||
|
#if DESKTOPS
|
||||||
|
#include "Desktop.H"
|
||||||
|
#endif
|
||||||
|
#include <FL/Fl_Box.H>
|
||||||
|
#include <FL/Fl_Return_Button.H>
|
||||||
|
#include <FL/Fl_Input.H>
|
||||||
|
#include <FL/Fl_Menu_Item.H>
|
||||||
|
#include <FL/fl_draw.H>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "FrameWindow.H"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
// it is possible for the window to be deleted or withdrawn while
|
||||||
|
// the menu is up. This will detect that case (with reasonable
|
||||||
|
// reliability):
|
||||||
|
static int
|
||||||
|
window_deleted(Frame* c)
|
||||||
|
{
|
||||||
|
return c->state() != NORMAL
|
||||||
|
&& c->state() != ICONIC
|
||||||
|
&& c->state() != OTHER_DESKTOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
frame_callback(Fl_Widget*, void*d)
|
||||||
|
{
|
||||||
|
Frame* c = (Frame*)d;
|
||||||
|
if (window_deleted(c)) return;
|
||||||
|
c->raise();
|
||||||
|
c->activate(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
// raise it but also put it on the current desktop:
|
||||||
|
static void
|
||||||
|
move_frame_callback(Fl_Widget*, void*d)
|
||||||
|
{
|
||||||
|
Frame* c = (Frame*)d;
|
||||||
|
if (window_deleted(c)) return;
|
||||||
|
c->desktop(Desktop::current());
|
||||||
|
c->raise();
|
||||||
|
c->activate(2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SCREEN_DX 1 // offset to corner of contents area
|
||||||
|
#define SCREEN_W (MENU_ICON_W-2) // size of area to draw contents in
|
||||||
|
#define SCREEN_H (MENU_ICON_H-2) // size of area to draw contents in
|
||||||
|
|
||||||
|
#define MAX_NESTING_DEPTH 32
|
||||||
|
|
||||||
|
extern Fl_Window* Root;
|
||||||
|
|
||||||
|
static void
|
||||||
|
frame_label_draw(const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align)
|
||||||
|
{
|
||||||
|
Frame* f = (Frame*)(o->value);
|
||||||
|
if (window_deleted(f)) return;
|
||||||
|
fl_draw_box(FL_THIN_DOWN_BOX, X, Y, MENU_ICON_W, MENU_ICON_H, FL_GRAY);
|
||||||
|
for (Frame* c = Frame::first; c; c = c->next) {
|
||||||
|
if (c->state() != UNMAPPED && (c==f || c->is_transient_for(f))) {
|
||||||
|
int x = ((c->x()-Root->x())*SCREEN_W+Root->w()/2)/Root->w();
|
||||||
|
int w = (c->w()*SCREEN_W+Root->w()-1)/Root->w();
|
||||||
|
if (w > SCREEN_W) w = SCREEN_W;
|
||||||
|
if (w < 3) w = 3;
|
||||||
|
if (x+w > SCREEN_W) x = SCREEN_W-w;
|
||||||
|
if (x < 0) x = 0;
|
||||||
|
int y = ((c->y()-Root->y())*SCREEN_H+Root->h()/2)/Root->h();
|
||||||
|
int h = (c->h()*SCREEN_H+Root->h()-1)/Root->h();
|
||||||
|
if (h > SCREEN_H) h = SCREEN_H;
|
||||||
|
if (h < 3) h = 3;
|
||||||
|
if (y+h > SCREEN_H) y = SCREEN_H-h;
|
||||||
|
if (y < 0) y = 0;
|
||||||
|
fl_color(FL_BLACK);
|
||||||
|
if (c->state() == ICONIC)
|
||||||
|
fl_rect(X+x+SCREEN_DX, Y+y+SCREEN_DX, w, h);
|
||||||
|
else
|
||||||
|
fl_rectf(X+x+SCREEN_DX, Y+y+SCREEN_DX, w, h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fl_font(o->font, o->size);
|
||||||
|
fl_color((Fl_Color)o->color);
|
||||||
|
const char* l = f->label(); if (!l) l = "unnamed";
|
||||||
|
fl_draw(l, X+MENU_ICON_W+3, Y, W-MENU_ICON_W-3, H, align);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
frame_label_measure(const Fl_Label* o, int& W, int& H)
|
||||||
|
{
|
||||||
|
Frame* f = (Frame*)(o->value);
|
||||||
|
if (window_deleted(f)) {W = MENU_ICON_W+3; H = MENU_ICON_H; return;}
|
||||||
|
const char* l = f->label(); if (!l) l = "unnamed";
|
||||||
|
fl_font(o->font, o->size);
|
||||||
|
fl_measure(l, W, H);
|
||||||
|
W += MENU_ICON_W+3;
|
||||||
|
if (W > MAX_MENU_WIDTH) W = MAX_MENU_WIDTH;
|
||||||
|
if (H < MENU_ICON_H) H = MENU_ICON_H;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This labeltype is used for non-frame items so the text can line
|
||||||
|
// up with the icons:
|
||||||
|
|
||||||
|
static void
|
||||||
|
label_draw(const Fl_Label* o, int X, int Y, int W, int H, Fl_Align align)
|
||||||
|
{
|
||||||
|
fl_font(o->font, o->size);
|
||||||
|
fl_color((Fl_Color)o->color);
|
||||||
|
fl_draw(o->value, X+MENU_ICON_W+3, Y, W-MENU_ICON_W-3, H, align);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
label_measure(const Fl_Label* o, int& W, int& H)
|
||||||
|
{
|
||||||
|
fl_font(o->font, o->size);
|
||||||
|
fl_measure(o->value, W, H);
|
||||||
|
W += MENU_ICON_W+3;
|
||||||
|
if (W > MAX_MENU_WIDTH) W = MAX_MENU_WIDTH;
|
||||||
|
if (H < MENU_ICON_H) H = MENU_ICON_H;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FRAME_LABEL FL_FREE_LABELTYPE
|
||||||
|
#define TEXT_LABEL Fl_Labeltype(FL_FREE_LABELTYPE+1)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void
|
||||||
|
cancel_cb(Fl_Widget* w, void*)
|
||||||
|
{
|
||||||
|
w->window()->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
|
||||||
|
static void
|
||||||
|
desktop_cb(Fl_Widget*, void* v)
|
||||||
|
{
|
||||||
|
Desktop::current((Desktop*)v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
delete_desktop_cb(Fl_Widget*, void* v)
|
||||||
|
{
|
||||||
|
delete (Desktop*)v;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ASK_FOR_NEW_DESKTOP_NAME
|
||||||
|
|
||||||
|
static Fl_Input* new_desktop_input;
|
||||||
|
|
||||||
|
static void
|
||||||
|
new_desktop_ok_cb(Fl_Widget* w, void*)
|
||||||
|
{
|
||||||
|
w->window()->hide();
|
||||||
|
Desktop::current(new Desktop(new_desktop_input->value(), Desktop::available_number()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
new_desktop_cb(Fl_Widget*, void*)
|
||||||
|
{
|
||||||
|
if (!new_desktop_input) {
|
||||||
|
FrameWindow* w = new FrameWindow(190,90);
|
||||||
|
new_desktop_input = new Fl_Input(10,30,170,25,"New desktop name:");
|
||||||
|
new_desktop_input->align(FL_ALIGN_TOP_LEFT);
|
||||||
|
new_desktop_input->labelfont(FL_BOLD);
|
||||||
|
Fl_Return_Button* b = new Fl_Return_Button(100,60,80,20,"OK");
|
||||||
|
b->callback(new_desktop_ok_cb);
|
||||||
|
Fl_Button* b2 = new Fl_Button(10,60,80,20,"Cancel");
|
||||||
|
b2->callback(cancel_cb);
|
||||||
|
w->set_non_modal();
|
||||||
|
w->end();
|
||||||
|
}
|
||||||
|
char buf[120];
|
||||||
|
sprintf(buf, "Desktop %d", Desktop::available_number());
|
||||||
|
new_desktop_input->value(buf);
|
||||||
|
new_desktop_input->window()->hotspot(new_desktop_input);
|
||||||
|
new_desktop_input->window()->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !ASK_FOR_NEW_DESKTOP_NAME
|
||||||
|
|
||||||
|
static void
|
||||||
|
new_desktop_cb(Fl_Widget*, void*)
|
||||||
|
{
|
||||||
|
char buf[120];
|
||||||
|
int i = Desktop::available_number();
|
||||||
|
sprintf(buf, "Desktop %d", i);
|
||||||
|
Desktop::current(new Desktop(buf, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static void
|
||||||
|
exit_cb(Fl_Widget*, void*)
|
||||||
|
{
|
||||||
|
Frame::save_protocol();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
logout_cb(Fl_Widget*, void*)
|
||||||
|
{
|
||||||
|
static FrameWindow* w;
|
||||||
|
if (!w) {
|
||||||
|
w = new FrameWindow(190,90);
|
||||||
|
Fl_Box* l = new Fl_Box(0, 0, 190, 60, "Really log out?");
|
||||||
|
l->labelfont(FL_BOLD);
|
||||||
|
Fl_Return_Button* b = new Fl_Return_Button(100,60,80,20,"OK");
|
||||||
|
b->callback(exit_cb);
|
||||||
|
Fl_Button* b2 = new Fl_Button(10,60,80,20,"Cancel");
|
||||||
|
b2->callback(cancel_cb);
|
||||||
|
w->set_non_modal();
|
||||||
|
w->end();
|
||||||
|
}
|
||||||
|
w->hotspot(w);
|
||||||
|
w->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#if XTERM_MENU_ITEM || WMX_MENU_ITEMS
|
||||||
|
|
||||||
|
static const char* xtermname = "xterm";
|
||||||
|
|
||||||
|
static void
|
||||||
|
spawn_cb(Fl_Widget*, void*n)
|
||||||
|
{
|
||||||
|
char* name = (char*)n;
|
||||||
|
// strange code thieved from 9wm to avoid leaving zombies
|
||||||
|
if (fork() == 0) {
|
||||||
|
if (fork() == 0) {
|
||||||
|
close(ConnectionNumber(fl_display));
|
||||||
|
if (name == xtermname) execlp(name, name, "-ut", 0);
|
||||||
|
else execl(name, name, 0);
|
||||||
|
fprintf(stderr, "flwm: can't run %s, %s\n", name, strerror(errno));
|
||||||
|
XBell(fl_display, 70);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
wait((int *) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Fl_Menu_Item other_menu_items[] = {
|
||||||
|
#if XTERM_MENU_ITEM
|
||||||
|
{"New xterm", 0, spawn_cb, (void*)xtermname, 0, 0, 0, 12},
|
||||||
|
#endif
|
||||||
|
#if DESKTOPS
|
||||||
|
{"New desktop", 0, new_desktop_cb, 0, 0, 0, 0, 12},
|
||||||
|
#endif
|
||||||
|
{"Logout", 0, logout_cb, 0, 0, 0, 0, 12},
|
||||||
|
{0}};
|
||||||
|
#define num_other_items (sizeof(other_menu_items)/sizeof(Fl_Menu_Item))
|
||||||
|
|
||||||
|
// use this to fill in a menu location:
|
||||||
|
static void
|
||||||
|
init(Fl_Menu_Item& m, const char* data)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_STYLES
|
||||||
|
m.style = 0;
|
||||||
|
#endif
|
||||||
|
m.label(data);
|
||||||
|
m.flags = 0;
|
||||||
|
m.labeltype(FL_NORMAL_LABEL);
|
||||||
|
m.shortcut(0);
|
||||||
|
m.labelfont(MENU_FONT_SLOT);
|
||||||
|
m.labelsize(MENU_FONT_SIZE);
|
||||||
|
m.labelcolor(FL_BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WMX_MENU_ITEMS
|
||||||
|
|
||||||
|
// wmxlist is an array of char* pointers (for efficient sorting purposes),
|
||||||
|
// which are stored in wmxbuffer (for memory efficiency and to avoid
|
||||||
|
// freeing and fragmentation)
|
||||||
|
static char** wmxlist = NULL;
|
||||||
|
static int wmxlistsize = 0;
|
||||||
|
// wmx commands are read from ~/.wmx,
|
||||||
|
// they are stored null-separated here:
|
||||||
|
static char* wmxbuffer = NULL;
|
||||||
|
static int wmxbufsize = 0;
|
||||||
|
static int num_wmx = 0;
|
||||||
|
static time_t wmx_time = 0;
|
||||||
|
static int wmx_pathlen = 0;
|
||||||
|
|
||||||
|
static int
|
||||||
|
scan_wmx_dir (char *path, int bufindex, int nest)
|
||||||
|
{
|
||||||
|
DIR* dir = opendir(path);
|
||||||
|
struct stat st;
|
||||||
|
int pathlen = strlen (path);
|
||||||
|
if (dir) {
|
||||||
|
struct dirent* ent;
|
||||||
|
while ((ent=readdir(dir))) {
|
||||||
|
if (ent->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
strcpy(path+pathlen, ent->d_name);
|
||||||
|
if (stat(path, &st) < 0) continue;
|
||||||
|
int len = pathlen+strlen(ent->d_name);
|
||||||
|
// worst-case alloc needs
|
||||||
|
if (bufindex+len+nest+1 > wmxbufsize)
|
||||||
|
wmxbuffer = (char*)realloc(wmxbuffer, (wmxbufsize+=1024));
|
||||||
|
for (int i=0; i<nest; i++)
|
||||||
|
wmxbuffer[bufindex++] = '/'; // extra slash marks menu titles
|
||||||
|
if (S_ISDIR(st.st_mode) && (st.st_mode & 0555) && nest<MAX_NESTING_DEPTH){
|
||||||
|
strcpy(wmxbuffer+bufindex, path);
|
||||||
|
bufindex += len+1;
|
||||||
|
strcat(path, "/");
|
||||||
|
bufindex = scan_wmx_dir (path, bufindex, nest+1);
|
||||||
|
num_wmx++;
|
||||||
|
} else if (S_ISREG(st.st_mode) && (st.st_mode & 0111)) {
|
||||||
|
// make sure it exists and is an executable file:
|
||||||
|
strcpy(wmxbuffer+bufindex, path);
|
||||||
|
bufindex += len+1;
|
||||||
|
num_wmx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
return bufindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// comparison for qsort
|
||||||
|
// We keep submenus together by noting that they're proper superstrings
|
||||||
|
static int
|
||||||
|
wmxCompare(const void *A, const void *B)
|
||||||
|
{
|
||||||
|
char *pA, *pB;
|
||||||
|
pA = *(char **)A;
|
||||||
|
pB = *(char **)B;
|
||||||
|
|
||||||
|
pA += strspn(pA, "/");
|
||||||
|
pB += strspn(pB, "/");
|
||||||
|
|
||||||
|
// caseless compare
|
||||||
|
while (*pA && *pB) {
|
||||||
|
if (toupper(*pA) > toupper(*pB))
|
||||||
|
return(1);
|
||||||
|
if (toupper(*pA) < toupper(*pB))
|
||||||
|
return(-1);
|
||||||
|
pA++;
|
||||||
|
pB++;
|
||||||
|
}
|
||||||
|
if (*pA)
|
||||||
|
return(1);
|
||||||
|
if (*pB)
|
||||||
|
return(-1);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
load_wmx()
|
||||||
|
{
|
||||||
|
const char* home=getenv("HOME"); if (!home) home = ".";
|
||||||
|
char path[1024];
|
||||||
|
strcpy(path, home);
|
||||||
|
if (path[strlen(path)-1] != '/') strcat(path, "/");
|
||||||
|
strcat(path, ".wmx/");
|
||||||
|
struct stat st; if (stat(path, &st) < 0) return;
|
||||||
|
if (st.st_mtime == wmx_time) return;
|
||||||
|
wmx_time = st.st_mtime;
|
||||||
|
num_wmx = 0;
|
||||||
|
wmx_pathlen = strlen(path);
|
||||||
|
scan_wmx_dir(path, 0, 0);
|
||||||
|
|
||||||
|
// Build wmxlist
|
||||||
|
if (num_wmx > wmxlistsize) {
|
||||||
|
if (wmxlist)
|
||||||
|
delete [] wmxlist;
|
||||||
|
wmxlist = new char *[num_wmx];
|
||||||
|
wmxlistsize = num_wmx;
|
||||||
|
}
|
||||||
|
for (int i=0; i<num_wmx; i++) {
|
||||||
|
char* cmd = wmxbuffer;
|
||||||
|
|
||||||
|
for (int j = 0; j < num_wmx; j++) {
|
||||||
|
wmxlist[j] = cmd;
|
||||||
|
cmd += strlen(cmd)+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(wmxlist, num_wmx, sizeof(char *), wmxCompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int exit_flag; // set by the -x switch
|
||||||
|
|
||||||
|
static int is_active_frame(Frame* c) {
|
||||||
|
for (Frame* a = Frame::activeFrame(); a; a = a->transient_for())
|
||||||
|
if (a == c) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ShowTabMenu(int tab)
|
||||||
|
{
|
||||||
|
|
||||||
|
static char beenhere;
|
||||||
|
if (!beenhere) {
|
||||||
|
beenhere = 1;
|
||||||
|
Fl::set_labeltype(FRAME_LABEL, frame_label_draw, frame_label_measure);
|
||||||
|
Fl::set_labeltype(TEXT_LABEL, label_draw, label_measure);
|
||||||
|
if (exit_flag) {
|
||||||
|
Fl_Menu_Item* m = other_menu_items+num_other_items-2;
|
||||||
|
m->label("Exit");
|
||||||
|
m->callback(exit_cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Fl_Menu_Item* menu = 0;
|
||||||
|
static int arraysize = 0;
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
int one_desktop = !Desktop::first->next;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// count up how many items are on the menu:
|
||||||
|
|
||||||
|
int n = num_other_items;
|
||||||
|
#if WMX_MENU_ITEMS
|
||||||
|
load_wmx();
|
||||||
|
if (num_wmx) {
|
||||||
|
n -= 1; // delete "new xterm"
|
||||||
|
// add wmx items
|
||||||
|
int level = 0;
|
||||||
|
for (int i=0; i<num_wmx; i++) {
|
||||||
|
int nextlev = (i==num_wmx-1)?0:strspn(wmxlist[i+1], "/")-1;
|
||||||
|
if (nextlev < level) {
|
||||||
|
n += level-nextlev;
|
||||||
|
level = nextlev;
|
||||||
|
} else if (nextlev > level)
|
||||||
|
level++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
// count number of items per desktop in these variables:
|
||||||
|
int numsticky = 0;
|
||||||
|
Desktop* d;
|
||||||
|
for (d = Desktop::first; d; d = d->next) d->junk = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// every frame contributes 1 item:
|
||||||
|
Frame* c;
|
||||||
|
for (c = Frame::first; c; c = c->next) {
|
||||||
|
if (c->state() == UNMAPPED || c->transient_for()) continue;
|
||||||
|
#if DESKTOPS
|
||||||
|
if (!c->desktop()) {
|
||||||
|
numsticky++;
|
||||||
|
} else {
|
||||||
|
c->desktop()->junk++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DESKTOPS
|
||||||
|
if (!one_desktop) {
|
||||||
|
// add the sticky "desktop":
|
||||||
|
n += 2; if (!numsticky) n++;
|
||||||
|
if (Desktop::current()) {
|
||||||
|
n += numsticky;
|
||||||
|
Desktop::current()->junk += numsticky;
|
||||||
|
}
|
||||||
|
// every desktop contributes menu title, null terminator,
|
||||||
|
// and possible delete:
|
||||||
|
for (d = Desktop::first; d; d = d->next) {
|
||||||
|
n += 2; if (!d->junk) n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (n > arraysize) {
|
||||||
|
delete[] menu;
|
||||||
|
menu = new Fl_Menu_Item[arraysize = n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the menu:
|
||||||
|
n = 0;
|
||||||
|
const Fl_Menu_Item* preset = 0;
|
||||||
|
const Fl_Menu_Item* first_on_desk = 0;
|
||||||
|
#if DESKTOPS
|
||||||
|
if (one_desktop) {
|
||||||
|
#endif
|
||||||
|
for (c = Frame::first; c; c = c->next) {
|
||||||
|
if (c->state() == UNMAPPED || c->transient_for()) continue;
|
||||||
|
init(menu[n],(char*)c);
|
||||||
|
menu[n].labeltype(FRAME_LABEL);
|
||||||
|
menu[n].callback(frame_callback, c);
|
||||||
|
if (is_active_frame(c)) preset = menu+n;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (n > 0) first_on_desk = menu;
|
||||||
|
#if DESKTOPS
|
||||||
|
} else for (d = Desktop::first; ; d = d->next) {
|
||||||
|
// this loop adds the "sticky" desktop last, when d==0
|
||||||
|
if (d == Desktop::current()) preset = menu+n;
|
||||||
|
init(menu[n], d ? d->name() : "Sticky");
|
||||||
|
menu[n].callback(desktop_cb, d);
|
||||||
|
menu[n].flags = FL_SUBMENU;
|
||||||
|
n++;
|
||||||
|
if (d && !d->junk) {
|
||||||
|
init(menu[n],"delete this desktop");
|
||||||
|
menu[n].callback(delete_desktop_cb, d);
|
||||||
|
n++;
|
||||||
|
} else if (!d && !numsticky) {
|
||||||
|
init(menu[n],"(empty)");
|
||||||
|
menu[n].callback_ = 0;
|
||||||
|
menu[n].deactivate();
|
||||||
|
n++;
|
||||||
|
} else {
|
||||||
|
if (d == Desktop::current()) first_on_desk = menu+n;
|
||||||
|
for (c = Frame::first; c; c = c->next) {
|
||||||
|
if (c->state() == UNMAPPED || c->transient_for()) continue;
|
||||||
|
if (c->desktop() == d || !c->desktop() && d == Desktop::current()) {
|
||||||
|
init(menu[n],(char*)c);
|
||||||
|
menu[n].labeltype(FRAME_LABEL);
|
||||||
|
menu[n].callback(d == Desktop::current() ?
|
||||||
|
frame_callback : move_frame_callback, c);
|
||||||
|
if (d == Desktop::current() && is_active_frame(c)) preset = menu+n;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu[n].label(0); n++; // terminator for submenu
|
||||||
|
if (!d) break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For ALT+Tab, move the selection forward or backward:
|
||||||
|
if (tab > 0 && first_on_desk) {
|
||||||
|
if (!preset)
|
||||||
|
preset = first_on_desk;
|
||||||
|
else {
|
||||||
|
preset++;
|
||||||
|
if (!preset->label() || preset->callback_ != frame_callback)
|
||||||
|
preset = first_on_desk;
|
||||||
|
}
|
||||||
|
} else if (tab < 0 && first_on_desk) {
|
||||||
|
if (preset && preset != first_on_desk)
|
||||||
|
preset--;
|
||||||
|
else {
|
||||||
|
// go to end of menu
|
||||||
|
preset = first_on_desk;
|
||||||
|
while (preset[1].label() && preset[1].callback_ == frame_callback)
|
||||||
|
preset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WMX_MENU_ITEMS
|
||||||
|
// put wmx-style commands above that:
|
||||||
|
if (num_wmx > 0) {
|
||||||
|
char* cmd;
|
||||||
|
int pathlen[MAX_NESTING_DEPTH];
|
||||||
|
int level = 0;
|
||||||
|
pathlen[0] = wmx_pathlen;
|
||||||
|
for (int i = 0; i < num_wmx; i++) {
|
||||||
|
cmd = wmxlist[i];
|
||||||
|
cmd += strspn(cmd, "/")-1;
|
||||||
|
init(menu[n], cmd+pathlen[level]);
|
||||||
|
#if DESKTOPS
|
||||||
|
if (one_desktop)
|
||||||
|
#endif
|
||||||
|
if (!level)
|
||||||
|
menu[n].labeltype(TEXT_LABEL);
|
||||||
|
|
||||||
|
int nextlev = (i==num_wmx-1)?0:strspn(wmxlist[i+1], "/")-1;
|
||||||
|
if (nextlev < level) {
|
||||||
|
menu[n].callback(spawn_cb, cmd);
|
||||||
|
// Close 'em off
|
||||||
|
for (; level>nextlev; level--)
|
||||||
|
init(menu[++n], 0);
|
||||||
|
} else if (nextlev > level) {
|
||||||
|
// This should be made a submenu
|
||||||
|
pathlen[++level] = strlen(cmd)+1; // extra for next trailing /
|
||||||
|
menu[n].flags = FL_SUBMENU;
|
||||||
|
menu[n].callback((Fl_Callback*)0);
|
||||||
|
} else {
|
||||||
|
menu[n].callback(spawn_cb, cmd);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the fixed menu items at the bottom:
|
||||||
|
#if XTERM_MENU_ITEM
|
||||||
|
if (num_wmx) // if wmx commands, delete the built-in xterm item:
|
||||||
|
memcpy(menu+n, other_menu_items+1, sizeof(other_menu_items)-sizeof(Fl_Menu_Item));
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
memcpy(menu+n, other_menu_items, sizeof(other_menu_items));
|
||||||
|
#if DESKTOPS
|
||||||
|
if (one_desktop)
|
||||||
|
#endif
|
||||||
|
// fix the menus items so they are indented to align with window names:
|
||||||
|
while (menu[n].label()) menu[n++].labeltype(TEXT_LABEL);
|
||||||
|
|
||||||
|
const Fl_Menu_Item* picked =
|
||||||
|
menu->popup(Fl::event_x(), Fl::event_y(), 0, preset);
|
||||||
|
if (picked && picked->callback()) picked->do_callback(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowMenu() {ShowTabMenu(0);}
|
||||||
121
README
Normal file
121
README
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
flwm Version 0.25
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
How to compile flwm:
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
You need fltk. If you do not have it yet, download it from
|
||||||
|
http://fltk.easysw.com, and compile and install it.
|
||||||
|
|
||||||
|
To customize flwm (for instance to turn on click-to-type), edit the
|
||||||
|
config.h file.
|
||||||
|
|
||||||
|
Type "./configure" (not necessary if you have gmake)
|
||||||
|
|
||||||
|
Type "make"
|
||||||
|
|
||||||
|
Become superuser and type "make install"
|
||||||
|
|
||||||
|
If you wish to edit the code, type "make depend"
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
How to run flwm:
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Flwm should be run by X when it logs you in. This is done by putting
|
||||||
|
a call to flwm into the file ~/.xinitrc. With any luck you already
|
||||||
|
have this file. If not try copying /usr/X11/lib/X11/xinit/xinitrc.
|
||||||
|
Edit the file and try to remove any call to another window manager
|
||||||
|
(these are usually near the end).
|
||||||
|
|
||||||
|
Recommended contents of ~/.xinitrc:
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
xsetroot -solid \#006060
|
||||||
|
xrdb .Xresources
|
||||||
|
# <xset, xmodmap, other configuration programs>
|
||||||
|
flwm &
|
||||||
|
WindowManager=$!
|
||||||
|
# <xterm, other automatically-launched programs>
|
||||||
|
wait $WindowManager
|
||||||
|
|
||||||
|
ALLOWING THE WINDOW MANAGER TO EXIT W/O LOGOUT:
|
||||||
|
|
||||||
|
That is the most user-friendly but it logs you out when flwm exits,
|
||||||
|
which means it logs out if flwm crashes (:-)) and you cannot switch
|
||||||
|
window managers. Another possibility is to run another program last
|
||||||
|
so flwm can exit, by putting lines like this at the end:
|
||||||
|
|
||||||
|
/usr/local/bin/flwm -x &
|
||||||
|
exec rxvt -geometry 80x11+8-8 -C -T "Ctrl-D_to_logout"
|
||||||
|
|
||||||
|
The -x tells flwm to put "exit" on the menu rather than "logout".
|
||||||
|
|
||||||
|
REDHAT USERS:
|
||||||
|
|
||||||
|
You may want to run the program "./flwm_wmconfig". This will read
|
||||||
|
RedHat's window manager menu configuration files and build an initial
|
||||||
|
.wmx directory so you have a large set of menu items that run
|
||||||
|
programs.
|
||||||
|
|
||||||
|
SGI IRIX:
|
||||||
|
|
||||||
|
You need to edit the file ~/.xsession instead of ~/.xinitrc.
|
||||||
|
|
||||||
|
SGI's version of XDM has a nice feature so that the window manager can
|
||||||
|
still have a logout command, but you are not logged out if it
|
||||||
|
crashes. This is done by running the programs "reaper" and
|
||||||
|
"endsession", as in this sample .xsession file:
|
||||||
|
|
||||||
|
#! /bin/sh
|
||||||
|
xsetroot -solid \#004040
|
||||||
|
xrdb .Xresources
|
||||||
|
reaper
|
||||||
|
flwm -x &
|
||||||
|
xwsh -console -t console -iconic &
|
||||||
|
|
||||||
|
Also create the file "~/.wmx/Logout" with these contents:
|
||||||
|
|
||||||
|
#! /bin/sh
|
||||||
|
endsession
|
||||||
|
|
||||||
|
The result will be that flwm has a menu itme "Logout" that logs you
|
||||||
|
out.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Usage:
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Type "man flwm" for the manual page.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Acknoledgements
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
This program was inspired by and much code copied from the "wm2"
|
||||||
|
window manager by Chris Cannam <cannam@zands.demon.co.uk>
|
||||||
|
|
||||||
|
Code contributions by Steve );Hara-Smith <steveo@iol.ie>
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
Copyright (C) 1998-1999 Bill Spitzak
|
||||||
|
----------------------------------------------------------------
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
USA.
|
||||||
|
|
||||||
|
Written by Bill Spitzak spitzak@d2.com
|
||||||
|
----------------------------------------------------------------
|
||||||
|
END
|
||||||
|
----------------------------------------------------------------
|
||||||
427
Rotated.C
Normal file
427
Rotated.C
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
// Rotated text drawing with X.
|
||||||
|
|
||||||
|
// Original code:
|
||||||
|
// Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) */
|
||||||
|
//
|
||||||
|
// Modifications for fltk:
|
||||||
|
// Copyright (c) 1997 Bill Spitzak (spitzak@d2.com)
|
||||||
|
// Modifications are to draw using the current fl_font. All fonts
|
||||||
|
// used are cached in local structures. This can get real expensive,
|
||||||
|
// use "
|
||||||
|
|
||||||
|
/* xvertext, Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma)
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software and its
|
||||||
|
* documentation for any purpose and without fee is hereby granted, provided
|
||||||
|
* that the above copyright notice appear in all copies and that both the
|
||||||
|
* copyright notice and this permission notice appear in supporting
|
||||||
|
* documentation. All work developed as a consequence of the use of
|
||||||
|
* this program should duly acknowledge such use. No representations are
|
||||||
|
* made about the suitability of this software for any purpose. It is
|
||||||
|
* provided "as is" without express or implied warranty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// if not defined then portions not used by flwm are included:
|
||||||
|
#define FLWM 1
|
||||||
|
|
||||||
|
/* ********************************************************************** */
|
||||||
|
|
||||||
|
#include <FL/x.H>
|
||||||
|
#include <FL/fl_draw.H>
|
||||||
|
#include "Rotated.H"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct BitmapStruct {
|
||||||
|
int bit_w;
|
||||||
|
int bit_h;
|
||||||
|
Pixmap bm;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XRotCharStruct {
|
||||||
|
int ascent;
|
||||||
|
int descent;
|
||||||
|
int lbearing;
|
||||||
|
int rbearing;
|
||||||
|
int width;
|
||||||
|
BitmapStruct glyph;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XRotFontStruct {
|
||||||
|
int dir;
|
||||||
|
int height;
|
||||||
|
int max_ascent;
|
||||||
|
int max_descent;
|
||||||
|
int max_char;
|
||||||
|
int min_char;
|
||||||
|
XFontStruct* xfontstruct;
|
||||||
|
XRotCharStruct per_char[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* *** Load the rotated version of a given font *** */
|
||||||
|
|
||||||
|
static XRotFontStruct*
|
||||||
|
XRotLoadFont(Display *dpy, XFontStruct* fontstruct, int dir)
|
||||||
|
{
|
||||||
|
char val;
|
||||||
|
XImage *I1, *I2;
|
||||||
|
Pixmap canvas;
|
||||||
|
Window root;
|
||||||
|
int screen;
|
||||||
|
GC font_gc;
|
||||||
|
char text[3];/*, errstr[300];*/
|
||||||
|
|
||||||
|
XRotFontStruct *rotfont;
|
||||||
|
int ichar, i, j, index, boxlen = 60;
|
||||||
|
int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
|
||||||
|
int min_char, max_char;
|
||||||
|
unsigned char *vertdata, *bitdata;
|
||||||
|
int ascent, descent, lbearing, rbearing;
|
||||||
|
int on = 1, off = 0;
|
||||||
|
|
||||||
|
/* useful macros ... */
|
||||||
|
screen = DefaultScreen(dpy);
|
||||||
|
root = DefaultRootWindow(dpy);
|
||||||
|
|
||||||
|
/* create the depth 1 canvas bitmap ... */
|
||||||
|
canvas = XCreatePixmap(dpy, root, boxlen, boxlen, 1);
|
||||||
|
|
||||||
|
/* create a GC ... */
|
||||||
|
font_gc = XCreateGC(dpy, canvas, 0, 0);
|
||||||
|
XSetBackground(dpy, font_gc, off);
|
||||||
|
|
||||||
|
XSetFont(dpy, font_gc, fontstruct->fid);
|
||||||
|
|
||||||
|
/* allocate space for rotated font ... */
|
||||||
|
rotfont = (XRotFontStruct *)malloc((unsigned)sizeof(XRotFontStruct));
|
||||||
|
|
||||||
|
/* determine which characters are defined in font ... */
|
||||||
|
min_char = fontstruct->min_char_or_byte2;
|
||||||
|
if (min_char<0) min_char = 0;
|
||||||
|
rotfont->min_char = min_char;
|
||||||
|
max_char = fontstruct->max_char_or_byte2;
|
||||||
|
if (max_char>255) max_char = 255;
|
||||||
|
rotfont->max_char = max_char;
|
||||||
|
|
||||||
|
/* some overall font data ... */
|
||||||
|
rotfont->dir = dir;
|
||||||
|
rotfont->max_ascent = fontstruct->max_bounds.ascent;
|
||||||
|
rotfont->max_descent = fontstruct->max_bounds.descent;
|
||||||
|
rotfont->height = rotfont->max_ascent+rotfont->max_descent;
|
||||||
|
|
||||||
|
rotfont->xfontstruct = fontstruct;
|
||||||
|
/* remember xfontstruct for `normal' text ... */
|
||||||
|
if (dir != 0) {
|
||||||
|
/* font needs rotation ... */
|
||||||
|
/* loop through each character ... */
|
||||||
|
for (ichar = min_char; ichar <= max_char; ichar++) {
|
||||||
|
|
||||||
|
index = ichar-fontstruct->min_char_or_byte2;
|
||||||
|
|
||||||
|
/* per char dimensions ... */
|
||||||
|
ascent = rotfont->per_char[ichar].ascent =
|
||||||
|
fontstruct->per_char[index].ascent;
|
||||||
|
descent = rotfont->per_char[ichar].descent =
|
||||||
|
fontstruct->per_char[index].descent;
|
||||||
|
lbearing = rotfont->per_char[ichar].lbearing =
|
||||||
|
fontstruct->per_char[index].lbearing;
|
||||||
|
rbearing = rotfont->per_char[ichar].rbearing =
|
||||||
|
fontstruct->per_char[index].rbearing;
|
||||||
|
rotfont->per_char[ichar].width =
|
||||||
|
fontstruct->per_char[index].width;
|
||||||
|
|
||||||
|
/* some space chars have zero body, but a bitmap can't have ... */
|
||||||
|
if (!ascent && !descent)
|
||||||
|
ascent = rotfont->per_char[ichar].ascent = 1;
|
||||||
|
if (!lbearing && !rbearing)
|
||||||
|
rbearing = rotfont->per_char[ichar].rbearing = 1;
|
||||||
|
|
||||||
|
/* glyph width and height when vertical ... */
|
||||||
|
vert_w = rbearing-lbearing;
|
||||||
|
vert_h = ascent+descent;
|
||||||
|
|
||||||
|
/* width in bytes ... */
|
||||||
|
vert_len = (vert_w-1)/8+1;
|
||||||
|
|
||||||
|
XSetForeground(dpy, font_gc, off);
|
||||||
|
XFillRectangle(dpy, canvas, font_gc, 0, 0, boxlen, boxlen);
|
||||||
|
|
||||||
|
/* draw the character centre top right on canvas ... */
|
||||||
|
sprintf(text, "%c", ichar);
|
||||||
|
XSetForeground(dpy, font_gc, on);
|
||||||
|
XDrawImageString(dpy, canvas, font_gc, boxlen/2 - lbearing,
|
||||||
|
boxlen/2 - descent, text, 1);
|
||||||
|
|
||||||
|
/* reserve memory for first XImage ... */
|
||||||
|
vertdata = (unsigned char *) malloc((unsigned)(vert_len*vert_h));
|
||||||
|
|
||||||
|
/* create the XImage ... */
|
||||||
|
I1 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap,
|
||||||
|
0, (char *)vertdata, vert_w, vert_h, 8, 0);
|
||||||
|
|
||||||
|
// if (I1 == NULL) ... do something here
|
||||||
|
|
||||||
|
I1->byte_order = I1->bitmap_bit_order = MSBFirst;
|
||||||
|
|
||||||
|
/* extract character from canvas ... */
|
||||||
|
XGetSubImage(dpy, canvas, boxlen/2, boxlen/2-vert_h,
|
||||||
|
vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
|
||||||
|
I1->format = XYBitmap;
|
||||||
|
|
||||||
|
/* width, height of rotated character ... */
|
||||||
|
if (dir == 2) {
|
||||||
|
bit_w = vert_w;
|
||||||
|
bit_h = vert_h;
|
||||||
|
} else {
|
||||||
|
bit_w = vert_h;
|
||||||
|
bit_h = vert_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* width in bytes ... */
|
||||||
|
bit_len = (bit_w-1)/8 + 1;
|
||||||
|
|
||||||
|
rotfont->per_char[ichar].glyph.bit_w = bit_w;
|
||||||
|
rotfont->per_char[ichar].glyph.bit_h = bit_h;
|
||||||
|
|
||||||
|
/* reserve memory for the rotated image ... */
|
||||||
|
bitdata = (unsigned char *)calloc((unsigned)(bit_h*bit_len), 1);
|
||||||
|
|
||||||
|
/* create the image ... */
|
||||||
|
I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
|
||||||
|
(char *)bitdata, bit_w, bit_h, 8, 0);
|
||||||
|
|
||||||
|
// if (I2 == NULL) ... error
|
||||||
|
|
||||||
|
I2->byte_order = I2->bitmap_bit_order = MSBFirst;
|
||||||
|
|
||||||
|
/* map vertical data to rotated character ... */
|
||||||
|
for (j = 0; j < bit_h; j++) {
|
||||||
|
for (i = 0; i < bit_w; i++) {
|
||||||
|
/* map bits ... */
|
||||||
|
if (dir == 1)
|
||||||
|
val = vertdata[i*vert_len + (vert_w-j-1)/8] &
|
||||||
|
(128>>((vert_w-j-1)%8));
|
||||||
|
|
||||||
|
else if (dir == 2)
|
||||||
|
val = vertdata[(vert_h-j-1)*vert_len + (vert_w-i-1)/8] &
|
||||||
|
(128>>((vert_w-i-1)%8));
|
||||||
|
|
||||||
|
else
|
||||||
|
val = vertdata[(vert_h-i-1)*vert_len + j/8] &
|
||||||
|
(128>>(j%8));
|
||||||
|
|
||||||
|
if (val)
|
||||||
|
bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
|
||||||
|
(128>>(i%8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create this character's bitmap ... */
|
||||||
|
rotfont->per_char[ichar].glyph.bm =
|
||||||
|
XCreatePixmap(dpy, root, bit_w, bit_h, 1);
|
||||||
|
|
||||||
|
/* put the image into the bitmap ... */
|
||||||
|
XPutImage(dpy, rotfont->per_char[ichar].glyph.bm,
|
||||||
|
font_gc, I2, 0, 0, 0, 0, bit_w, bit_h);
|
||||||
|
|
||||||
|
/* free the image and data ... */
|
||||||
|
XDestroyImage(I1);
|
||||||
|
XDestroyImage(I2);
|
||||||
|
/* free((char *)bitdata); -- XDestroyImage does this
|
||||||
|
free((char *)vertdata);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ichar = 0; ichar < min_char; ichar++)
|
||||||
|
rotfont->per_char[ichar] = rotfont->per_char['?'];
|
||||||
|
for (ichar = max_char+1; ichar < 256; ichar++)
|
||||||
|
rotfont->per_char[ichar] = rotfont->per_char['?'];
|
||||||
|
|
||||||
|
/* free pixmap and GC ... */
|
||||||
|
XFreePixmap(dpy, canvas);
|
||||||
|
XFreeGC(dpy, font_gc);
|
||||||
|
|
||||||
|
return rotfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *** Free the resources associated with a rotated font *** */
|
||||||
|
|
||||||
|
static void XRotUnloadFont(Display *dpy, XRotFontStruct *rotfont)
|
||||||
|
{
|
||||||
|
int ichar;
|
||||||
|
|
||||||
|
if (rotfont->dir != 0) {
|
||||||
|
/* loop through each character, freeing its pixmap ... */
|
||||||
|
for (ichar = rotfont->min_char; ichar <= rotfont->max_char; ichar++)
|
||||||
|
XFreePixmap(dpy, rotfont->per_char[ichar].glyph.bm);
|
||||||
|
}
|
||||||
|
/* rotfont should never be referenced again ... */
|
||||||
|
free((char *)rotfont);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* *** A front end to XRotPaintString : mimics XDrawString *** */
|
||||||
|
|
||||||
|
static void
|
||||||
|
XRotDrawString(Display *dpy, XRotFontStruct *rotfont, Drawable drawable,
|
||||||
|
GC gc, int x, int y, const char *str, int len)
|
||||||
|
{
|
||||||
|
int i, xp, yp, dir, ichar;
|
||||||
|
|
||||||
|
if (str == NULL || len<1) return;
|
||||||
|
|
||||||
|
dir = rotfont->dir;
|
||||||
|
|
||||||
|
/* a horizontal string is easy ... */
|
||||||
|
if (dir == 0) {
|
||||||
|
XSetFont(dpy, gc, rotfont->xfontstruct->fid);
|
||||||
|
XDrawString(dpy, drawable, gc, x, y, str, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vertical or upside down ... */
|
||||||
|
|
||||||
|
XSetFillStyle(dpy, gc, FillStippled);
|
||||||
|
|
||||||
|
/* loop through each character in string ... */
|
||||||
|
for (i = 0; i<len; i++) {
|
||||||
|
ichar = ((unsigned char*)str)[i];
|
||||||
|
|
||||||
|
/* suitable offset ... */
|
||||||
|
if (dir == 1) {
|
||||||
|
xp = x-rotfont->per_char[ichar].ascent;
|
||||||
|
yp = y-rotfont->per_char[ichar].rbearing;
|
||||||
|
}
|
||||||
|
else if (dir == 2) {
|
||||||
|
xp = x-rotfont->per_char[ichar].rbearing;
|
||||||
|
yp = y-rotfont->per_char[ichar].descent+1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
xp = x-rotfont->per_char[ichar].descent+1;
|
||||||
|
yp = y+rotfont->per_char[ichar].lbearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* draw the glyph ... */
|
||||||
|
XSetStipple(dpy, gc, rotfont->per_char[ichar].glyph.bm);
|
||||||
|
|
||||||
|
XSetTSOrigin(dpy, gc, xp, yp);
|
||||||
|
|
||||||
|
XFillRectangle(dpy, drawable, gc, xp, yp,
|
||||||
|
rotfont->per_char[ichar].glyph.bit_w,
|
||||||
|
rotfont->per_char[ichar].glyph.bit_h);
|
||||||
|
|
||||||
|
/* advance position ... */
|
||||||
|
if (dir == 1)
|
||||||
|
y -= rotfont->per_char[ichar].width;
|
||||||
|
else if (dir == 2)
|
||||||
|
x -= rotfont->per_char[ichar].width;
|
||||||
|
else
|
||||||
|
y += rotfont->per_char[ichar].width;
|
||||||
|
}
|
||||||
|
XSetFillStyle(dpy, gc, FillSolid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FLWM
|
||||||
|
/* *** Return the width of a string *** */
|
||||||
|
|
||||||
|
static int XRotTextWidth(XRotFontStruct *rotfont, const char *str, int len)
|
||||||
|
{
|
||||||
|
int i, width = 0, ichar;
|
||||||
|
|
||||||
|
if (str == NULL) return 0;
|
||||||
|
|
||||||
|
if (rotfont->dir == 0)
|
||||||
|
width = XTextWidth(rotfont->xfontstruct, str, strlen(str));
|
||||||
|
|
||||||
|
else
|
||||||
|
for (i = 0; i<len; i++) {
|
||||||
|
width += rotfont->per_char[((unsigned char*)str)[i]].width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
// the public functions use the fltk global variables for font & gc:
|
||||||
|
|
||||||
|
static XRotFontStruct* font;
|
||||||
|
|
||||||
|
void draw_rotated(const char* text, int n, int x, int y, int angle) {
|
||||||
|
if (!text || !*text) return;
|
||||||
|
/* make angle positive ... */
|
||||||
|
if (angle < 0) do angle += 360; while (angle < 0);
|
||||||
|
/* get nearest vertical or horizontal direction ... */
|
||||||
|
int dir = ((angle+45)/90)%4;
|
||||||
|
|
||||||
|
if (font && font->xfontstruct == fl_xfont && font->dir == dir) {
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
if (font) XRotUnloadFont(fl_display, font);
|
||||||
|
font = XRotLoadFont(fl_display, fl_xfont, dir);
|
||||||
|
}
|
||||||
|
XRotDrawString(fl_display, font, fl_window, fl_gc, x, y, text, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FLWM
|
||||||
|
void draw_rotated(const char* text, int x, int y, int angle) {
|
||||||
|
if (!text || !*text) return;
|
||||||
|
draw_rotated(text, strlen(text), x, y, angle);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void draw_rot90(const char* str, int n, int x, int y) {
|
||||||
|
draw_rotated(str, n, y, -x, 90);
|
||||||
|
}
|
||||||
|
void draw_rotated90(
|
||||||
|
const char* str, // the (multi-line) string
|
||||||
|
int x, int y, int w, int h, // bounding box
|
||||||
|
Fl_Align align) {
|
||||||
|
if (!str || !*str) return;
|
||||||
|
if (w && h && !fl_not_clipped(x, y, w, h)) return;
|
||||||
|
if (align & FL_ALIGN_CLIP) fl_clip(x, y, w, h);
|
||||||
|
int a1 = align&(-16);
|
||||||
|
if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_TOP;
|
||||||
|
if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_BOTTOM;
|
||||||
|
if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_RIGHT;
|
||||||
|
if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_LEFT;
|
||||||
|
fl_draw(str, -(y+h), x, h, w, (Fl_Align)a1, draw_rot90);
|
||||||
|
if (align & FL_ALIGN_CLIP) fl_pop_clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FLWM
|
||||||
|
static void draw_rot180(const char* str, int n, int x, int y) {
|
||||||
|
draw_rotated(str, n, -x, -y, 180);
|
||||||
|
}
|
||||||
|
void draw_rotated180(
|
||||||
|
const char* str, // the (multi-line) string
|
||||||
|
int x, int y, int w, int h, // bounding box
|
||||||
|
Fl_Align align) {
|
||||||
|
int a1 = align&(-16);
|
||||||
|
if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_RIGHT;
|
||||||
|
if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_LEFT;
|
||||||
|
if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_BOTTOM;
|
||||||
|
if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_TOP;
|
||||||
|
fl_draw(str, -(x+w), -(y+h), w, h, (Fl_Align)a1, draw_rot180);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_rot270(const char* str, int n, int x, int y) {
|
||||||
|
draw_rotated(str, n, -y, x, 270);
|
||||||
|
}
|
||||||
|
void draw_rotated270(
|
||||||
|
const char* str, // the (multi-line) string
|
||||||
|
int x, int y, int w, int h, // bounding box
|
||||||
|
Fl_Align align) {
|
||||||
|
int a1 = align&(-16);
|
||||||
|
if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_BOTTOM;
|
||||||
|
if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_TOP;
|
||||||
|
if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_LEFT;
|
||||||
|
if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_RIGHT;
|
||||||
|
fl_draw(str, y, -(x+w), h, w, (Fl_Align)a1, draw_rot270);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
18
Rotated.H
Normal file
18
Rotated.H
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Rotated text drawing with X.
|
||||||
|
|
||||||
|
// Original code:
|
||||||
|
// Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) */
|
||||||
|
//
|
||||||
|
// Modifications for fltk:
|
||||||
|
// Copyright (c) 1997 Bill Spitzak (spitzak@d2.com)
|
||||||
|
|
||||||
|
#ifndef Rotated_H
|
||||||
|
#define Rotated_H
|
||||||
|
|
||||||
|
void draw_rotated(const char* text, int n, int x, int y, int angle);
|
||||||
|
void draw_rotated(const char* text, int x, int y, int angle);
|
||||||
|
void draw_rotated90(const char*, int x, int y, int w, int h, Fl_Align);
|
||||||
|
void draw_rotated270(const char*, int x, int y, int w, int h, Fl_Align);
|
||||||
|
void draw_rotated180(const char*, int x, int y, int w, int h, Fl_Align);
|
||||||
|
|
||||||
|
#endif
|
||||||
60
configure.in
Normal file
60
configure.in
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
dnl# -*- sh -*-
|
||||||
|
dnl# the "configure" script is made from this by running GNU "autoconf"
|
||||||
|
|
||||||
|
AC_INIT(Frame.C)
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_CXX
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
|
||||||
|
AC_PATH_XTRA
|
||||||
|
echo "Ignoring libraries \"$X_PRE_LIBS\" requested by configure."
|
||||||
|
dnl# LIBS="$LIBS$X_LIBS$X_PRE_LIBS"
|
||||||
|
LIBS="$LIBS$X_LIBS"
|
||||||
|
|
||||||
|
MAKEDEPEND="\$(CXX) -M"
|
||||||
|
|
||||||
|
dnl# add warnings and optimization to compiler switches:
|
||||||
|
dnl# do this last so messing with switches does not break tests
|
||||||
|
if test -n "$GXX"; then
|
||||||
|
# GNU C compiler
|
||||||
|
# -Wno-return-type is necessary for Xlib header files on many systems:
|
||||||
|
CFLAGS="$CFLAGS -Wall -Wno-return-type -O2 $X_CFLAGS"
|
||||||
|
CFLAGS_D="$CFLAGS -Wall -Wno-return-type -g -DDEBUG $X_CFLAGS"
|
||||||
|
CXXFLAGS="$CXXFLAGS -Wall -Wno-return-type -O2 $X_CFLAGS"
|
||||||
|
CXXFLAGS_D="$CXXFLAGS -Wall -Wno-return-type -g -DDEBUG $X_CFLAGS"
|
||||||
|
else
|
||||||
|
if test "`(uname) 2>/dev/null`" = IRIX; then
|
||||||
|
if expr "`(uname -r)`" \>= 6.2; then
|
||||||
|
# turn on new "n32" Irix compiler:
|
||||||
|
CXX="CC -n32"
|
||||||
|
CC="cc -n32"
|
||||||
|
LD="ld -n32"
|
||||||
|
# but -M is broken so use old compiler:
|
||||||
|
MAKEDEPEND="CC -M"
|
||||||
|
# -woff 3322 is necessary due to errors in Xlib headers on IRIX
|
||||||
|
CFLAGS="$CFLAGS -fullwarn -O2 $X_CFLAGS"
|
||||||
|
CFLAGS_D="$CFLAGS -fullwarn -gslim -DDEBUG $X_CFLAGS"
|
||||||
|
CXXFLAGS="$CXXFLAGS -fullwarn -woff 3322 -O2 $X_CFLAGS"
|
||||||
|
CXXFLAGS_D="$CXXFLAGS -fullwarn -woff 3322 -gslim -DDEBUG $X_CFLAGS"
|
||||||
|
else
|
||||||
|
# old Irix compiler:
|
||||||
|
CFLAGS="$CFLAGS -O2 $X_CFLAGS"
|
||||||
|
CFLAGS_D="$CFLAGS -g -DDEBUG $X_CFLAGS"
|
||||||
|
CXXFLAGS="$CXXFLAGS +w +pp -O2 $X_CFLAGS"
|
||||||
|
CXXFLAGS_D="$CXXFLAGS +w +pp -g -DDEBUG $X_CFLAGS"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# generic C compiler:
|
||||||
|
CFLAGS="$CFLAGS -O $X_CFLAGS"
|
||||||
|
CFLAGS_D="$CFLAGS -g -DDEBUG $X_CFLAGS"
|
||||||
|
CXXFLAGS="$CXXFLAGS -O $X_CFLAGS"
|
||||||
|
CXXFLAGS_D="$CXXFLAGS -g -DDEBUG $X_CFLAGS"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
AC_SUBST(MAKEDEPEND)
|
||||||
|
AC_SUBST(CFLAGS_D)
|
||||||
|
AC_SUBST(CXXFLAGS_D)
|
||||||
|
dnl# AC_CONFIG_HEADER(config.h:configh.in)
|
||||||
|
AC_OUTPUT(makeinclude)
|
||||||
|
|
||||||
|
dnl# end of configure.in
|
||||||
274
flwm.1
Normal file
274
flwm.1
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
.\"Man page for flwm, by Bill Spitzak.
|
||||||
|
.TH flwm 1 "15 May 1999"
|
||||||
|
.SH NAME
|
||||||
|
\fIflwm\fR - The Fast Light Window Manager
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B flwm
|
||||||
|
[-d[isplay] host:n.n] [-g[eometry] WxH+X+Y]
|
||||||
|
[-fg color] [-bg color] [-bg2 color]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I flwm
|
||||||
|
is a very small and fast X window manager, featuring
|
||||||
|
.I no
|
||||||
|
icons and "sideways" title bars.
|
||||||
|
|
||||||
|
.SH .xinitrc
|
||||||
|
|
||||||
|
Recommened contents of your ~/.xinitrc file:
|
||||||
|
|
||||||
|
.nf
|
||||||
|
#!/bin/sh
|
||||||
|
xsetroot -solid \#006060
|
||||||
|
xrdb .Xresources
|
||||||
|
# <xset, xmodmap, other configuration programs>
|
||||||
|
flwm &
|
||||||
|
WindowManager=$!
|
||||||
|
# <xterm, other automatically-launched programs>
|
||||||
|
wait $WindowManager
|
||||||
|
.fi
|
||||||
|
|
||||||
|
You may instead use the standard of running the window manager last with
|
||||||
|
an "exec flwm" statement at the end of .xinitrc.
|
||||||
|
|
||||||
|
.SH SWITCHES
|
||||||
|
|
||||||
|
.B -d[isplay] host:#.#
|
||||||
|
Sets the display and screen for flwm to manage
|
||||||
|
|
||||||
|
.B -v[isual] #
|
||||||
|
Visual number to use (probably only works for non-color-mapped ones)
|
||||||
|
|
||||||
|
.B -g[eometry] WxH+X+Y
|
||||||
|
Flwm will act as though the screen is only the specified area. It
|
||||||
|
will constrain initial window positions to this area and stop them at
|
||||||
|
the edges when dragging them around. This can be used to surround the
|
||||||
|
screen with fixed "toolbars" that are never covered by windows. These
|
||||||
|
toolbars must be created by a program using override-redirect so that
|
||||||
|
flwm does not try to move them.
|
||||||
|
|
||||||
|
.B -m[aximum] WxH
|
||||||
|
Set the size of windows when the maximize buttons are pushed.
|
||||||
|
Normally this is the size of the screen. This is useful for
|
||||||
|
XFree86 servers that are run with a smaller screen than display
|
||||||
|
memory.
|
||||||
|
|
||||||
|
.B -x
|
||||||
|
The menu will say "Exit" instead of "Logout" and will not ask for
|
||||||
|
confirmation. This is a good idea if you are running flwm in some
|
||||||
|
other way than with exec at the end of .xinitrc, since it won't log
|
||||||
|
you out then.
|
||||||
|
|
||||||
|
.B -fg color, -bg color
|
||||||
|
Set the label color and the color of the window frames and the
|
||||||
|
menu.
|
||||||
|
|
||||||
|
.B -c[ursor] #
|
||||||
|
What cursor to use on the desktop (you will have to experiment to find
|
||||||
|
out what each number means)
|
||||||
|
|
||||||
|
.B -cfg color, -cbg color
|
||||||
|
Colors for the desktop and window resizing cursors
|
||||||
|
|
||||||
|
In addition to these switches there is much customization that can be
|
||||||
|
done by editing the config.h file in the source code and recompiling.
|
||||||
|
GCC is your friend.
|
||||||
|
|
||||||
|
.SH MENU ITEMS
|
||||||
|
|
||||||
|
Flwm can launch programs from it's menu. This is controlled by files
|
||||||
|
in the directory
|
||||||
|
.B ~/.wmx
|
||||||
|
(this was chosen to be compatible with wmx and wm2).
|
||||||
|
|
||||||
|
Each executable file in ~/.wmx is a program to run. Usually these are
|
||||||
|
symbolic links to the real program or very short shell scripts.
|
||||||
|
|
||||||
|
Each subdirectory creates a child menu so you can build a hierarchy
|
||||||
|
(up to 10 deep).
|
||||||
|
|
||||||
|
Cut and paste the following lines you your shell to create some
|
||||||
|
example files:
|
||||||
|
|
||||||
|
.nf
|
||||||
|
mkdir ~/.wmx
|
||||||
|
ln -s /usr/bin/gimp ~/.wmx/"The Gimp"
|
||||||
|
cat << EOF > ~/.wmx/"Terminal"
|
||||||
|
#! /bin/sh
|
||||||
|
/usr/local/bin/rxvt -ut
|
||||||
|
EOF
|
||||||
|
chmod +x !*
|
||||||
|
.fi
|
||||||
|
|
||||||
|
RedHat users can run the program
|
||||||
|
.B flwm_wmconfig
|
||||||
|
to read the /etc/X11/wmconfig directory and produce an initial set of
|
||||||
|
menu items.
|
||||||
|
|
||||||
|
.SH MOUSE USAGE
|
||||||
|
|
||||||
|
.B Left-click
|
||||||
|
on a window border raises window.
|
||||||
|
|
||||||
|
.B Left-drag
|
||||||
|
will move the window when in the title bar, and will resize it in the
|
||||||
|
edges. If the window cannot be resized then it will always move the
|
||||||
|
window. What it will do is indicated by the cursor shape.
|
||||||
|
|
||||||
|
.B Middle-click
|
||||||
|
on a window border lowers it to bottom.
|
||||||
|
|
||||||
|
.B Middle-drag
|
||||||
|
anywhere on window border will move the window.
|
||||||
|
|
||||||
|
When you move a window it will stop at the edges of the screen.
|
||||||
|
Dragging about 150 pixels further will unstick it and let you drag it
|
||||||
|
off the screen.
|
||||||
|
|
||||||
|
.B Right-click
|
||||||
|
on a window border pops up the menu.
|
||||||
|
|
||||||
|
.B Any button
|
||||||
|
on the desktop will pop up the menu.
|
||||||
|
|
||||||
|
.SH BUTTONS
|
||||||
|
|
||||||
|
The empty button "iconizes" the window: it will completely vanish. To
|
||||||
|
get it back use the menu.
|
||||||
|
|
||||||
|
The vertical-bar button "shades" (or "Venetian blinds"?) the window.
|
||||||
|
Click it again to restore the window. You can also resize the shaded
|
||||||
|
window to a new height or "open" it by resizing horizontally.
|
||||||
|
|
||||||
|
The two buttons below it toggle maximum height and/or maximum width.
|
||||||
|
|
||||||
|
The X button at the bottom closes the window.
|
||||||
|
|
||||||
|
.SH MENU
|
||||||
|
|
||||||
|
.B Right-click
|
||||||
|
on window border, or
|
||||||
|
.B any-click
|
||||||
|
on the desktop, or typing
|
||||||
|
.B Alt+Esc
|
||||||
|
or
|
||||||
|
.B Alt+Tab
|
||||||
|
or
|
||||||
|
.B Alt+Shift+Tab
|
||||||
|
will pop up the menu.
|
||||||
|
|
||||||
|
Releasing Alt will pick the current menu item. This makes flwm work
|
||||||
|
very much (exactly?) like the Windows 95 shortcuts.
|
||||||
|
|
||||||
|
Each main window is a menu item. If the window is "iconized" the
|
||||||
|
little picture shows an open rectangle, otherwise it shows a filled
|
||||||
|
rectangle. Picking a menu item deiconizes and raises that window and
|
||||||
|
warps the pointer so it is current.
|
||||||
|
|
||||||
|
.B New desktop
|
||||||
|
asks for a name of a new desktop and makes it current. The desktop
|
||||||
|
will initially be empty (except for sticky items).
|
||||||
|
|
||||||
|
To move windows to the current desktop, pop up the menu and pick
|
||||||
|
windows off of other desktops (if using the keyboard, use left
|
||||||
|
arrow to go to the desktop names, move up and down to the other
|
||||||
|
desktop, and use right arrow to enter that desktop). The window will
|
||||||
|
be moved from the other desktop to the current one.
|
||||||
|
|
||||||
|
To switch to another desktop, pick the title of the desktop (if using
|
||||||
|
the keyboard, use left arrow to go to the desktop names, move up and
|
||||||
|
down to the other desktop).
|
||||||
|
|
||||||
|
If a desktop is empty you can delete it. It's sub menu will show
|
||||||
|
.B delete this desktop.
|
||||||
|
Pick that and the desktop is gone.
|
||||||
|
|
||||||
|
.B Sticky
|
||||||
|
is a special "desktop": windows on it appear on all desktops. To make
|
||||||
|
a window "sticky" switch to the Sticky desktop and pick the window off
|
||||||
|
it's current desktop (thus "moving" it to the Sticky desktop). To
|
||||||
|
"unstick" a window go to another desktop and pick the window off the
|
||||||
|
sticky desktop menu.
|
||||||
|
|
||||||
|
.B New xterm
|
||||||
|
will run a new xterm on the current desktop. Useful if
|
||||||
|
you accidentally close everything. This item does not appear if a
|
||||||
|
~/.wmx directory exists.
|
||||||
|
|
||||||
|
.B Logout
|
||||||
|
will ask for confirmation and if so flwm will exit.
|
||||||
|
|
||||||
|
.B Exit
|
||||||
|
will exit flwm without confirmation. This item will appear if flwm
|
||||||
|
was run with the -x switch.
|
||||||
|
|
||||||
|
.SH HOT KEYS
|
||||||
|
|
||||||
|
These are the defaults, the hot keys may be different depending on how
|
||||||
|
flwm was compiled:
|
||||||
|
|
||||||
|
.B Alt+Escape
|
||||||
|
Pops up the menu with the current window preselected
|
||||||
|
|
||||||
|
.B Alt+Tab
|
||||||
|
Pops up the menu with the next window preselected
|
||||||
|
|
||||||
|
.B Alt+Shift+Tab
|
||||||
|
Pops up the menu with the previous window preselected
|
||||||
|
|
||||||
|
.B Ctrl+Tab
|
||||||
|
Switch to the next desktop.
|
||||||
|
|
||||||
|
.B Ctrl+Shift+Tab
|
||||||
|
Switch to the previous desktop.
|
||||||
|
|
||||||
|
.B Ctrl+Function key
|
||||||
|
Switch to desktop N.
|
||||||
|
|
||||||
|
.B Alt+Up
|
||||||
|
Raise the current window.
|
||||||
|
|
||||||
|
.B Alt+Down
|
||||||
|
Lower the current window.
|
||||||
|
|
||||||
|
.B Alt+Delete
|
||||||
|
Close the current window (same as clicking close box).
|
||||||
|
|
||||||
|
.B Alt+Enter
|
||||||
|
"Iconizes" (hides) the current window.
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
|
||||||
|
It is impossible to move windows smaller than 100 pixels off
|
||||||
|
the screen.
|
||||||
|
|
||||||
|
Only obeys "keep aspect" if the aspect ratio is 1x1.
|
||||||
|
|
||||||
|
.SH ACKNOWLEDGEMENTS
|
||||||
|
|
||||||
|
This program was inspired by and much code copied from the "wm2"
|
||||||
|
window manager by Chris Cannam <cannam@zands.demon.co.uk>
|
||||||
|
|
||||||
|
Thanks to Ron Koerner for the recursive .wmx directory reading code.
|
||||||
|
|
||||||
|
.SH COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 1999 Bill Spitzak
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
USA.
|
||||||
|
|
||||||
|
.SH AUTHORS
|
||||||
|
|
||||||
|
Written by Bill Spitzak spitzak@d2.com
|
||||||
65
flwm_wmconfig
Executable file
65
flwm_wmconfig
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#! /usr/bin/tcl
|
||||||
|
|
||||||
|
# flwm_wmconfig reads the RedHat "/etc/X11/wmconfig" directory (and
|
||||||
|
# the ~/.wmconfig directory) and builds a ~/.wmx directory from it so
|
||||||
|
# that you have a big complex menu in flwm!
|
||||||
|
|
||||||
|
set count 0
|
||||||
|
|
||||||
|
proc read_wmfile {fname} {
|
||||||
|
global count
|
||||||
|
global env
|
||||||
|
if [catch {set f [open $fname]} message] {
|
||||||
|
puts $message
|
||||||
|
} else {
|
||||||
|
set group ""
|
||||||
|
set name ""
|
||||||
|
set exec ""
|
||||||
|
while {[gets $f list]>=0} {
|
||||||
|
if [llength $list]<3 continue
|
||||||
|
set tag [lindex $list 1]
|
||||||
|
set value [lrange $list 2 1000]
|
||||||
|
if [llength $value]==1 {set value [lindex $value 0]}
|
||||||
|
if {$tag=="group"} {set group $value}
|
||||||
|
if {$tag=="name"} {set name $value}
|
||||||
|
if {$tag=="exec"} {set exec $value}
|
||||||
|
}
|
||||||
|
close $f
|
||||||
|
if {$group=="" || $name == "" || $exec == ""} {
|
||||||
|
puts "$fname is missing necessary data"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set dir $env(HOME)/.wmx/$group
|
||||||
|
set exec [string trimright $exec "& "]
|
||||||
|
catch {mkdir [list $dir]}
|
||||||
|
if [llength $exec]==1 {
|
||||||
|
if [catch {set command [exec which $exec]}] {
|
||||||
|
puts "$fname : can't find the program $exec"
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
catch {unlink [list $dir/$name]}
|
||||||
|
link -sym $command $dir/$name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set f [open $dir/$name "w"]
|
||||||
|
puts $f "#! /bin/sh"
|
||||||
|
puts $f "exec $exec"
|
||||||
|
close $f
|
||||||
|
chmod +x [list $dir/$name]
|
||||||
|
}
|
||||||
|
incr count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ![catch {set l [glob /etc/X11/wmconfig/*]}] {
|
||||||
|
foreach f $l {read_wmfile $f}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ![catch {set l [glob $env(HOME)/.wmconfig/*]}] {
|
||||||
|
foreach f $l {read_wmfile $f}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !$count {
|
||||||
|
error "No files found in /etc/X11/wmconfig or ~/.wmconfig"
|
||||||
|
}
|
||||||
|
|
||||||
374
main.C
Normal file
374
main.C
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
// Define "TEST" and it will compile to make a single fake window so
|
||||||
|
// you can test the window controls.
|
||||||
|
//#define TEST 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()) {}
|
||||||
|
void show() {
|
||||||
|
if (!shown()) Fl_X::set_xid(this, RootWindow(fl_display, fl_screen));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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.
|
||||||
|
Window 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;}
|
||||||
|
case KeyRelease: {
|
||||||
|
if (!Fl::grab()) return 0;
|
||||||
|
// see if they released the alt key:
|
||||||
|
unsigned long keysym =
|
||||||
|
XKeycodeToKeysym(fl_display, fl_xevent->xkey.keycode, 0);
|
||||||
|
if (keysym == FL_Alt_L || keysym == FL_Alt_R) {
|
||||||
|
Fl::e_keysym = FL_Enter;
|
||||||
|
return Fl::grab()->handle(FL_KEYBOARD);
|
||||||
|
}
|
||||||
|
return 0;}
|
||||||
|
}
|
||||||
|
} else if (e == FL_SHORTCUT) {
|
||||||
|
#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;
|
||||||
|
static int cursor = FL_CURSOR_ARROW;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialize() {
|
||||||
|
|
||||||
|
Display* d = fl_display;
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
Window w = XCreateSimpleWindow(d, root,
|
||||||
|
100, 100, 200, 300, 10,
|
||||||
|
BlackPixel(fl_display, 0),
|
||||||
|
// WhitePixel(fl_display, 0));
|
||||||
|
0x1234);
|
||||||
|
Frame* frame = new Frame(w);
|
||||||
|
XSelectInput(d, w,
|
||||||
|
ExposureMask | StructureNotifyMask |
|
||||||
|
KeyPressMask | KeyReleaseMask | FocusChangeMask |
|
||||||
|
KeymapStateMask |
|
||||||
|
ButtonPressMask | ButtonReleaseMask |
|
||||||
|
EnterWindowMask | LeaveWindowMask /*|PointerMotionMask*/
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
|
||||||
|
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);
|
||||||
|
color_setup(CURSOR_FG_SLOT, cfg, CURSOR_FG_COLOR<<8);
|
||||||
|
color_setup(CURSOR_BG_SLOT, cbg, CURSOR_BG_COLOR<<8);
|
||||||
|
Root->cursor((Fl_Cursor)cursor, CURSOR_FG_SLOT, CURSOR_BG_SLOT);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
Window 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;
|
||||||
|
Window 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);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
} else if (*s == 'c') {
|
||||||
|
cursor = atoi(v);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
program_name = 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"
|
||||||
|
);
|
||||||
|
Root = new Fl_Root();
|
||||||
|
Root->show(argc,argv); // fools fltk into using -geometry to set the size
|
||||||
|
XSetErrorHandler(xerror_handler);
|
||||||
|
initialize();
|
||||||
|
return Fl::run();
|
||||||
|
}
|
||||||
26
makeinclude.in
Normal file
26
makeinclude.in
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# @configure_input@
|
||||||
|
|
||||||
|
prefix =@prefix@
|
||||||
|
exec_prefix =@exec_prefix@
|
||||||
|
bindir =@bindir@
|
||||||
|
mandir =@mandir@
|
||||||
|
includedir =@includedir@
|
||||||
|
libdir =@libdir@
|
||||||
|
srcdir =@srcdir@
|
||||||
|
VPATH =@srcdir@
|
||||||
|
|
||||||
|
# compiler names:
|
||||||
|
CXX =@CXX@
|
||||||
|
CC =@CC@
|
||||||
|
MAKEDEPEND =@MAKEDEPEND@
|
||||||
|
|
||||||
|
# flags for C++ compiler:
|
||||||
|
CFLAGS =@CFLAGS@
|
||||||
|
CFLAGS_D =@CFLAGS_D@
|
||||||
|
CXXFLAGS =@CXXFLAGS@
|
||||||
|
CXXFLAGS_D =@CXXFLAGS_D@
|
||||||
|
|
||||||
|
# libraries to link with:
|
||||||
|
LDLIBS =@LIBS@ -lX11 -lXext @X_EXTRA_LIBS@ -lm
|
||||||
|
|
||||||
|
INSTALL =@INSTALL@
|
||||||
118
rotated_test.C
Normal file
118
rotated_test.C
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Test the xvertext routines for rotated text
|
||||||
|
|
||||||
|
#include <FL/Fl.H>
|
||||||
|
#include <FL/Fl_Double_Window.H>
|
||||||
|
#include <FL/Fl_Box.H>
|
||||||
|
#include <FL/Fl_Hor_Value_Slider.H>
|
||||||
|
#include <FL/Fl_Toggle_Button.H>
|
||||||
|
#include <FL/Fl_Input.H>
|
||||||
|
#include <FL/Fl_Choice.H>
|
||||||
|
#include <FL/fl_draw.H>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "Rotated.H"
|
||||||
|
|
||||||
|
class RotText : public Fl_Box {
|
||||||
|
void draw();
|
||||||
|
public:
|
||||||
|
RotText(int X, int Y, int W, int H, const char* L = 0) :
|
||||||
|
Fl_Box(X,Y,W,H,L) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void RotText::draw() {
|
||||||
|
draw_box();
|
||||||
|
fl_color(FL_BLACK);
|
||||||
|
fl_font(labelfont(), labelsize());
|
||||||
|
draw_rotated90(label(), x(), y(), w(), h(), align());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Fl_Toggle_Button *leftb,*rightb,*topb,*bottomb,*insideb,*clipb,*wrapb;
|
||||||
|
RotText *text;
|
||||||
|
Fl_Input *input;
|
||||||
|
Fl_Hor_Value_Slider *fonts;
|
||||||
|
Fl_Hor_Value_Slider *sizes;
|
||||||
|
Fl_Double_Window *window;
|
||||||
|
|
||||||
|
void button_cb(Fl_Widget *,void *) {
|
||||||
|
int i = 0;
|
||||||
|
if (leftb->value()) i |= FL_ALIGN_LEFT;
|
||||||
|
if (rightb->value()) i |= FL_ALIGN_RIGHT;
|
||||||
|
if (topb->value()) i |= FL_ALIGN_TOP;
|
||||||
|
if (bottomb->value()) i |= FL_ALIGN_BOTTOM;
|
||||||
|
if (insideb->value()) i |= FL_ALIGN_INSIDE;
|
||||||
|
if (clipb->value()) i |= FL_ALIGN_CLIP;
|
||||||
|
if (wrapb->value()) i |= FL_ALIGN_WRAP;
|
||||||
|
text->align(i);
|
||||||
|
window->redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void font_cb(Fl_Widget *,void *) {
|
||||||
|
text->labelfont(int(fonts->value()));
|
||||||
|
window->redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void size_cb(Fl_Widget *,void *) {
|
||||||
|
text->labelsize(int(sizes->value()));
|
||||||
|
window->redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void input_cb(Fl_Widget *,void *) {
|
||||||
|
text->label(input->value());
|
||||||
|
window->redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
window = new Fl_Double_Window(400,400);
|
||||||
|
|
||||||
|
input = new Fl_Input(50,0,350,25);
|
||||||
|
input->static_value("The quick brown fox jumped over the lazy dog.");
|
||||||
|
input->when(FL_WHEN_CHANGED);
|
||||||
|
input->callback(input_cb);
|
||||||
|
|
||||||
|
sizes= new Fl_Hor_Value_Slider(50,25,350,25,"Size:");
|
||||||
|
sizes->align(FL_ALIGN_LEFT);
|
||||||
|
sizes->bounds(1,64);
|
||||||
|
sizes->step(1);
|
||||||
|
sizes->value(14);
|
||||||
|
sizes->callback(size_cb);
|
||||||
|
|
||||||
|
fonts=new Fl_Hor_Value_Slider(50,50,350,25,"Font:");
|
||||||
|
fonts->align(FL_ALIGN_LEFT);
|
||||||
|
fonts->bounds(0,15);
|
||||||
|
fonts->step(1);
|
||||||
|
fonts->value(0);
|
||||||
|
fonts->callback(font_cb);
|
||||||
|
|
||||||
|
Fl_Group *g = new Fl_Group(0,0,0,0);
|
||||||
|
leftb = new Fl_Toggle_Button(50,75,50,25,"left");
|
||||||
|
leftb->callback(button_cb);
|
||||||
|
rightb = new Fl_Toggle_Button(100,75,50,25,"right");
|
||||||
|
rightb->callback(button_cb);
|
||||||
|
topb = new Fl_Toggle_Button(150,75,50,25,"top");
|
||||||
|
topb->callback(button_cb);
|
||||||
|
bottomb = new Fl_Toggle_Button(200,75,50,25,"bottom");
|
||||||
|
bottomb->callback(button_cb);
|
||||||
|
insideb = new Fl_Toggle_Button(250,75,50,25,"inside");
|
||||||
|
insideb->callback(button_cb);
|
||||||
|
wrapb = new Fl_Toggle_Button(300,75,50,25,"wrap");
|
||||||
|
wrapb->callback(button_cb);
|
||||||
|
clipb = new Fl_Toggle_Button(350,75,50,25,"clip");
|
||||||
|
clipb->callback(button_cb);
|
||||||
|
g->resizable(insideb);
|
||||||
|
g->forms_end();
|
||||||
|
|
||||||
|
text= new RotText(100,225,200,100,input->value());
|
||||||
|
text->box(FL_FRAME_BOX);
|
||||||
|
text->align(FL_ALIGN_CENTER);
|
||||||
|
window->resizable(text);
|
||||||
|
window->forms_end();
|
||||||
|
window->show(argc,argv);
|
||||||
|
return Fl::run();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// End of "$Id$".
|
||||||
|
//
|
||||||
Reference in New Issue
Block a user