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