1 /*************************************************************************/
11 /* Copyright (C) knorke */
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. */
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. */
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 /*************************************************************************/
33 #include <sys/select.h>
36 #include <X11/keysym.h>
37 #include <X11/Xutil.h>
38 #include <X11/cursorfont.h>
42 #define DOWN (-height-optHandleWidth)
44 #define TRANSPARENCY_HACK do { \
45 XResizeWindow(dpy, termwin, optWidth, height-1);\
46 XResizeWindow(dpy, termwin, optWidth, height); \
53 static Window termwin
;
54 static const char *progname
;
55 static char command
[4096];
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
;
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
);
85 static void buildTermCommand (int argc
, char *argv
[]) {
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
]);
98 static int runTerm (int move
) {
103 if (WEXITSTATUS(system(command
))) return -1;
106 XMaskEvent(dpy
, SubstructureNotifyMask
, &ev
);
107 if (ev
.type
== CreateNotify
|| ev
.type
== MapNotify
) {
108 termwin
= ev
.xcreatewindow
.window
;
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
;
119 XResizeWindow(dpy
, termwin
, optWidth
, height
);
120 XResizeWindow(dpy
, win
, optWidth
, height
+optHandleWidth
);
121 if (move
) XMoveWindow(dpy
, win
, optX
, -(height
+optHandleWidth
));
126 static void createWindow (void) {
127 XSetWindowAttributes attrib
;
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) {
152 ButtonPressMask
| ButtonReleaseMask
| PointerMotionMask
,
153 GrabModeAsync
, GrabModeAsync
, None
, cursor
,
154 CurrentTime
) == GrabSuccess
)
158 ButtonPressMask
| ButtonReleaseMask
|
159 PointerMotionHintMask
, &ev
);
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
);
168 XUngrabPointer(dpy
, CurrentTime
); /*k8: ???*/
172 XUngrabPointer(dpy
, CurrentTime
);
179 static KeySym
grabKey (const char *opt
, unsigned int numlockmask
) {
180 unsigned int modmask
= 0;
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
);
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
);
209 int defi
; // -1: DisplayWidth
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) {
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;
247 printf("%s", options
[f
].defs
);
255 static void getOptions (int argc
, char *argv
[]) {
256 unsigned int numlockmask
= 0;
258 XModifierKeymap
*modmap
;
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
);
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
) {
277 *(options
[f
].optI
) = opt
? atoi(opt
) : options
[f
].defi
;
280 if (*(options
[f
].optS
)) free(*(options
[f
].optS
));
281 *(options
[f
].optS
) = strdup(opt
?opt
:options
[f
].defs
);
284 *(options
[f
].optKS
) = grabKey(opt
?opt
:options
[f
].defs
, numlockmask
);
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
) {
311 XSetInputFocus(dpy
, termwin
, RevertToPointerRoot
, CurrentTime
);
313 if (last_focused
) XSetInputFocus(dpy
, last_focused
, RevertToPointerRoot
, CurrentTime
);
322 static void xsleep (int ms
) {
326 int fd
= ConnectionNumber(dpy
), r
;
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
);
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;
349 static void roll (int y
) {
350 int step
= height
/100+1+optStep
;
351 if (y
== UP
) step
= -step
;
354 XMoveWindow(dpy
, win
, optX
, y
);
355 //XMoveResizeWindow(dpy, win, optX, y, optWidth, height);
358 xsleep(optSlideDelay
);
360 usleep(optSlideDelay
*100);
362 if (y
/step
== 0 || y
< -(height
+optHandleWidth
)) break;
367 int main (int argc
, char *argv
[]) {
373 /* strip the path from argv[0] if there is one */
374 progname
= strrchr(argv
[0], '/');
375 progname
= progname
? progname
+1 : argv
[0];
377 for (f
= 1; f
< argc
; ++f
) {
378 if (!strcmp(argv
[f
], "-h")) {
381 "-e: program to execute\n"
382 "you can configure me via xresources:\n"
384 "foo can be any standard xterm/urxvt xresource or:\n"
385 "resource: default value\n"
393 if (!(dpy
= XOpenDisplay(NULL
))) {
394 fprintf(stderr
, "FATAL: can not open dpy %s\n", XDisplayName(NULL
));
398 screen
= DefaultScreen(dpy
);
399 root
= RootWindow(dpy
, screen
);
400 XSetErrorHandler(handle_xerror
);
401 cursor
= XCreateFontCursor(dpy
, XC_double_arrow
);
403 getOptions(argc
, argv
);
405 buildTermCommand(argc
, argv
);
407 fprintf(stderr
, "FATAL: can't start terminal!\n");
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;
419 key
= XKeycodeToKeysym(dpy
, event
.xkey
.keycode
, 0);
420 if (key
== optKeyToggle
) {
422 XGetInputFocus(dpy
, ¤t_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
);
431 XRaiseWindow(dpy
, win
);
432 XGetInputFocus(dpy
, &last_focused
, &revert_to
);
433 XSetInputFocus(dpy
, termwin
, RevertToPointerRoot
, CurrentTime
);
434 if (optStep
&& !fullscreen
) {
437 //XUngrabServer(dpy);
439 XMoveWindow(dpy
, win
, optX
, 0);
449 if (key
== optKeyFullScreen
) {
452 height
= DisplayHeight(dpy
, screen
);
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; }
467 XResizeWindow(dpy
, termwin
, optWidth
, height
);
468 XResizeWindow(dpy
, win
, optWidth
, height
+optHandleWidth
);
469 /*XSetInputFocus(dpy, termwin, RevertToPointerRoot, CurrentTime);*/