mirror of
https://github.com/0intro/wmii
synced 2024-11-25 07:09:38 +03:00
Add wimenu
This commit is contained in:
parent
7cbe734db9
commit
2560525d20
26
cmd/menu/Makefile
Normal file
26
cmd/menu/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
ROOT= ../..
|
||||
include $(ROOT)/mk/hdr.mk
|
||||
include $(ROOT)/mk/wmii.mk
|
||||
|
||||
main.c: $(ROOT)/mk/wmii.mk
|
||||
|
||||
TARG = menu
|
||||
HFILES= dat.h fns.h
|
||||
|
||||
LIB = $(LIBIXP)
|
||||
LDFLAGS += -lm $(LIBX11) -lXext -lXrandr -lXinerama \
|
||||
-lregexp9 -lbio -lfmt -lutf
|
||||
CFLAGS += $(INCX11) -DVERSION=\"$(VERSION)\" \
|
||||
-DIXP_NEEDAPI=86
|
||||
OBJ = main \
|
||||
event \
|
||||
menu \
|
||||
../wmii/geom \
|
||||
../wmii/map \
|
||||
../wmii/printevent \
|
||||
../wmii/x11 \
|
||||
../wmii/xext \
|
||||
../util
|
||||
|
||||
include $(ROOT)/mk/one.mk
|
||||
|
63
cmd/menu/dat.h
Normal file
63
cmd/menu/dat.h
Normal file
@ -0,0 +1,63 @@
|
||||
#define IXP_P9_STRUCTS
|
||||
#define IXP_NO_P9_
|
||||
#include <fmt.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <util.h>
|
||||
#include <ixp.h>
|
||||
#include <x11.h>
|
||||
|
||||
#define BLOCK(x) do { x; }while(0)
|
||||
|
||||
#ifndef EXTERN
|
||||
# define EXTERN extern
|
||||
#endif
|
||||
|
||||
typedef struct Item Item;
|
||||
|
||||
struct Item {
|
||||
char* string;
|
||||
char* retstring;
|
||||
Item* next_link;
|
||||
Item* next;
|
||||
Item* prev;
|
||||
int len;
|
||||
int width;
|
||||
};
|
||||
|
||||
EXTERN long xtime;
|
||||
EXTERN Image* ibuf;
|
||||
EXTERN Font* font;
|
||||
EXTERN CTuple cnorm, csel;
|
||||
EXTERN bool ontop;
|
||||
|
||||
EXTERN Cursor cursor[1];
|
||||
EXTERN Visual* render_visual;
|
||||
|
||||
EXTERN IxpServer srv;
|
||||
|
||||
EXTERN Item* items;
|
||||
EXTERN Item* matchfirst;
|
||||
EXTERN Item* matchstart;
|
||||
EXTERN Item* matchend;
|
||||
EXTERN Item* matchidx;
|
||||
|
||||
EXTERN Item* histidx;
|
||||
|
||||
EXTERN char filter[1024];
|
||||
|
||||
EXTERN int maxwidth;
|
||||
EXTERN int result;
|
||||
|
||||
EXTERN char buffer[8092];
|
||||
EXTERN char* _buffer;
|
||||
|
||||
static char* const _buf_end = buffer + sizeof buffer;
|
||||
|
||||
#define bufclear() \
|
||||
BLOCK( _buffer = buffer; _buffer[0] = '\0' )
|
||||
#define bufprint(...) \
|
||||
_buffer = seprint(_buffer, _buf_end, __VA_ARGS__)
|
||||
|
334
cmd/menu/event.c
Normal file
334
cmd/menu/event.c
Normal file
@ -0,0 +1,334 @@
|
||||
/* Copyright ©2006-2008 Kris Maglione <fbsdaemon@gmail.com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
|
||||
typedef void (*EvHandler)(XEvent*);
|
||||
static EvHandler handler[LASTEvent];
|
||||
|
||||
void
|
||||
dispatch_event(XEvent *e) {
|
||||
if(e->type < nelem(handler)) {
|
||||
if(handler[e->type])
|
||||
handler[e->type](e);
|
||||
}else
|
||||
xext_event(e);
|
||||
}
|
||||
|
||||
#define handle(w, fn, ev) \
|
||||
BLOCK(if((w)->handler->fn) (w)->handler->fn((w), ev))
|
||||
|
||||
static int
|
||||
findtime(Display *d, XEvent *e, XPointer v) {
|
||||
Window *w;
|
||||
|
||||
w = (Window*)v;
|
||||
if(e->type == PropertyNotify && e->xproperty.window == w->w) {
|
||||
xtime = e->xproperty.time;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
xtime_kludge(void) {
|
||||
/* Round trip. */
|
||||
static Window *w;
|
||||
WinAttr wa;
|
||||
XEvent e;
|
||||
long l;
|
||||
|
||||
if(w == nil) {
|
||||
w = createwindow(&scr.root, Rect(0, 0, 1, 1), 0, InputOnly, &wa, 0);
|
||||
selectinput(w, PropertyChangeMask);
|
||||
}
|
||||
changeprop_long(w, "ATOM", "ATOM", &l, 0);
|
||||
sync();
|
||||
XIfEvent(display, &e, findtime, (void*)w);
|
||||
}
|
||||
|
||||
uint
|
||||
flushevents(long event_mask, bool dispatch) {
|
||||
XEvent ev;
|
||||
uint n = 0;
|
||||
|
||||
while(XCheckMaskEvent(display, event_mask, &ev)) {
|
||||
if(dispatch)
|
||||
dispatch_event(&ev);
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
findenter(Display *d, XEvent *e, XPointer v) {
|
||||
long *l;
|
||||
|
||||
USED(d);
|
||||
l = (long*)v;
|
||||
if(*l)
|
||||
return false;
|
||||
if(e->type == EnterNotify)
|
||||
return true;
|
||||
if(e->type == MotionNotify)
|
||||
(*l)++;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This isn't perfect. If there were motion events in the queue
|
||||
* before this was called, then it flushes nothing. If we don't
|
||||
* check for them, we might lose a legitamate enter event.
|
||||
*/
|
||||
uint
|
||||
flushenterevents(void) {
|
||||
XEvent e;
|
||||
long l;
|
||||
int n;
|
||||
|
||||
l = 0;
|
||||
n = 0;
|
||||
while(XCheckIfEvent(display, &e, findenter, (void*)&l))
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
buttonrelease(XButtonPressedEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, bup, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
buttonpress(XButtonPressedEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, bdown, ev);
|
||||
else
|
||||
XAllowEvents(display, ReplayPointer, ev->time);
|
||||
}
|
||||
|
||||
static void
|
||||
configurerequest(XConfigureRequestEvent *ev) {
|
||||
XWindowChanges wc;
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, configreq, ev);
|
||||
else{
|
||||
wc.x = ev->x;
|
||||
wc.y = ev->y;
|
||||
wc.width = ev->width;
|
||||
wc.height = ev->height;
|
||||
wc.border_width = ev->border_width;
|
||||
wc.sibling = ev->above;
|
||||
wc.stack_mode = ev->detail;
|
||||
XConfigureWindow(display, ev->window, ev->value_mask, &wc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
configurenotify(XConfigureEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
USED(ev);
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, config, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
clientmessage(XClientMessageEvent *ev) {
|
||||
|
||||
USED(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
destroynotify(XDestroyWindowEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, destroy, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
enternotify(XCrossingEvent *ev) {
|
||||
Window *w;
|
||||
static int sel_screen;
|
||||
|
||||
xtime = ev->time;
|
||||
if(ev->mode != NotifyNormal)
|
||||
return;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, enter, ev);
|
||||
else if(ev->window == scr.root.w)
|
||||
sel_screen = true;
|
||||
}
|
||||
|
||||
static void
|
||||
leavenotify(XCrossingEvent *ev) {
|
||||
|
||||
xtime = ev->time;
|
||||
#if 0
|
||||
if((ev->window == scr.root.w) && !ev->same_screen)
|
||||
sel_screen = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
focusin(XFocusChangeEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
/* Yes, we're focusing in on nothing, here. */
|
||||
if(ev->detail == NotifyDetailNone) {
|
||||
/* FIXME: Do something. */
|
||||
return;
|
||||
}
|
||||
|
||||
if(!((ev->detail == NotifyNonlinear)
|
||||
||(ev->detail == NotifyNonlinearVirtual)
|
||||
||(ev->detail == NotifyVirtual)
|
||||
||(ev->detail == NotifyInferior)
|
||||
||(ev->detail == NotifyAncestor)))
|
||||
return;
|
||||
if((ev->mode == NotifyWhileGrabbed)) /* && (screen->hasgrab != &c_root)) */
|
||||
return;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, focusin, ev);
|
||||
#if 0
|
||||
else if(ev->mode == NotifyGrab) {
|
||||
if(ev->window == scr.root.w)
|
||||
screen->hasgrab = &c_root;
|
||||
/* Some unmanaged window has grabbed focus */
|
||||
else if((c = screen->focus)) {
|
||||
print_focus("focusin", &c_magic, "<magic>");
|
||||
screen->focus = &c_magic;
|
||||
if(c->sel)
|
||||
frame_draw(c->sel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
focusout(XFocusChangeEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if(!((ev->detail == NotifyNonlinear)
|
||||
||(ev->detail == NotifyNonlinearVirtual)
|
||||
||(ev->detail == NotifyVirtual)
|
||||
||(ev->detail == NotifyInferior)
|
||||
||(ev->detail == NotifyAncestor)))
|
||||
return;
|
||||
#if 0
|
||||
if(ev->mode == NotifyUngrab)
|
||||
screen->hasgrab = nil;
|
||||
#endif
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, focusout, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
expose(XExposeEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if(ev->count == 0) {
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, expose, ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
keypress(XKeyEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
xtime = ev->time;
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, kdown, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
mappingnotify(XMappingEvent *ev) {
|
||||
|
||||
/* Why do you need me to tell you this? */
|
||||
XRefreshKeyboardMapping(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
maprequest(XMapRequestEvent *ev) {
|
||||
|
||||
USED(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
motionnotify(XMotionEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
xtime = ev->time;
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, motion, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
propertynotify(XPropertyEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
xtime = ev->time;
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, property, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
mapnotify(XMapEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)))
|
||||
handle(w, map, ev);
|
||||
}
|
||||
|
||||
static void
|
||||
unmapnotify(XUnmapEvent *ev) {
|
||||
Window *w;
|
||||
|
||||
if((w = findwin(ev->window)) && (ev->event == w->parent->w)) {
|
||||
w->mapped = false;
|
||||
if(ev->send_event || w->unmapped-- == 0)
|
||||
handle(w, unmap, ev);
|
||||
}
|
||||
}
|
||||
|
||||
static EvHandler handler[LASTEvent] = {
|
||||
[ButtonPress] = (EvHandler)buttonpress,
|
||||
[ButtonRelease] = (EvHandler)buttonrelease,
|
||||
[ConfigureRequest] = (EvHandler)configurerequest,
|
||||
[ConfigureNotify] = (EvHandler)configurenotify,
|
||||
[ClientMessage] = (EvHandler)clientmessage,
|
||||
[DestroyNotify] = (EvHandler)destroynotify,
|
||||
[EnterNotify] = (EvHandler)enternotify,
|
||||
[Expose] = (EvHandler)expose,
|
||||
[FocusIn] = (EvHandler)focusin,
|
||||
[FocusOut] = (EvHandler)focusout,
|
||||
[KeyPress] = (EvHandler)keypress,
|
||||
[LeaveNotify] = (EvHandler)leavenotify,
|
||||
[MapNotify] = (EvHandler)mapnotify,
|
||||
[MapRequest] = (EvHandler)maprequest,
|
||||
[MappingNotify] = (EvHandler)mappingnotify,
|
||||
[MotionNotify] = (EvHandler)motionnotify,
|
||||
[PropertyNotify] = (EvHandler)propertynotify,
|
||||
[UnmapNotify] = (EvHandler)unmapnotify,
|
||||
};
|
||||
|
||||
void
|
||||
check_x_event(IxpConn *c) {
|
||||
XEvent ev;
|
||||
|
||||
USED(c);
|
||||
while(XCheckMaskEvent(display, ~0, &ev))
|
||||
dispatch_event(&ev);
|
||||
}
|
||||
|
29
cmd/menu/fns.h
Normal file
29
cmd/menu/fns.h
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
void check_x_event(IxpConn*);
|
||||
void debug(int, const char*, ...);
|
||||
void dispatch_event(XEvent*);
|
||||
Item* filter_list(Item*, char*);
|
||||
uint flushenterevents(void);
|
||||
uint flushevents(long, bool);
|
||||
void init_screens(void);
|
||||
void menu_init(void);
|
||||
void menu_show(void);
|
||||
void xtime_kludge(void);
|
||||
void update_filter(void);
|
||||
|
||||
/* geom.c */
|
||||
Align get_sticky(Rectangle src, Rectangle dst);
|
||||
Cursor quad_cursor(Align);
|
||||
Align quadrant(Rectangle, Point);
|
||||
bool rect_contains_p(Rectangle, Rectangle);
|
||||
bool rect_haspoint_p(Point, Rectangle);
|
||||
bool rect_intersect_p(Rectangle, Rectangle);
|
||||
Rectangle rect_intersection(Rectangle, Rectangle);
|
||||
|
||||
/* xext.c */
|
||||
void randr_event(XEvent*);
|
||||
bool render_argb_p(Visual*);
|
||||
void xext_event(XEvent*);
|
||||
void xext_init(void);
|
||||
Rectangle* xinerama_screens(int*);
|
||||
|
292
cmd/menu/main.c
Normal file
292
cmd/menu/main.c
Normal file
@ -0,0 +1,292 @@
|
||||
/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
|
||||
* Copyright ©2006-2008 Kris Maglione <fbsdaemon@gmail.com>
|
||||
* See LICENSE file for license details.
|
||||
*/
|
||||
#define IXP_NO_P9_
|
||||
#define IXP_P9_STRUCTS
|
||||
#define EXTERN
|
||||
#include "dat.h"
|
||||
#include <X11/Xproto.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <bio.h>
|
||||
#include "fns.h"
|
||||
#define link _link
|
||||
|
||||
static const char version[] = "wimenu-"VERSION", ©2008 Kris Maglione\n";
|
||||
static IxpClient* client;
|
||||
static IxpCFid* ctlfid;
|
||||
static Biobuf* inbuf;
|
||||
static char ctl[1024];
|
||||
static char* ectl;
|
||||
|
||||
static char* (*find)(const char*, const char*);
|
||||
|
||||
static void
|
||||
usage(void) {
|
||||
fatal("usage: wimenu ...\n");
|
||||
}
|
||||
|
||||
static int
|
||||
errfmt(Fmt *f) {
|
||||
return fmtstrcpy(f, ixp_errbuf());
|
||||
}
|
||||
|
||||
/* Stubs. */
|
||||
void
|
||||
debug(int flag, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
USED(flag);
|
||||
va_start(ap, fmt);
|
||||
vfprint(2, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void dprint(long, char*, ...);
|
||||
void dprint(long mask, char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
USED(mask);
|
||||
va_start(ap, fmt);
|
||||
vfprint(2, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static char*
|
||||
readctl(char *key) {
|
||||
char *s, *p;
|
||||
int nkey, n;
|
||||
|
||||
nkey = strlen(key);
|
||||
p = ctl - 1;
|
||||
do {
|
||||
p++;
|
||||
if(!strncmp(p, key, nkey)) {
|
||||
p += nkey;
|
||||
s = strchr(p, '\n');
|
||||
n = (s ? s : ectl) - p;
|
||||
s = emalloc(n + 1);
|
||||
s[n] = '\0';
|
||||
return strncpy(s, p, n);
|
||||
}
|
||||
} while((p = strchr(p, '\n')));
|
||||
return "";
|
||||
}
|
||||
|
||||
static void
|
||||
splice(Item *i) {
|
||||
i->next->prev = i->prev;
|
||||
i->prev->next = i->next;
|
||||
}
|
||||
static void
|
||||
link(Item *i, Item *j) {
|
||||
i->next = j;
|
||||
j->prev = i;
|
||||
}
|
||||
|
||||
static Item*
|
||||
populate_list(Biobuf *buf, bool hist) {
|
||||
Item ret;
|
||||
Item *i;
|
||||
char *p;
|
||||
|
||||
i = &ret;
|
||||
while((p = Brdstr(buf, '\n', true))) {
|
||||
link(i, emallocz(sizeof *i));
|
||||
i->next_link = i->next;
|
||||
i = i->next;
|
||||
i->string = p;
|
||||
i->retstring = p;
|
||||
if(!hist) {
|
||||
i->len = strlen(p);
|
||||
i->width = textwidth_l(font, p, i->len);
|
||||
if(i->width > maxwidth)
|
||||
maxwidth = i->width;
|
||||
}
|
||||
}
|
||||
|
||||
link(i, &ret);
|
||||
splice(&ret);
|
||||
return ret.next != &ret ? ret.next : nil;
|
||||
}
|
||||
|
||||
Item*
|
||||
filter_list(Item *i, char *filter) {
|
||||
static Item exact;
|
||||
Item start, substr;
|
||||
Item *exactp, *startp, *substrp;
|
||||
Item **ip;
|
||||
char *p;
|
||||
int len;
|
||||
|
||||
len = strlen(filter);
|
||||
exactp = &exact;
|
||||
startp = &start;
|
||||
substrp = &substr;
|
||||
for(; i; i=i->next_link)
|
||||
if((p = find(i->string, filter))) {
|
||||
ip = &substrp;
|
||||
if(p == i->string)
|
||||
if(strlen(p) == len)
|
||||
ip = &exactp;
|
||||
else
|
||||
ip = &startp;
|
||||
link(*ip, i);
|
||||
*ip = i;
|
||||
}
|
||||
|
||||
link(substrp, &exact);
|
||||
link(startp, &substr);
|
||||
link(exactp, &start);
|
||||
splice(&substr);
|
||||
splice(&start);
|
||||
splice(&exact);
|
||||
return exact.next;
|
||||
}
|
||||
|
||||
void
|
||||
update_filter(void) {
|
||||
/* TODO: Perhaps filter only previous matches unless filter
|
||||
* has been truncated.
|
||||
*/
|
||||
matchfirst = matchstart = matchidx = filter_list(items, filter);
|
||||
}
|
||||
|
||||
/*
|
||||
* There's no way to check accesses to destroyed windows, thus
|
||||
* those cases are ignored (especially on UnmapNotifies).
|
||||
* Other types of errors call Xlib's default error handler, which
|
||||
* calls exit().
|
||||
*/
|
||||
ErrorCode ignored_xerrors[] = {
|
||||
{ 0, BadWindow },
|
||||
{ X_SetInputFocus, BadMatch },
|
||||
{ X_PolyText8, BadDrawable },
|
||||
{ X_PolyFillRectangle, BadDrawable },
|
||||
{ X_PolySegment, BadDrawable },
|
||||
{ X_ConfigureWindow, BadMatch },
|
||||
{ X_GrabKey, BadAccess },
|
||||
{ X_GetAtomName, BadAtom },
|
||||
};
|
||||
|
||||
static void
|
||||
end(IxpConn *c) {
|
||||
|
||||
USED(c);
|
||||
srv.running = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
preselect(IxpServer *s) {
|
||||
|
||||
USED(s);
|
||||
check_x_event(nil);
|
||||
}
|
||||
|
||||
void
|
||||
init_screens(void) {
|
||||
Rectangle *rects;
|
||||
Point p;
|
||||
int i, n;
|
||||
|
||||
/* Pick the screen with the pointer, for now. Later,
|
||||
* try for the screen with the focused window first.
|
||||
*/
|
||||
p = querypointer(&scr.root);
|
||||
rects = xinerama_screens(&n);
|
||||
for(i=0; i < n; i++)
|
||||
if(rect_haspoint_p(p, rects[i]))
|
||||
break;
|
||||
if(i == n)
|
||||
i = 0;
|
||||
/* Probably not the best route. */
|
||||
scr.rect = rects[i];
|
||||
menu_show();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
Item hist = { .string = "", };
|
||||
Item *item;
|
||||
char *address;
|
||||
char *histfile;
|
||||
int i;
|
||||
|
||||
quotefmtinstall();
|
||||
fmtinstall('r', errfmt);
|
||||
address = getenv("WMII_ADDRESS");
|
||||
histfile = nil;
|
||||
find = strstr;
|
||||
|
||||
ARGBEGIN{
|
||||
case 'a':
|
||||
address = EARGF(usage());
|
||||
break;
|
||||
case 'h':
|
||||
histfile = EARGF(usage());
|
||||
break;
|
||||
case 'i':
|
||||
find = strcasestr;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}ARGEND;
|
||||
|
||||
if(argc)
|
||||
usage();
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
initdisplay();
|
||||
|
||||
if(address && *address)
|
||||
client = ixp_mount(address);
|
||||
else
|
||||
client = ixp_nsmount("wmii");
|
||||
if(client == nil)
|
||||
fatal("can't mount: %r\n");
|
||||
|
||||
ctlfid = ixp_open(client, "ctl", OREAD);
|
||||
i = ixp_read(ctlfid, ctl, 1023);
|
||||
ectl = ctl + i;
|
||||
|
||||
srv.preselect = preselect;
|
||||
ixp_listen(&srv, ConnectionNumber(display), nil, check_x_event, end);
|
||||
|
||||
loadcolor(&cnorm, readctl("normcolors "));
|
||||
loadcolor(&csel, readctl("focuscolors "));
|
||||
font = loadfont(readctl("font "));
|
||||
if(!font)
|
||||
fatal("Can't load font %q", readctl("font "));
|
||||
|
||||
inbuf = Bfdopen(0, OREAD);
|
||||
items = populate_list(inbuf, false);
|
||||
update_filter();
|
||||
|
||||
Bterm(inbuf);
|
||||
histidx = &hist;
|
||||
if(histfile) {
|
||||
inbuf = Bopen(histfile, OREAD);
|
||||
if(!inbuf)
|
||||
fatal("Can't open histfile %q: %r", histfile);
|
||||
item = populate_list(inbuf, true);
|
||||
if(item) {
|
||||
link(item->prev, &hist);
|
||||
item->prev = nil;
|
||||
}
|
||||
Bterm(inbuf);
|
||||
}
|
||||
|
||||
xext_init();
|
||||
menu_init();
|
||||
init_screens();
|
||||
|
||||
i = ixp_serverloop(&srv);
|
||||
if(i)
|
||||
fprint(2, "%s: error: %r\n", argv0);
|
||||
XCloseDisplay(display);
|
||||
return result;
|
||||
}
|
||||
|
354
cmd/menu/menu.c
Normal file
354
cmd/menu/menu.c
Normal file
@ -0,0 +1,354 @@
|
||||
#include "dat.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include "fns.h"
|
||||
|
||||
static Window* barwin;
|
||||
static Handlers handlers;
|
||||
|
||||
static int ltwidth;
|
||||
static int numlock;
|
||||
|
||||
static void menu_draw(void);
|
||||
|
||||
enum {
|
||||
ACCEPT,
|
||||
REJECT,
|
||||
HIST_NEXT,
|
||||
HIST_PREV,
|
||||
KILL_CHAR,
|
||||
KILL_WORD,
|
||||
KILL_LINE,
|
||||
CMPL_NEXT,
|
||||
CMPL_PREV,
|
||||
CMPL_FIRST,
|
||||
CMPL_LAST,
|
||||
CMPL_NEXT_PAGE,
|
||||
CMPL_PREV_PAGE,
|
||||
};
|
||||
|
||||
void
|
||||
menu_init(void) {
|
||||
WinAttr wa;
|
||||
|
||||
ltwidth = textwidth(font, "<");
|
||||
|
||||
wa.override_redirect = 1;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.event_mask = ExposureMask | KeyPressMask;
|
||||
barwin = createwindow(&scr.root, Rect(0, 0, 1, 1), scr.depth, InputOutput,
|
||||
&wa, CWOverrideRedirect
|
||||
| CWBackPixmap
|
||||
| CWEventMask);
|
||||
sethandler(barwin, &handlers);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_unmap(long id, void *p) {
|
||||
|
||||
USED(id, p);
|
||||
unmapwin(barwin);
|
||||
XFlush(display);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_cmd(int op) {
|
||||
bool res;
|
||||
int i;
|
||||
|
||||
i = strlen(filter);
|
||||
switch(op) {
|
||||
case ACCEPT:
|
||||
srv.running = false;
|
||||
if(matchidx)
|
||||
print("%s", matchidx->retstring);
|
||||
else
|
||||
result = 1;
|
||||
break;
|
||||
case REJECT:
|
||||
srv.running = false;
|
||||
result = 1;
|
||||
break;
|
||||
case HIST_NEXT:
|
||||
if(histidx->next) {
|
||||
histidx = histidx->next;
|
||||
strncpy(filter, histidx->string, sizeof filter);
|
||||
}
|
||||
break;
|
||||
case HIST_PREV:
|
||||
if(histidx->prev) {
|
||||
histidx = histidx->prev;
|
||||
strncpy(filter, histidx->string, sizeof filter);
|
||||
}
|
||||
break;
|
||||
case KILL_CHAR:
|
||||
if(i > 0)
|
||||
filter[i-1] = '\0';
|
||||
break;
|
||||
case KILL_WORD:
|
||||
if(i == 0)
|
||||
break;
|
||||
for(i--; i >= 0 && isspace(filter[i]); i--)
|
||||
filter[i] = '\0';
|
||||
if(i >= 0)
|
||||
res = !isalnum(filter[i]);
|
||||
for(; i >= 0 && !isalnum(filter[i]) == res && !isspace(filter[i]); i--)
|
||||
filter[i] = '\0';
|
||||
break;
|
||||
case KILL_LINE:
|
||||
/* TODO: Add a caret. */
|
||||
filter[0] = '\0';
|
||||
break;
|
||||
case CMPL_NEXT:
|
||||
matchidx = matchidx->next;
|
||||
break;
|
||||
case CMPL_PREV:
|
||||
matchidx = matchidx->prev;
|
||||
break;
|
||||
case CMPL_FIRST:
|
||||
matchidx = matchfirst;
|
||||
break;
|
||||
case CMPL_LAST:
|
||||
matchidx = matchfirst->prev;
|
||||
break;
|
||||
case CMPL_NEXT_PAGE:
|
||||
matchstart = matchend->next;
|
||||
break;
|
||||
case CMPL_PREV_PAGE:
|
||||
matchend = matchstart->prev;
|
||||
break;
|
||||
}
|
||||
update_filter();
|
||||
menu_draw();
|
||||
}
|
||||
|
||||
static void
|
||||
menu_draw(void) {
|
||||
Rectangle r, r2;
|
||||
CTuple *c;
|
||||
Item *i;
|
||||
int inputw, itemoff, end, pad;
|
||||
|
||||
r = barwin->r;
|
||||
r = rectsetorigin(r, ZP);
|
||||
r2 = r;
|
||||
|
||||
inputw = min(Dx(r) / 3, maxwidth) + pad;
|
||||
itemoff = inputw + 2 * ltwidth;
|
||||
end = Dx(r) - ltwidth;
|
||||
pad = (font->height & ~1);
|
||||
|
||||
fill(ibuf, r, cnorm.bg);
|
||||
|
||||
for(i=matchstart; i->string; i=i->next) {
|
||||
r2.min.x = itemoff;
|
||||
itemoff = itemoff + i->width + pad;
|
||||
r2.max.x = min(itemoff, end);
|
||||
if(i != matchstart && itemoff > end)
|
||||
break;
|
||||
|
||||
c = (i == matchidx) ? &csel : &cnorm;
|
||||
fill(ibuf, r2, c->bg);
|
||||
drawstring(ibuf, font, r2, Center, i->string, c->fg);
|
||||
matchend = i;
|
||||
if(i->next == matchfirst)
|
||||
break;
|
||||
}
|
||||
|
||||
r2 = r;
|
||||
r2.min.x = inputw;
|
||||
if(matchstart != matchfirst)
|
||||
drawstring(ibuf, font, r2, West, "<", cnorm.fg);
|
||||
if(matchend->next != matchfirst)
|
||||
drawstring(ibuf, font, r2, East, ">", cnorm.fg);
|
||||
r2 = r;
|
||||
r2.max.x = inputw;
|
||||
drawstring(ibuf, font, r2, West, filter, cnorm.fg);
|
||||
|
||||
r2.min.x = textwidth(font, filter) + pad/2 + 1;
|
||||
r2.max.x = r2.min.x + 2;
|
||||
r2.min.y++;
|
||||
r2.max.y--;
|
||||
border(ibuf, r2, 1, cnorm.border);
|
||||
|
||||
border(ibuf, r, 1, cnorm.border);
|
||||
copyimage(barwin, r, ibuf, ZP);
|
||||
}
|
||||
|
||||
void
|
||||
menu_show(void) {
|
||||
Rectangle r;
|
||||
int height, pad;
|
||||
|
||||
USED(menu_unmap);
|
||||
|
||||
pad = (font->height & ~1)/2;
|
||||
height = font->height + 2;
|
||||
|
||||
r = scr.rect;
|
||||
if(ontop)
|
||||
r.max.y = r.min.y + height;
|
||||
else
|
||||
r.min.y = r.max.y - height;
|
||||
reshapewin(barwin, r);
|
||||
|
||||
freeimage(ibuf);
|
||||
ibuf = allocimage(Dx(r), Dy(r), scr.depth);
|
||||
|
||||
mapwin(barwin);
|
||||
raisewin(barwin);
|
||||
menu_draw();
|
||||
setfocus(barwin, RevertToPointerRoot);
|
||||
}
|
||||
|
||||
static void
|
||||
kdown_event(Window *w, XKeyEvent *e) {
|
||||
char buf[32];
|
||||
int num, i;
|
||||
KeySym ksym;
|
||||
|
||||
buf[0] = 0;
|
||||
num = XLookupString(e, buf, sizeof buf, &ksym, 0);
|
||||
if(IsKeypadKey(ksym))
|
||||
if(ksym == XK_KP_Enter)
|
||||
ksym = XK_Return;
|
||||
else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
|
||||
ksym = (ksym - XK_KP_0) + XK_0;
|
||||
|
||||
if(IsFunctionKey(ksym)
|
||||
|| IsKeypadKey(ksym)
|
||||
|| IsMiscFunctionKey(ksym)
|
||||
|| IsPFKey(ksym)
|
||||
|| IsPrivateKeypadKey(ksym))
|
||||
return;
|
||||
|
||||
/* first check if a control mask is omitted */
|
||||
if(e->state & ControlMask) {
|
||||
switch (ksym) {
|
||||
default:
|
||||
return;
|
||||
case XK_bracketleft: /* Esc */
|
||||
menu_cmd(REJECT);
|
||||
return;
|
||||
case XK_j:
|
||||
case XK_J:
|
||||
case XK_m:
|
||||
case XK_M:
|
||||
menu_cmd(ACCEPT);
|
||||
return;
|
||||
case XK_n:
|
||||
case XK_N:
|
||||
menu_cmd(HIST_NEXT);
|
||||
return;
|
||||
case XK_p:
|
||||
case XK_P:
|
||||
menu_cmd(HIST_PREV);
|
||||
return;
|
||||
case XK_i: /* Tab */
|
||||
case XK_I:
|
||||
menu_cmd(CMPL_NEXT);
|
||||
return;
|
||||
case XK_h:
|
||||
case XK_H:
|
||||
menu_cmd(KILL_CHAR);
|
||||
return;
|
||||
case XK_w:
|
||||
case XK_W:
|
||||
menu_cmd(KILL_WORD);
|
||||
return;
|
||||
case XK_u:
|
||||
case XK_U:
|
||||
menu_cmd(KILL_LINE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Alt-<Key> - Vim */
|
||||
if((e->state & ~(numlock | LockMask)) & Mod1Mask) {
|
||||
switch(ksym) {
|
||||
default:
|
||||
return;
|
||||
case XK_h:
|
||||
menu_cmd(CMPL_PREV);
|
||||
return;
|
||||
case XK_l:
|
||||
menu_cmd(CMPL_NEXT);
|
||||
return;
|
||||
case XK_j:
|
||||
menu_cmd(CMPL_NEXT_PAGE);
|
||||
return;
|
||||
case XK_k:
|
||||
menu_cmd(CMPL_PREV_PAGE);
|
||||
return;
|
||||
case XK_g:
|
||||
menu_cmd(CMPL_FIRST);
|
||||
return;
|
||||
case XK_G:
|
||||
menu_cmd(CMPL_LAST);
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch(ksym) {
|
||||
default:
|
||||
if(num && !iscntrl(buf[0])) {
|
||||
i = strlen(filter);
|
||||
if(i < sizeof filter - 1) {
|
||||
filter[i] = buf[0];
|
||||
filter[i+1] = '\0';
|
||||
}
|
||||
update_filter();
|
||||
menu_draw();
|
||||
}
|
||||
break;
|
||||
case XK_Escape:
|
||||
menu_cmd(REJECT);
|
||||
return;
|
||||
case XK_Return:
|
||||
menu_cmd(ACCEPT);
|
||||
return;
|
||||
case XK_BackSpace:
|
||||
menu_cmd(KILL_CHAR);
|
||||
return;
|
||||
case XK_Up:
|
||||
menu_cmd(HIST_PREV);
|
||||
return;
|
||||
case XK_Down:
|
||||
menu_cmd(HIST_NEXT);
|
||||
return;
|
||||
case XK_Home:
|
||||
/* TODO: Caret. */
|
||||
menu_cmd(CMPL_FIRST);
|
||||
return;
|
||||
case XK_End:
|
||||
/* TODO: Caret. */
|
||||
menu_cmd(CMPL_LAST);
|
||||
return;
|
||||
case XK_Left:
|
||||
menu_cmd(CMPL_PREV);
|
||||
return;
|
||||
case XK_Right:
|
||||
menu_cmd(CMPL_NEXT);
|
||||
return;
|
||||
case XK_Next:
|
||||
menu_cmd(CMPL_NEXT_PAGE);
|
||||
return;
|
||||
case XK_Prior:
|
||||
menu_cmd(CMPL_PREV_PAGE);
|
||||
return;
|
||||
case XK_Tab:
|
||||
menu_cmd(CMPL_NEXT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
expose_event(Window *w, XExposeEvent *e) {
|
||||
|
||||
USED(w);
|
||||
menu_draw();
|
||||
}
|
||||
|
||||
static Handlers handlers = {
|
||||
.expose = expose_event,
|
||||
.kdown = kdown_event,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user