ryudo/menu.c

539 lines
11 KiB
C
Raw Permalink Normal View History

2019-12-02 18:23:00 +00:00
/*
* Pop-up menus.
*/
/* Copyright (c) 1994-1996 David Hogan, see README for licence details */
2021-02-26 18:01:22 +00:00
#define _SVID_SOURCE 1 /* putenv in glibc */
2019-12-02 18:23:00 +00:00
#define _DEFAULT_SOURCE 1
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xrandr.h>
#include "config.h"
#ifdef VIRTNOTIFY
#include <libnotify/notify.h>
#endif
2019-12-02 18:23:00 +00:00
#include "dat.h"
#include "fns.h"
2021-02-26 18:01:22 +00:00
Client* hiddenc[MAXHIDDEN];
2019-12-02 18:23:00 +00:00
2021-02-26 18:01:22 +00:00
int numhidden;
2019-12-02 18:23:00 +00:00
int virt;
int reversehide = 1;
2021-02-26 18:01:22 +00:00
Client* currents[NUMVIRTUALS] = {NULL, NULL, NULL, NULL};
char* b2items[NUMVIRTUALS + 1] = VIRTUALLIST;
2021-02-26 18:01:22 +00:00
Menu b2menu = {b2items};
char* b3items[B3FIXED + MAXHIDDEN + 1] = {
"New",
"Reshape",
"Move",
#if defined(SHOWMAX) && defined(SHOWSTICK)
"Maximize",
"Stick",
#else
#ifdef SHOWMAX
"Maximize",
#endif
#ifdef SHOWSTICK
2021-02-26 18:01:22 +00:00
"Stick",
#endif
#endif
"Delete",
"Hide",
2021-02-26 18:01:22 +00:00
0};
2019-12-02 18:23:00 +00:00
2021-02-26 18:01:22 +00:00
enum {
New,
Reshape,
Move,
#if defined(SHOWMAX) && defined(SHOWSTICK)
Maximize,
Stick,
#else
#ifdef SHOWMAX
Maximize,
#endif
#ifdef SHOWSTICK
Stick,
#endif
#endif
Delete,
Hide
2019-12-02 18:23:00 +00:00
};
2021-02-26 18:01:22 +00:00
Menu b3menu = {b3items};
2019-12-02 18:23:00 +00:00
2021-02-26 18:01:22 +00:00
Menu egg = {version};
2019-12-02 18:23:00 +00:00
2021-02-26 19:50:23 +00:00
void button(XButtonEvent* e) {
2021-02-26 18:01:22 +00:00
int n, shift;
Client* c;
Window dw;
ScreenInfo* s;
#ifdef SHOWMAX
XRRMonitorInfo monitor;
#endif
2021-02-26 18:01:22 +00:00
curtime = e->time;
s = getscreen(e->root);
2021-02-26 19:50:23 +00:00
if (s == 0)
return;
2021-02-26 18:01:22 +00:00
c = getclient(e->window, 0);
if (c) {
if (debug)
fprintf(
stderr,
"but: e x=%d y=%d c x=%d y=%d dx=%d dy=%d BORDR %d\n",
e->x,
e->y,
c->x,
c->y,
c->dx,
c->dy,
BORDER);
if (borderorient(c, e->x, e->y) != BorderUnknown) {
switch (e->button) {
case Button1:
2021-02-26 19:50:23 +00:00
case Button2:
reshape(c, e->button, pull, e);
return;
case Button3:
move(c, Button3);
return;
default:
return;
2021-02-26 18:01:22 +00:00
}
}
e->x += c->x - BORDER;
e->y += c->y - BORDER;
} else if (e->window != e->root) {
2021-02-26 19:50:23 +00:00
if (debug)
fprintf(stderr, "but no client: e x=%d y=%d\n", e->x, e->y);
2021-02-26 18:01:22 +00:00
XTranslateCoordinates(
2021-02-26 19:50:23 +00:00
dpy,
e->window,
s->root,
e->x,
e->y,
&e->x,
&e->y,
&dw);
2021-02-26 18:01:22 +00:00
}
switch (e->button) {
case Button1:
fflush(stdout);
if (c) {
2021-02-26 19:50:23 +00:00
if (ffm)
XRaiseWindow(dpy, c->window);
2021-02-26 18:01:22 +00:00
XMapRaised(dpy, c->parent);
top(c);
active(c);
}
return;
case Button2:
if (c) {
XMapRaised(dpy, c->parent);
top(c);
2021-02-26 18:01:22 +00:00
active(c);
} else if (
(e->state & (ShiftMask | ControlMask)) == (ShiftMask | ControlMask)) {
menuhit(e, &egg);
} else if (numvirtuals > 1 && (n = menuhit(e, &b2menu)) > -1)
button2(n);
return;
2021-02-26 19:50:23 +00:00
case Button3:
if (c) {
XMapRaised(dpy, c->parent);
top(c);
active(c);
return;
}
2021-02-26 19:50:23 +00:00
break;
2021-02-26 18:01:22 +00:00
case Button4:
/* scroll up changes to previous virtual screen, wraps around */
if (!c && e->type == ButtonPress) {
if (numvirtuals > 1) {
if (virt > 0) {
switch_to(virt - 1);
} else {
switch_to(numvirtuals - 1);
}
}
}
2021-02-26 18:01:22 +00:00
return;
case Button5:
/* scroll down changes to next virtual screen, wraps around */
if (!c && e->type == ButtonPress) {
if (numvirtuals > 1) {
if (virt < numvirtuals - 1) {
switch_to(virt + 1);
} else {
switch_to(0);
}
}
}
2021-02-26 19:50:23 +00:00
return;
default:
2021-02-26 18:01:22 +00:00
return;
}
2021-02-26 19:50:23 +00:00
if (current && current->screen == s)
cmapnofocus(s);
2021-02-26 18:01:22 +00:00
switch (n = menuhit(e, &b3menu)) {
2021-02-26 19:50:23 +00:00
case New:
spawn(s);
break;
case Reshape:
reshape(selectwin(1, 0, s), Button3, sweep, 0);
break;
case Move:
move(selectwin(0, 0, s), Button3);
break;
2021-02-26 18:01:22 +00:00
case Delete:
shift = 0;
c = selectwin(1, &shift, s);
delete (c, shift);
2021-02-26 18:01:22 +00:00
break;
2021-02-26 19:50:23 +00:00
case Hide:
hide(selectwin(1, 0, s));
break;
#ifdef SHOWMAX
case Maximize:
c = selectwin(1, 0, s);
#ifdef AUTOSTICK
if (isautostick(c))
break;
#endif
monitor = monitorinfo[getmonitorbyclient(c)];
quickreshape(c, monitor.x, monitor.y, monitor.width, monitor.height, 1);
active(c);
top(c);
break;
#endif
#ifdef SHOWSTICK
2021-02-26 19:50:23 +00:00
case Stick:
stick(selectwin(1, 0, s));
break;
#endif
2021-02-26 19:50:23 +00:00
default: /* unhide window */
unhide(n - B3FIXED, 1);
break;
case -1: /* nothing */
break;
2021-02-26 18:01:22 +00:00
}
2021-02-26 19:50:23 +00:00
if (current && current->screen == s)
cmapfocus(current);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void spawn(ScreenInfo* s) {
2021-02-26 18:01:22 +00:00
/*
* ugly dance to cause sweeping for terminals.
* the very next window created will require sweeping.
* hope it's created by the program we're about to
* exec!
*/
isNew = 1;
/*
* ugly dance to avoid leaving zombies. Could use SIGCHLD,
* but it's not very portable.
*/
if (fork() == 0) {
if (fork() == 0) {
close(ConnectionNumber(dpy));
2021-02-26 19:50:23 +00:00
if (s->display[0] != '\0')
putenv(s->display);
2021-02-26 18:01:22 +00:00
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGHUP, SIG_DFL);
if (termprog != NULL) {
execl(shell, shell, "-c", termprog, (char*)0);
fprintf(stderr, "ryudo: exec %s", shell);
perror(" failed");
}
execlp("urxvt", "urxvt", (char*)0);
execlp("9term", "9term", scrolling ? "-ws" : "-w", (char*)0);
execlp("xterm", "xterm", "-ut", (char*)0);
perror("ryudo: exec urxvt/9term/xterm failed");
exit(1);
}
exit(0);
}
wait((int*)0);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void reshape(
2021-02-26 18:01:22 +00:00
Client* c, int but, int (*fn)(Client*, int, XButtonEvent*), XButtonEvent* e) {
int odx, ody;
2021-02-26 19:50:23 +00:00
if (c == 0)
return;
2021-02-26 18:01:22 +00:00
odx = c->dx;
ody = c->dy;
2021-02-26 19:50:23 +00:00
if (fn(c, but, e) == 0)
return;
c->border = BORDER;
2021-02-26 18:01:22 +00:00
active(c);
top(c);
XRaiseWindow(dpy, c->parent);
XMoveResizeWindow(
dpy,
c->parent,
c->x - BORDER,
c->y - BORDER,
c->dx + 2 * BORDER,
c->dy + 2 * BORDER);
if (c->dx == odx && c->dy == ody)
sendconfig(c);
else
XMoveResizeWindow(dpy, c->window, BORDER, BORDER, c->dx, c->dy);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void move(Client* c, int but) {
if (c == 0)
return;
if (drag(c, but) == 0)
return;
c->border = BORDER;
2021-02-26 18:01:22 +00:00
active(c);
top(c);
XRaiseWindow(dpy, c->parent);
XMoveWindow(dpy, c->parent, c->x - c->border, c->y - c->border);
2021-02-26 18:01:22 +00:00
sendconfig(c);
2019-12-02 18:23:00 +00:00
}
2021-02-26 18:01:22 +00:00
void delete (Client* c, int shift) {
Client* revertc;
int m;
2021-02-26 19:50:23 +00:00
if (c == 0)
return;
if (c == current) {
m = getmonitorbyclient(c);
if (revertc = getclient(getrevert(c), 0)) {
top(revertc);
active(revertc);
} else {
shuffleonmonitor(m);
}
}
if ((c->proto & Pdelete) && !shift)
2021-02-26 18:01:22 +00:00
sendcmessage(c->window, wm_protocols, wm_delete, 0, 0);
else
XKillClient(dpy, c->window); /* let event clean up */
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void hide(Client* c) {
int monitor;
Client* revertc;
2021-02-26 19:50:23 +00:00
if (c == 0 || numhidden == MAXHIDDEN)
return;
2021-02-26 18:01:22 +00:00
if (hidden(c)) {
fprintf(stderr, "ryudo: already hidden: %s\n", c->label);
return;
}
monitor = getmonitorbyclient(c);
2021-02-26 18:01:22 +00:00
XUnmapWindow(dpy, c->parent);
XUnmapWindow(dpy, c->window);
setstate(c, IconicState);
if (c == current) {
if (revertc = getclient(getrevert(c), 0)) {
top(revertc);
active(revertc);
} else {
shuffleonmonitor(monitor);
}
}
2021-02-26 18:01:22 +00:00
if (reversehide) {
memmove(hiddenc + 1, hiddenc, numhidden * sizeof hiddenc[0]);
memmove(
2021-02-26 19:50:23 +00:00
b3items + B3FIXED + 1,
b3items + B3FIXED,
numhidden * sizeof b3items[0]);
2021-02-26 18:01:22 +00:00
hiddenc[0] = c;
b3items[B3FIXED] = c->label;
} else {
hiddenc[numhidden] = c;
b3items[B3FIXED + numhidden] = c->label;
}
numhidden++;
b3items[B3FIXED + numhidden] = 0;
ensureactiveonmonitor(monitor);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void unhide(int n, int map) {
2021-02-26 18:01:22 +00:00
Client* c;
int i;
if (n >= numhidden) {
fprintf(stderr, "ryudo: unhide: n %d numhidden %d\n", n, numhidden);
return;
}
c = hiddenc[n];
if (!hidden(c)) {
fprintf(
stderr,
"ryudo: unhide: not hidden: %s(0x%x)\n",
c->label,
(int)c->window);
return;
}
c->virt = virt;
if (map) {
XMapWindow(dpy, c->window);
XMapRaised(dpy, c->parent);
setstate(c, NormalState);
active(c);
top(c);
}
numhidden--;
for (i = n; i < numhidden; i++) {
hiddenc[i] = hiddenc[i + 1];
b3items[B3FIXED + i] = b3items[B3FIXED + i + 1];
}
b3items[B3FIXED + numhidden] = 0;
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void unhidec(Client* c, int map) {
2021-02-26 18:01:22 +00:00
int i;
for (i = 0; i < numhidden; i++)
if (c == hiddenc[i]) {
unhide(i, map);
return;
}
fprintf(
2021-02-26 19:50:23 +00:00
stderr,
"ryudo: unhidec: not hidden: %s(0x%x)\n",
c->label,
(int)c->window);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void stick(Client* c) {
2021-02-26 18:01:22 +00:00
if (numvirtuals > 1 && c->virt >= 0)
c->virt = -1;
2021-02-26 18:01:22 +00:00
else
c->virt = virt;
}
2021-02-26 19:50:23 +00:00
void renamec(Client* c, char* name) {
2021-02-26 18:01:22 +00:00
int i;
2021-02-26 19:50:23 +00:00
if (name == 0)
name = "???";
2021-02-26 18:01:22 +00:00
c->label = name;
2021-02-26 19:50:23 +00:00
if (!hidden(c))
return;
2021-02-26 18:01:22 +00:00
for (i = 0; i < numhidden; i++)
if (c == hiddenc[i]) {
b3items[B3FIXED + i] = name;
return;
}
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void button2(int n) {
2021-02-26 18:01:22 +00:00
switch_to(n);
2021-02-26 19:50:23 +00:00
if (current)
cmapfocus(current);
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void switch_to_c(int n, Client* c) {
if (c == 0)
return;
2019-12-02 18:23:00 +00:00
2021-02-26 19:50:23 +00:00
if (c->next)
switch_to_c(n, c->next);
2019-12-02 18:23:00 +00:00
2021-02-26 19:50:23 +00:00
if (c->parent == DefaultRootWindow(dpy))
return;
2019-12-02 18:23:00 +00:00
#ifdef AUTOSTICK
2021-02-26 19:50:23 +00:00
if (c->virt >= 0 && isautostick(c)) {
stick(c);
}
#endif
2021-02-26 18:01:22 +00:00
if (c->virt != virt && c->state == NormalState && c->virt >= 0) {
XUnmapWindow(dpy, c->parent);
XUnmapWindow(dpy, c->window);
setstate(c, IconicState);
2021-02-26 19:50:23 +00:00
if (c == current)
setactive(c, 0);
2021-02-26 18:01:22 +00:00
} else if (c->virt == virt && c->state == IconicState) {
int i;
for (i = 0; i < numhidden; i++)
2021-02-26 19:50:23 +00:00
if (c == hiddenc[i])
break;
2021-02-26 18:01:22 +00:00
if (i == numhidden) {
XMapWindow(dpy, c->window);
XMapWindow(dpy, c->parent);
setstate(c, NormalState);
2021-02-26 19:50:23 +00:00
if (currents[virt] == c)
active(c);
else
setactive(c, 0);
2021-02-26 18:01:22 +00:00
}
}
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void switch_to(int n) {
#ifdef VIRTNOTIFY
2021-02-26 18:01:22 +00:00
static char virtmsg[32];
NotifyNotification* notification =
notify_notification_new(VIRTHEADER, NULL, NULL);
#endif
2021-02-26 19:50:23 +00:00
if (n == virt)
return;
2021-02-26 18:01:22 +00:00
currents[virt] = current;
// if current is sticky, and no current in next virt, keep this one
if (current && current->virt == -1 && !currents[n]) {
currents[n] = current;
}
2021-02-26 18:01:22 +00:00
virt = n;
/* redundant when called from a menu switch
* but needed for scroll-button switches
*/
b2menu.lasthit = n;
switch_to_c(n, clients);
2021-02-26 18:01:22 +00:00
current = currents[virt];
if (current) {
active(current);
top(current);
} else {
nofocus();
}
#ifdef VIRTNOTIFY
sprintf(virtmsg, VIRTMSG, b2items[virt]);
notify_notification_update(notification, VIRTHEADER, virtmsg, NULL);
notify_notification_set_category(notification, "virtual");
notify_notification_show(notification, NULL);
notify_notification_close(notification, NULL);
#endif
2019-12-02 18:23:00 +00:00
}
2021-02-26 19:50:23 +00:00
void initb2menu(int n) { b2items[n] = 0; }