added mouseDeactivate and keyMouseDeactivate options; 0 means 'do not deactivate...
[k8yeahconsole.git] / src / yeahconsole.c
blobf2abfda7a72019bb0909e74f96a97c627fee450a
1 /*************************************************************************/
2 /* _____________ */
3 /* < yeahconsole > */
4 /* ------------- */
5 /* \ ^__^ */
6 /* \ (oo)\_______ */
7 /* (__)\ )\/\ */
8 /* ||----w | */
9 /* || || */
10 /* */
11 /* Copyright (C) knorke */
12 /* */
13 /* This program is free software; you can redistribute it and/or modify */
14 /* it under the terms of the GNU General Public License as published by */
15 /* the Free Software Foundation; either version 2 of the License, or */
16 /* (at your option) any later version. */
17 /* */
18 /* This program is distributed in the hope that it will be useful, */
19 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
20 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
21 /* GNU General Public License for more details. */
22 /* */
23 /* You should have received a copy of the GNU General Public License */
24 /* along with this program; if not, write to the Free Software */
25 /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
26 /*************************************************************************/
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
33 #include <sys/select.h>
35 #include <X11/Xlib.h>
36 #include <X11/keysym.h>
37 #include <X11/Xutil.h>
38 #include <X11/cursorfont.h>
41 #define UP 0
42 #define DOWN (-height-optHandleWidth)
44 #define TRANSPARENCY_HACK do { \
45 XResizeWindow(dpy, termwin, optWidth, height-1);\
46 XResizeWindow(dpy, termwin, optWidth, height); \
47 } while (0);
50 static Display *dpy;
51 static Window root;
52 static Window win;
53 static Window termwin;
54 static const char *progname;
55 static char command[4096];
56 static int revert_to;
57 static int screen;
58 static int height;
59 static int optX, optWidth, optHeight, optSlideDelay, optHandleWidth, optStep, optRestart, optMouseDeactivate = 0;
60 static const char *optColor = NULL;
61 static const char *optTerm = NULL;
62 static KeySym optKeyToggle;
63 static KeySym optKeySmaller;
64 static KeySym optKeyBigger;
65 static KeySym optKeyFullScreen;
66 static KeySym optKeyMouseDeactivate;
67 static Cursor cursor;
68 static int resizeInc;
71 static int hidden = 1;
72 static int fullscreen = 0;
73 static int old_height = -1;
74 static Window last_focused, current_focused;
77 static int handle_xerror (Display *display, XErrorEvent *e) {
78 /* this function does nothing, we receive xerrors when the last_focused window gets lost.. */
79 /* fprintf(stderr, "XError caught\n"); */
80 fprintf(stderr, "%d XError caught\n", e->error_code);
81 return 0;
85 static void buildTermCommand (int argc, char *argv[]) {
86 int f;
87 char *pos;
88 /* */
89 pos = command;
90 if (strstr(optTerm, "urxvt")) pos += sprintf(pos, "%s -b 0 -embed %d -name %s ", optTerm, (int)win, progname);
91 if (strstr(optTerm, "mrxvt")) pos += sprintf(pos, "%s -into %d -aht -bl -name %s ", optTerm, (int)win, progname);
92 else pos += sprintf(pos, "%s -b 0 -into %d -name %s ", optTerm, (int)win, progname);
93 for (f = 1; f < argc; ++f) pos += sprintf(pos, "%s ", argv[f]);
94 sprintf(pos, "&");
98 static int runTerm (int move) {
99 XEvent ev;
100 long dummy;
101 XSizeHints *size;
102 /* */
103 if (WEXITSTATUS(system(command))) return -1;
105 while (1) {
106 XMaskEvent(dpy, SubstructureNotifyMask, &ev);
107 if (ev.type == CreateNotify || ev.type == MapNotify) {
108 termwin = ev.xcreatewindow.window;
109 break;
113 XSetWindowBorderWidth(dpy, termwin, 0);
114 size = XAllocSizeHints();
115 XGetWMNormalHints(dpy, termwin, size, &dummy);
116 resizeInc = size->height_inc;
117 if (move) height = resizeInc*optHeight;
118 XFree(size);
119 XResizeWindow(dpy, termwin, optWidth, height);
120 XResizeWindow(dpy, win, optWidth, height+optHandleWidth);
121 if (move) XMoveWindow(dpy, win, optX, -(height+optHandleWidth));
122 return 0;
126 static void createWindow (void) {
127 XSetWindowAttributes attrib;
128 XColor color;
129 XColor dummy_color;
130 /* */
131 attrib.override_redirect = True;
132 attrib.background_pixel = BlackPixel(dpy, screen);
133 win = XCreateWindow(dpy, root,
134 optX, -200, optWidth, 200,
135 0, CopyFromParent, InputOutput, CopyFromParent,
136 CWOverrideRedirect | CWBackPixel, &attrib);
137 XSelectInput(dpy, win,
138 SubstructureNotifyMask | EnterWindowMask | LeaveWindowMask
139 | KeyPressMask | ButtonPressMask | ButtonReleaseMask);
140 XAllocNamedColor(dpy, DefaultColormap(dpy, screen), optColor, &color, &dummy_color);
141 XSetWindowBackground(dpy, win, color.pixel);
142 XDefineCursor(dpy, win, cursor);
143 XMapWindow(dpy, win);
147 static void resize (void) {
148 XEvent ev;
149 /* */
150 if (!XGrabPointer
151 (dpy, root, False,
152 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
153 GrabModeAsync, GrabModeAsync, None, cursor,
154 CurrentTime) == GrabSuccess)
155 return;
156 for (;;) {
157 XMaskEvent(dpy,
158 ButtonPressMask | ButtonReleaseMask |
159 PointerMotionHintMask, &ev);
160 switch (ev.type) {
161 case MotionNotify:
162 if (ev.xmotion.y >= resizeInc) {
163 height = ev.xmotion.y-ev.xmotion.y%resizeInc;
164 XResizeWindow(dpy, termwin, optWidth, height);
165 XResizeWindow(dpy, win, optWidth, height+optHandleWidth);
166 XSync(dpy, False);
167 } else {
168 XUngrabPointer(dpy, CurrentTime); /*k8: ???*/
170 break;
171 case ButtonRelease:
172 XUngrabPointer(dpy, CurrentTime);
173 return;
179 static KeySym grabKey (const char *opt, unsigned int numlockmask) {
180 unsigned int modmask = 0;
181 KeySym keysym;
182 /* */
183 if (strstr(opt, "Control")) modmask = modmask | ControlMask;
184 if (strstr(opt, "Alt")) modmask = modmask | Mod1Mask;
185 if (strstr(opt, "Win")) modmask = modmask | Mod4Mask;
186 if (strstr(opt, "None")) modmask = 0;
187 opt = strrchr(opt, '+');
188 keysym = XStringToKeysym(++opt);
189 XGrabKey(dpy, XKeysymToKeycode(dpy, keysym), modmask, root, True, GrabModeAsync, GrabModeAsync);
190 XGrabKey(dpy, XKeysymToKeycode(dpy, keysym), LockMask | modmask, root, True, GrabModeAsync, GrabModeAsync);
191 if (numlockmask) {
192 XGrabKey(dpy, XKeysymToKeycode(dpy, keysym), numlockmask | modmask, root, True, GrabModeAsync, GrabModeAsync);
193 XGrabKey(dpy, XKeysymToKeycode(dpy, keysym), numlockmask | LockMask | modmask, root, True, GrabModeAsync, GrabModeAsync);
195 return keysym;
199 enum {
200 otInt,
201 otStr,
202 otKey
205 typedef struct {
206 int type;
207 char sn;
208 const char *name;
209 int defi; // -1: DisplayWidth
210 const char *defs;
211 union {
212 void *optV;
213 int *optI;
214 char **optS;
215 KeySym *optKS;
217 } Option;
219 static Option options[] = {
220 {otInt, 'w', "screenWidth", -1, NULL, {&optWidth}},
221 {otInt, 'W', "handleWidth", 3, NULL, {&optHandleWidth}},
222 {otStr, 'C', "handleColor", 0, "grey70", {&optColor}},
223 {otInt, 'h', "consoleHeight", 38, NULL, {&optHeight}},
224 {otInt, 'x', "xOffset", 0, NULL, {&optX}},
225 {otInt, 'a', "aniDelay", 20, NULL, {&optSlideDelay}},
226 {otInt, 's', "stepSize", 1, NULL, {&optStep}},
227 {otInt, 'r', "restart", 0, NULL, {&optRestart}},
228 {otStr, 't', "term", 0, "mrxvt", {&optTerm}},
229 {otInt, 'm', "mouseDeactivate", 0, NULL, {&optMouseDeactivate}},
230 {otKey, 'T', "keyToggle", -2, "Win+grave", {&optKeyToggle}},
231 {otKey, 'S', "keySmaller", -2, "Win+KP_Subtract", {&optKeySmaller}},
232 {otKey, 'B', "keyBigger", -2, "Win+KP_Add", {&optKeyBigger}},
233 {otKey, 'F', "keyFull", -2, "Win+F11", {&optKeyFullScreen}},
234 {otKey, 'M', "keyMouseDeactivate", -2, "Win+F12", {&optKeyMouseDeactivate}},
239 static void showOptions (void) {
240 int f;
241 for (f = 0; options[f].name; ++f) {
242 printf("%s: ", options[f].name);
243 switch (options[f].type) {
244 case otInt: printf("%d", options[f].defi); break;
245 case otStr:
246 case otKey:
247 printf("%s", options[f].defs);
248 break;
250 putchar('\n');
255 static void getOptions (int argc, char *argv[]) {
256 unsigned int numlockmask = 0;
257 const char *opt;
258 XModifierKeymap *modmap;
259 int f, c;
260 /* */
261 /* modifier stuff taken from evilwm */
262 modmap = XGetModifierMapping(dpy);
263 for (f = 0; f < 8; ++f) {
264 for (c = 0; c < modmap->max_keypermod; ++c) {
265 if (modmap->modifiermap[f*modmap->max_keypermod+c] == XKeysymToKeycode(dpy, XK_Num_Lock)) {
266 numlockmask = (1 << f);
270 XFreeModifiermap(modmap);
271 /* */
272 for (f = 0; options[f].name; ++f) {
273 if (options[f].type == otInt && options[f].defi == -1) options[f].defi = DisplayWidth(dpy, screen);
274 opt = XGetDefault(dpy, progname, options[f].name);
275 switch (options[f].type) {
276 case otInt:
277 *(options[f].optI) = opt ? atoi(opt) : options[f].defi;
278 break;
279 case otStr:
280 if (*(options[f].optS)) free(*(options[f].optS));
281 *(options[f].optS) = strdup(opt?opt:options[f].defs);
282 break;
283 case otKey:
284 *(options[f].optKS) = grabKey(opt?opt:options[f].defs, numlockmask);
285 break;
288 if (optWidth < 1) optWidth = DisplayWidth(dpy, screen);
292 static void eventEnter (XEvent *e) {
293 XSetInputFocus(dpy, termwin, RevertToPointerRoot, CurrentTime);
297 static void eventLeave (XEvent *e) {
298 if (optMouseDeactivate) {
299 if (last_focused && e->xcrossing.detail != NotifyInferior) {
300 XSetInputFocus(dpy, last_focused, RevertToPointerRoot, CurrentTime);
306 static void eventUnmap (XEvent *e) {
307 if (e->xunmap.window == termwin) {
308 if (optRestart) {
309 runTerm(0);
310 XSync(dpy, False);
311 XSetInputFocus(dpy, termwin, RevertToPointerRoot, CurrentTime);
312 } else {
313 if (last_focused) XSetInputFocus(dpy, last_focused, RevertToPointerRoot, CurrentTime);
314 XSync(dpy, False);
315 exit(0);
321 #ifdef __linux__
322 static void xsleep (int ms) {
323 XEvent event;
324 fd_set rds, wrs;
325 struct timeval to;
326 int fd = ConnectionNumber(dpy), r;
327 if (ms < 1) return;
328 to.tv_sec = ms/1000;
329 to.tv_usec = (ms%1000)*100;
330 while (to.tv_sec > 0 || to.tv_usec > 0) {
331 FD_ZERO(&rds); FD_ZERO(&wrs);
332 FD_SET(fd, &rds); FD_SET(fd, &wrs);
333 /*WARNING! Linux-specific usage of timeouts!*/
334 r = select(fd+1, &rds, &wrs, NULL, &to);
335 if (r <= 0) break;
336 while (XPending(dpy) > 0) {
337 XNextEvent(dpy, &event);
338 switch (event.type) {
339 case EnterNotify: eventEnter(&event); break;
340 case LeaveNotify: eventLeave(&event); break;
341 case UnmapNotify: eventUnmap(&event); break;
346 #endif
349 static void roll (int y) {
350 int step = height/100+1+optStep;
351 if (y == UP) step = -step;
352 for (;;) {
353 y += step;
354 XMoveWindow(dpy, win, optX, y);
355 //XMoveResizeWindow(dpy, win, optX, y, optWidth, height);
356 XSync(dpy, False);
357 #ifdef __linux__
358 xsleep(optSlideDelay);
359 #else
360 usleep(optSlideDelay*100);
361 #endif
362 if (y/step == 0 || y < -(height+optHandleWidth)) break;
367 int main (int argc, char *argv[]) {
368 int firstTimeH = 1;
369 int f;
370 KeySym key;
371 XEvent event;
372 /* */
373 /* strip the path from argv[0] if there is one */
374 progname = strrchr(argv[0], '/');
375 progname = progname ? progname+1 : argv[0];
376 /* */
377 for (f = 1; f < argc; ++f) {
378 if (!strcmp(argv[f], "-h")) {
379 printf(
380 "%s:\n"
381 "-e: program to execute\n"
382 "you can configure me via xresources:\n"
383 "%s*foo:value\n"
384 "foo can be any standard xterm/urxvt xresource or:\n"
385 "resource: default value\n"
387 progname, progname);
388 showOptions();
389 return 1;
393 if (!(dpy = XOpenDisplay(NULL))) {
394 fprintf(stderr, "FATAL: can not open dpy %s\n", XDisplayName(NULL));
395 return 1;
398 screen = DefaultScreen(dpy);
399 root = RootWindow(dpy, screen);
400 XSetErrorHandler(handle_xerror);
401 cursor = XCreateFontCursor(dpy, XC_double_arrow);
403 getOptions(argc, argv);
404 createWindow();
405 buildTermCommand(argc, argv);
406 if (runTerm(1)) {
407 fprintf(stderr, "FATAL: can't start terminal!\n");
408 return 2;
411 while (1) {
412 XNextEvent(dpy, &event);
413 switch (event.type) {
414 case EnterNotify: eventEnter(&event); break;
415 case LeaveNotify: eventLeave(&event); break;
416 case UnmapNotify: eventUnmap(&event); break;
417 case ButtonPress: resize(); break;
418 case KeyPress:
419 key = XKeycodeToKeysym(dpy, event.xkey.keycode, 0);
420 if (key == optKeyToggle) {
421 if (!hidden) {
422 XGetInputFocus(dpy, &current_focused, &revert_to);
423 if (last_focused && current_focused == termwin) {
424 XSetInputFocus(dpy, last_focused, RevertToPointerRoot, CurrentTime);
425 } /*else XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);*/
426 if (optStep && !fullscreen) roll(UP);
427 XMoveWindow(dpy, win, optX, -height-optHandleWidth);
428 hidden = 1;
429 XSync(dpy, False);
430 } else {
431 XRaiseWindow(dpy, win);
432 XGetInputFocus(dpy, &last_focused, &revert_to);
433 XSetInputFocus(dpy, termwin, RevertToPointerRoot, CurrentTime);
434 if (optStep && !fullscreen) {
435 //XGrabServer(dpy);
436 roll(DOWN);
437 //XUngrabServer(dpy);
439 XMoveWindow(dpy, win, optX, 0);
440 if (firstTimeH) {
441 firstTimeH = 0;
442 TRANSPARENCY_HACK
444 hidden = 0;
446 break;
448 if (!hidden) {
449 if (key == optKeyFullScreen) {
450 if (!fullscreen) {
451 old_height = height;
452 height = DisplayHeight(dpy, screen);
453 fullscreen = 1;
454 } else {
455 height = old_height;
456 fullscreen = 0;
458 firstTimeH = 1;
460 else if (key == optKeyBigger) { height += resizeInc; firstTimeH = 1; }
461 else if (key == optKeySmaller) { height -= resizeInc; firstTimeH = 1; }
462 else if (key == optKeyMouseDeactivate) optMouseDeactivate = !optMouseDeactivate;
464 if (height < resizeInc) { height = resizeInc; firstTimeH = 1; }
465 if (firstTimeH) {
466 firstTimeH = 0;
467 XResizeWindow(dpy, termwin, optWidth, height);
468 XResizeWindow(dpy, win, optWidth, height+optHandleWidth);
469 /*XSetInputFocus(dpy, termwin, RevertToPointerRoot, CurrentTime);*/
472 break;
475 return 0;