|
|
452870 |
|
|
|
452870 |
#include "app.h"
|
|
|
452870 |
|
|
|
cbe4b8 |
#include <math.h></math.h>
|
|
|
cbe4b8 |
|
|
|
5b6cfc |
#include <sys time.h=""></sys>
|
|
|
5b6cfc |
#include <sys types.h=""></sys>
|
|
|
8603b4 |
#include <sys poll.h=""></sys>
|
|
|
5b6cfc |
#include <unistd.h></unistd.h>
|
|
|
452870 |
|
|
|
dbe1a1 |
#include <x11 xutil.h=""></x11>
|
|
|
dbe1a1 |
#include <x11 xatom.h=""></x11>
|
|
|
452870 |
|
|
|
452870 |
|
|
|
7e0df9 |
int appInit(App *app, const char *touch_dev) {
|
|
|
452870 |
LOGDBG("app: init");
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
CLEARFROM(app, dpy);
|
|
|
452870 |
|
|
|
452870 |
LOGDBG("app: init: connect to xcb");
|
|
|
dbe1a1 |
app->dpy = XOpenDisplay(NULL);
|
|
|
dbe1a1 |
if (!app->dpy)
|
|
|
452870 |
return LOGERR("app: init: cannot connect to xcb");
|
|
|
452870 |
|
|
|
452870 |
LOGDBG("app: init: get screen");
|
|
|
dbe1a1 |
app->screen = DefaultScreen(app->dpy);
|
|
|
dbe1a1 |
app->sw = DisplayWidth(app->dpy, app->screen);
|
|
|
dbe1a1 |
app->sh = DisplayHeight(app->dpy, app->screen);
|
|
|
7e0df9 |
XRRRotations(app->dpy, app->screen, &app->sr);
|
|
|
7e0df9 |
LOGDBG("app: init: screen=%d sw=%d sh=%d sr=%02x", app->screen, app->sw, app->sh, app->sr);
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
LOGDBG("app: init: get root window");
|
|
|
dbe1a1 |
app->root = RootWindow(app->dpy, app->screen);
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
LOGDBG("app: init: create window");
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
app->x = 0;
|
|
|
4b2399 |
app->w = app->sw * WIDTH_SCALE;
|
|
|
4b2399 |
app->h = app->sh * HEIGHT_SCALE;
|
|
|
cbe4b8 |
|
|
|
cbe4b8 |
#if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
|
|
|
cbe4b8 |
if (app->sw < app->sh) {
|
|
|
56756f |
app->w = app->sw * WIDTH_SCALE_P;
|
|
|
56756f |
app->h = app->sh * HEIGHT_SCALE_P;
|
|
|
cbe4b8 |
}
|
|
|
cbe4b8 |
#endif
|
|
|
cbe4b8 |
|
|
|
0da9df |
#ifdef TOP
|
|
|
0da9df |
app->y = 0;
|
|
|
0da9df |
#else
|
|
|
dbe1a1 |
app->y = app->sh - app->h;
|
|
|
0da9df |
#endif
|
|
|
7e0df9 |
|
|
|
dbe1a1 |
XSetWindowAttributes attr = {};
|
|
|
dbe1a1 |
#ifdef NOBORDER
|
|
|
dbe1a1 |
attr.override_redirect = 1;
|
|
|
dbe1a1 |
#endif
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
app->win = XCreateWindow(
|
|
|
dbe1a1 |
app->dpy, app->root, // display and parent window
|
|
|
dbe1a1 |
app->x, app->y, app->w, app->h, 0, // position, size and border width
|
|
|
dbe1a1 |
CopyFromParent, CopyFromParent, CopyFromParent, // depth, class and visual
|
|
|
51e530 |
CWOverrideRedirect, &attr ); // mask with attributes
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
XSelectInput(
|
|
|
dbe1a1 |
app->dpy, app->win,
|
|
|
dbe1a1 |
StructureNotifyMask
|
|
|
dbe1a1 |
| ExposureMask
|
|
|
dbe1a1 |
| ButtonPressMask
|
|
|
dbe1a1 |
| ButtonReleaseMask
|
|
|
dbe1a1 |
| Button1MotionMask );
|
|
|
dbe1a1 |
|
|
|
51e530 |
#if defined(NOBORDER) || defined(DOCK)
|
|
|
51e530 |
LOGDBG("app: init: disable decorations");
|
|
|
51e530 |
Atom amh = XInternAtom(app->dpy, "_MOTIF_WM_HINTS", False);
|
|
|
51e530 |
long amhv[5] = { 2 };
|
|
|
51e530 |
XChangeProperty(app->dpy, app->win, amh, amh, 32, PropModeReplace, (unsigned char*)amhv, 5);
|
|
|
51e530 |
#endif
|
|
|
51e530 |
|
|
|
dbe1a1 |
#ifndef NOBORDER
|
|
|
c456ae |
LOGDBG("app: init: set window title");
|
|
|
dbe1a1 |
char *title = TITLE;
|
|
|
dbe1a1 |
XTextProperty wtitle;
|
|
|
dbe1a1 |
if (!XStringListToTextProperty(&title, 1, &wtitle)) {
|
|
|
dbe1a1 |
LOGWRN("app: init: XStringListToTextProperty error for title: %s", title);
|
|
|
dbe1a1 |
} else {
|
|
|
dbe1a1 |
XSetWMName(app->dpy, app->win, &wtitle);
|
|
|
dbe1a1 |
XFree(wtitle.value);
|
|
|
dbe1a1 |
}
|
|
|
7aad5f |
#endif
|
|
|
c456ae |
|
|
|
51e530 |
#ifdef DOCK
|
|
|
c456ae |
LOGDBG("app: init: set window as dock");
|
|
|
c456ae |
Atom awt = XInternAtom(app->dpy, "_NET_WM_WINDOW_TYPE", False);
|
|
|
c456ae |
Atom awtv = XInternAtom(app->dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
|
|
|
51e530 |
XChangeProperty(app->dpy, app->win, awt, XA_ATOM, 32, PropModeReplace, (unsigned char*)&awtv, 1);
|
|
|
51e530 |
Atom aaa = XInternAtom(app->dpy, "_NET_WM_ALLOWED_ACTIONS", False);
|
|
|
51e530 |
XChangeProperty(app->dpy, app->win, aaa, XA_ATOM, 32, PropModeReplace, NULL, 0);
|
|
|
dbe1a1 |
#endif
|
|
|
dbe1a1 |
|
|
|
51e530 |
Atom ahps = XInternAtom(app->dpy, "_HILDON_PORTRAIT_MODE_SUPPORT", True);
|
|
|
c456ae |
if (ahps) {
|
|
|
c456ae |
LOGDBG("app: init: set portrait mode support for hildon");
|
|
|
51e530 |
long v = 1;
|
|
|
c456ae |
XChangeProperty(app->dpy, app->win, ahps, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&v, 1);
|
|
|
c456ae |
}
|
|
|
c456ae |
|
|
|
dbe1a1 |
LOGDBG("app: init: set window minimum size");
|
|
|
dbe1a1 |
XSizeHints *wsh = XAllocSizeHints();
|
|
|
dbe1a1 |
if (!wsh) {
|
|
|
dbe1a1 |
LOGWRN("app: init: XAllocSizeHints error");
|
|
|
dbe1a1 |
} else {
|
|
|
4b2399 |
#ifdef LOCK_SIZE
|
|
|
4b2399 |
wsh->flags = PMinSize | PMaxSize;
|
|
|
4b2399 |
wsh->min_width = wsh->max_width = app->w;
|
|
|
4b2399 |
wsh->min_height = wsh->max_height = app->h;
|
|
|
4b2399 |
#else
|
|
|
dbe1a1 |
wsh->flags = PMinSize;
|
|
|
dbe1a1 |
wsh->min_width = MIN_WIDTH;
|
|
|
dbe1a1 |
wsh->min_height = MIN_HEIGHT;
|
|
|
4b2399 |
#endif
|
|
|
dbe1a1 |
XSetSizeHints(app->dpy, app->win, wsh, XA_WM_NORMAL_HINTS);
|
|
|
dbe1a1 |
XFree(wsh);
|
|
|
dbe1a1 |
}
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
// window must not grab the focus
|
|
|
dbe1a1 |
LOGDBG("app: init: set window input hint");
|
|
|
dbe1a1 |
XWMHints *wmh = XAllocWMHints();
|
|
|
dbe1a1 |
if (!wmh) {
|
|
|
dbe1a1 |
LOGWRN("app: init: XAllocWMHints error");
|
|
|
dbe1a1 |
} else {
|
|
|
dbe1a1 |
wmh->flags = InputHint;
|
|
|
dbe1a1 |
wmh->input = False;
|
|
|
dbe1a1 |
XSetWMHints(app->dpy, app->win, wmh);
|
|
|
dbe1a1 |
XFree(wmh);
|
|
|
dbe1a1 |
}
|
|
|
dbe1a1 |
|
|
|
8864eb |
// subscribe to windows delete message from window manager
|
|
|
8864eb |
app->aWmDel = XInternAtom(app->dpy, "WM_DELETE_WINDOW", False);
|
|
|
8864eb |
XSetWMProtocols(app->dpy, app->win, &app->aWmDel, 1);
|
|
|
8864eb |
|
|
|
8864eb |
// subscribe to keyboard state and layout changes
|
|
|
8864eb |
XkbSelectEvents(app->dpy, XkbUseCoreKbd, XkbMapNotifyMask | XkbStateNotifyMask, XkbMapNotifyMask | XkbStateNotifyMask);
|
|
|
8864eb |
|
|
|
e3897e |
#ifdef SCREEN_EVENTS
|
|
|
e3897e |
// subscribe to xrandr events
|
|
|
e3897e |
if (!XRRQueryExtension(app->dpy, &app->xrev, &app->xrerr)) {
|
|
|
e3897e |
LOGWRN("app: init: XRRQueryExtension error");
|
|
|
e3897e |
} else {
|
|
|
e3897e |
app->xron = 1;
|
|
|
e3897e |
XRRSelectInput(app->dpy, app->win, RRScreenChangeNotifyMask);
|
|
|
e3897e |
}
|
|
|
e3897e |
#endif
|
|
|
e3897e |
|
|
|
51e530 |
//appMove(app, app->x, app->y, app->w, app->h, 1);
|
|
|
51e530 |
//appUpdateStrut(app);
|
|
|
51e530 |
|
|
|
dbe1a1 |
// init submodules
|
|
|
7e0df9 |
touchInit(&app->touch, app, touch_dev); // touch is optional
|
|
|
dbe1a1 |
if (graphInit(&app->graph, app)) {
|
|
|
dbe1a1 |
graphResize(&app->graph);
|
|
|
dbe1a1 |
if (inputInit(&app->input, app)) {
|
|
|
dbe1a1 |
if (keyboardInit(&app->keyboard, app)) {
|
|
|
dbe1a1 |
return 1;
|
|
|
452870 |
}
|
|
|
dbe1a1 |
inputDeinit(&app->input);
|
|
|
452870 |
}
|
|
|
dbe1a1 |
graphDeinit(&app->graph);
|
|
|
452870 |
} else {
|
|
|
dbe1a1 |
XCloseDisplay(app->dpy);
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
return 0;
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
|
|
|
452870 |
void appDeinit(App *app) {
|
|
|
452870 |
LOGDBG("app: deinit");
|
|
|
452870 |
keyboardDeinit(&app->keyboard);
|
|
|
452870 |
inputDeinit(&app->input);
|
|
|
452870 |
graphDeinit(&app->graph);
|
|
|
dbe1a1 |
XCloseDisplay(app->dpy);
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
|
|
|
452870 |
int appRun(App *app) {
|
|
|
452870 |
LOGDBG("app: run");
|
|
|
452870 |
|
|
|
843e7a |
if (app->run)
|
|
|
843e7a |
return LOGERR("app: run: seems already run");
|
|
|
843e7a |
app->run = 1;
|
|
|
843e7a |
|
|
|
452870 |
LOGDBG("app: run: show window");
|
|
|
dbe1a1 |
XMapWindow(app->dpy, app->win);
|
|
|
c456ae |
appUpdateStrut(app);
|
|
|
51e530 |
appMove(app, app->x, app->y, app->w, app->h, 1);
|
|
|
452870 |
|
|
|
dbe1a1 |
LOGDBG("app: run: get connection file descriptor");
|
|
|
dbe1a1 |
int fd = ConnectionNumber(app->dpy);
|
|
|
5b6cfc |
if (fd < 0)
|
|
|
dbe1a1 |
LOGERR("app: run: cannot get XLib file descruptor");
|
|
|
5b6cfc |
|
|
|
dbe1a1 |
LOGDBG("app: run: event loop");
|
|
|
5b6cfc |
int buttonDown = 0;
|
|
|
5b6cfc |
unsigned int buttonDownMs = 0;
|
|
|
8603b4 |
|
|
|
8603b4 |
struct pollfd fds[3] = {};
|
|
|
8603b4 |
fds[0].fd = fd;
|
|
|
8603b4 |
fds[1].fd = app->stopFd;
|
|
|
8603b4 |
fds[2].fd = app->touch.fd;
|
|
|
8603b4 |
fds[0].events = fds[1].events = fds[2].events = POLLIN;
|
|
|
8603b4 |
int fdcnt = app->touch.app ? 3 : 2;
|
|
|
7e0df9 |
|
|
|
96c481 |
XFlush(app->dpy);
|
|
|
5b6cfc |
while(1) {
|
|
|
dbe1a1 |
int hasEvent = 0;
|
|
|
dbe1a1 |
XEvent event = {};
|
|
|
3ec526 |
fds[0].revents = fds[1].revents = fds[2].revents = 0;
|
|
|
5b6cfc |
|
|
|
96c481 |
// wait for next event
|
|
|
96c481 |
if (XPending(app->dpy)) {
|
|
|
96c481 |
XNextEvent(app->dpy, &event);
|
|
|
96c481 |
hasEvent = 1;
|
|
|
96c481 |
} else
|
|
|
96c481 |
if (buttonDown != 1) {
|
|
|
96c481 |
// just wait for fds
|
|
|
3ec526 |
poll(fds, fdcnt, -1);
|
|
|
5b6cfc |
} else {
|
|
|
96c481 |
// manually call select for the X-socket to use timeout
|
|
|
8603b4 |
int dt = monotonicMs() - buttonDownMs;
|
|
|
96c481 |
if (dt < LONGPRESS_MS) {
|
|
|
96c481 |
dt = LONGPRESS_MS - dt;
|
|
|
8603b4 |
LOGDBG("app: run: use poll with timeout %d ms", dt);
|
|
|
96c481 |
XFlush(app->dpy);
|
|
|
3ec526 |
poll(fds, fdcnt, dt);
|
|
|
96c481 |
}
|
|
|
96c481 |
}
|
|
|
96c481 |
|
|
|
3ec526 |
if (!hasEvent && fds[0].revents && XPending(app->dpy)) {
|
|
|
dbe1a1 |
XNextEvent(app->dpy, &event);
|
|
|
dbe1a1 |
hasEvent = 1;
|
|
|
452870 |
}
|
|
|
5b6cfc |
|
|
|
dbe1a1 |
if (hasEvent) {
|
|
|
7e0df9 |
// handle X11 events
|
|
|
dbe1a1 |
switch(event.type) {
|
|
|
dbe1a1 |
case Expose: {
|
|
|
dbe1a1 |
LOGDBG( "app: expose: x=%d, y=%d, w=%d, h=%d",
|
|
|
dbe1a1 |
event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height );
|
|
|
dbe1a1 |
appInvalidateRect(app, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height);
|
|
|
5b6cfc |
break;
|
|
|
452870 |
}
|
|
|
dbe1a1 |
case ConfigureNotify: {
|
|
|
dbe1a1 |
if ( event.xconfigure.width
|
|
|
dbe1a1 |
&& event.xconfigure.height
|
|
|
dbe1a1 |
&& ( app->x != event.xconfigure.x
|
|
|
dbe1a1 |
|| app->y != event.xconfigure.y
|
|
|
dbe1a1 |
|| app->w != event.xconfigure.width
|
|
|
dbe1a1 |
|| app->h != event.xconfigure.height ))
|
|
|
5b6cfc |
{
|
|
|
dbe1a1 |
LOGDBG( "app: moved: x=%d y=%d w=%d, h=%d",
|
|
|
dbe1a1 |
event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height );
|
|
|
dbe1a1 |
int resized = app->w != event.xconfigure.width || app->h != event.xconfigure.height;
|
|
|
dbe1a1 |
app->x = event.xconfigure.x;
|
|
|
dbe1a1 |
app->y = event.xconfigure.y;
|
|
|
dbe1a1 |
app->w = event.xconfigure.width;
|
|
|
dbe1a1 |
app->h = event.xconfigure.height;
|
|
|
5b6cfc |
if (resized) graphResize(&app->graph);
|
|
|
51e530 |
appUpdateStrut(app);
|
|
|
5b6cfc |
}
|
|
|
5b6cfc |
break;
|
|
|
452870 |
}
|
|
|
dbe1a1 |
case ButtonPress: {
|
|
|
dbe1a1 |
if (event.xbutton.button == 1) {
|
|
|
dbe1a1 |
LOGDBG("app: mouse down: x=%d, y=%d", event.xbutton.x, event.xbutton.y);
|
|
|
5b6cfc |
if (!buttonDown) { buttonDown = 1; buttonDownMs = monotonicMs(); }
|
|
|
dbe1a1 |
keyboardMouseDown(&app->keyboard, event.xbutton.x, event.xbutton.y);
|
|
|
5b6cfc |
}
|
|
|
51e530 |
appUpdateStrut(app);
|
|
|
5b6cfc |
break;
|
|
|
63daec |
}
|
|
|
dbe1a1 |
case ButtonRelease: {
|
|
|
dbe1a1 |
if (event.xbutton.button == 1) {
|
|
|
5b6cfc |
LOGDBG("app: mouse up");
|
|
|
5b6cfc |
buttonDown = 0;
|
|
|
5b6cfc |
keyboardMouseUp(&app->keyboard);
|
|
|
5b6cfc |
}
|
|
|
5b6cfc |
break;
|
|
|
5b6cfc |
}
|
|
|
dbe1a1 |
case MotionNotify: {
|
|
|
dbe1a1 |
if (event.xmotion.state & Button1Mask) {
|
|
|
dbe1a1 |
LOGDBG("app: mouse motion: x=%d, y=%d", event.xmotion.x, event.xmotion.y);
|
|
|
dbe1a1 |
keyboardMouseMotion(&app->keyboard, event.xmotion.x, event.xmotion.y);
|
|
|
5b6cfc |
}
|
|
|
452870 |
break;
|
|
|
5b6cfc |
}
|
|
|
8864eb |
case MappingNotify: {
|
|
|
8864eb |
if ( event.xmapping.request == MappingModifier
|
|
|
8864eb |
|| event.xmapping.request == MappingKeyboard )
|
|
|
8864eb |
{
|
|
|
8864eb |
LOGDBG("app: kayboard layout changed");
|
|
|
8864eb |
XRefreshKeyboardMapping(&event.xmapping);
|
|
|
8864eb |
inputUpdateLayout(
|
|
|
8864eb |
&app->input,
|
|
|
8864eb |
event.xmapping.first_keycode,
|
|
|
8864eb |
event.xmapping.first_keycode + event.xmapping.count );
|
|
|
8864eb |
}
|
|
|
8864eb |
break;
|
|
|
8864eb |
}
|
|
|
8864eb |
case ClientMessage: {
|
|
|
8864eb |
if (event.xclient.data.l[0] == app->aWmDel) {
|
|
|
8864eb |
LOGDBG("app: delete window event");
|
|
|
8864eb |
appStop(app, 0);
|
|
|
8864eb |
}
|
|
|
8864eb |
break;
|
|
|
8864eb |
}
|
|
|
e3897e |
default:
|
|
|
e3897e |
if (app->xron && event.type == app->xrev + RRScreenChangeNotify) {
|
|
|
e3897e |
LOGDBG("app: screen change event");
|
|
|
e3897e |
XRRScreenChangeNotifyEvent *ev = (XRRScreenChangeNotifyEvent*)&event;
|
|
|
7e0df9 |
appUpdateScreenSize(app, ev->width, ev->height, ev->rotation);
|
|
|
e3897e |
}
|
|
|
e3897e |
break;
|
|
|
5b6cfc |
}
|
|
|
5b6cfc |
}
|
|
|
5b6cfc |
|
|
|
3ec526 |
if (app->touch.app && fds[2].revents) {
|
|
|
3ec526 |
// handle direct touch events
|
|
|
3ec526 |
int x, y, p;
|
|
|
3ec526 |
while(touchGet(&app->touch, &x, &y, &p)) {
|
|
|
3ec526 |
if (p && !buttonDown) {
|
|
|
3ec526 |
LOGDBG("app: touch pressed: x=%d, y=%d", x, y);
|
|
|
3ec526 |
buttonDown = 1; buttonDownMs = monotonicMs();
|
|
|
3ec526 |
keyboardMouseDown(&app->keyboard, x, y);
|
|
|
3ec526 |
hasEvent = 1;
|
|
|
3ec526 |
} else
|
|
|
3ec526 |
if (!p && buttonDown) {
|
|
|
3ec526 |
LOGDBG("app: touch released");
|
|
|
3ec526 |
buttonDown = 0;
|
|
|
3ec526 |
keyboardMouseUp(&app->keyboard);
|
|
|
3ec526 |
hasEvent = 1;
|
|
|
3ec526 |
} else
|
|
|
3ec526 |
if (p) {
|
|
|
3ec526 |
LOGDBG("app: touch motion: x=%d, y=%d", x, y);
|
|
|
3ec526 |
keyboardMouseMotion(&app->keyboard, x, y);
|
|
|
3ec526 |
hasEvent = 1;
|
|
|
3ec526 |
}
|
|
|
3ec526 |
}
|
|
|
3ec526 |
}
|
|
|
3ec526 |
|
|
|
5b6cfc |
if (buttonDown == 1 && monotonicMs() - buttonDownMs >= LONGPRESS_MS) {
|
|
|
5b6cfc |
LOGDBG("app: long press");
|
|
|
5b6cfc |
buttonDown = 2;
|
|
|
5b6cfc |
keyboardMouseLongDown(&app->keyboard);
|
|
|
3ec526 |
hasEvent = 1;
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
843e7a |
if (app->run != 1) break;
|
|
|
3ec526 |
if (!hasEvent) continue;
|
|
|
843e7a |
|
|
|
061fcf |
inputUpdateModifiers(&app->input);
|
|
|
061fcf |
keyboardResize(&app->keyboard);
|
|
|
061fcf |
keyboardUpdateModifiers(&app->keyboard);
|
|
|
452870 |
if (app->irw > 0 && app->irh > 0) {
|
|
|
452870 |
LOGDBG("app: draw: x=%d, y=%d, w=%d, h=%d", app->irx, app->iry, app->irw, app->irh);
|
|
|
452870 |
graphDrawBackgound(&app->graph, app->irx, app->iry, app->irw, app->irh);
|
|
|
452870 |
keyboardDraw(&app->keyboard, app->irx, app->iry, app->irw, app->irh);
|
|
|
452870 |
app->irw = 0;
|
|
|
452870 |
}
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
843e7a |
int err = app->run == -2;
|
|
|
843e7a |
app->run = 0;
|
|
|
843e7a |
LOGDBG("app: run: done err=%d", err);
|
|
|
843e7a |
return !err;
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
|
|
|
96c481 |
void appStopBySignal(App *app) {
|
|
|
96c481 |
char x = 0;
|
|
|
96c481 |
write(app->stopFd, &x, 1);
|
|
|
96c481 |
}
|
|
|
96c481 |
|
|
|
96c481 |
|
|
|
843e7a |
void appStop(App *app, int err) {
|
|
|
843e7a |
LOGDBG("app: stop: err=%d", err);
|
|
|
843e7a |
if (!app->run) {
|
|
|
843e7a |
LOGERR("app: stop: seems not started");
|
|
|
843e7a |
return;
|
|
|
843e7a |
}
|
|
|
843e7a |
if (app->run == 1) app->run = -1;
|
|
|
843e7a |
if (err) app->run = -2;
|
|
|
843e7a |
}
|
|
|
843e7a |
|
|
|
843e7a |
|
|
|
c456ae |
void appUpdateStrut(App *app) {
|
|
|
c456ae |
#ifdef DOCK
|
|
|
51e530 |
long v[12] = { 0, 0, 0, app->h, 0, app->sh, 0, app->sh, 0, app->sw, 0, app->sw };
|
|
|
51e530 |
|
|
|
51e530 |
#ifdef TOP
|
|
|
51e530 |
v[2] = app->h;
|
|
|
51e530 |
#else
|
|
|
51e530 |
v[3] = app->h;
|
|
|
c456ae |
#endif
|
|
|
c456ae |
|
|
|
c456ae |
if (app->dockt == v[2] && app->dockb == v[3])
|
|
|
c456ae |
return;
|
|
|
c456ae |
app->dockt = v[2];
|
|
|
c456ae |
app->dockb = v[3];
|
|
|
c456ae |
|
|
|
51e530 |
LOGDBG("app: update strut: %lu %lu %lu %lu", v[0], v[1], v[2], v[3]);
|
|
|
7e0df9 |
|
|
|
c456ae |
Atom k = XInternAtom(app->dpy, "_NET_WM_STRUT", False);
|
|
|
51e530 |
XChangeProperty(app->dpy, app->win, k, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)v, 4);
|
|
|
c456ae |
|
|
|
51e530 |
Atom kp = XInternAtom(app->dpy, "_NET_WM_STRUT_PARTIAL", False);
|
|
|
51e530 |
XChangeProperty(app->dpy, app->win, kp, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)v, 12);
|
|
|
51e530 |
|
|
|
51e530 |
#ifdef TOP
|
|
|
51e530 |
appMove(app, 0, 0, app->sw, app->h, 1);
|
|
|
51e530 |
#else
|
|
|
51e530 |
appMove(app, 0, app->sh - app->h, app->sw, app->h, 1);
|
|
|
51e530 |
#endif
|
|
|
51e530 |
#endif // DOCK
|
|
|
c456ae |
}
|
|
|
c456ae |
|
|
|
c456ae |
|
|
|
7e0df9 |
void appUpdateScreenSize(App *app, int sw, int sh, Rotation sr) {
|
|
|
e3897e |
if (sw <= 0 || sh <= 0) return;
|
|
|
c456ae |
|
|
|
7e0df9 |
if (sw == app->sw && sh == app->sh && sr == app->sr) return;
|
|
|
7e0df9 |
LOGDBG("app: update screen size: w=%d, h=%d, r=%d", sw, sh, sr);
|
|
|
7e0df9 |
|
|
|
7e0df9 |
app->sr = sr;
|
|
|
e3897e |
if (sw == app->sw && sh == app->sh) return;
|
|
|
cbe4b8 |
|
|
|
460d1a |
#if defined(DOCK) || (defined(LOCK_SIZE) && defined(NOBORDER) && defined(NOTITLE))
|
|
|
56756f |
// static size case
|
|
|
56756f |
int w = sw * WIDTH_SCALE;
|
|
|
56756f |
int h = sh * HEIGHT_SCALE;
|
|
|
56756f |
#if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
|
|
|
56756f |
if (sw < sh) {
|
|
|
56756f |
w = sw * WIDTH_SCALE_P;
|
|
|
56756f |
h = sh * HEIGHT_SCALE_P;
|
|
|
56756f |
}
|
|
|
56756f |
#endif
|
|
|
56756f |
|
|
|
56756f |
int x0 = 0;
|
|
|
56756f |
#ifdef TOP
|
|
|
56756f |
int y0 = 0;
|
|
|
56756f |
#else
|
|
|
56756f |
int y0 = sh - h;
|
|
|
56756f |
#endif
|
|
|
56756f |
|
|
|
56756f |
#else
|
|
|
56756f |
// dynamic size case
|
|
|
cbe4b8 |
double kx = sw/(double)app->sw;
|
|
|
cbe4b8 |
double ky = sh/(double)app->sh;
|
|
|
cbe4b8 |
|
|
|
cbe4b8 |
#if defined(WIDTH_SCALE_P) && defined(HEIGHT_SCALE_P)
|
|
|
cbe4b8 |
// rescale for portrait/landscape switching
|
|
|
cbe4b8 |
if (app->sw < app->sh && sw >= sh) {
|
|
|
cbe4b8 |
// portrait -> landscape
|
|
|
cbe4b8 |
kx *= ((double)WIDTH_SCALE)/((double)WIDTH_SCALE_P);
|
|
|
cbe4b8 |
ky *= ((double)HEIGHT_SCALE)/((double)HEIGHT_SCALE_P);
|
|
|
cbe4b8 |
} else
|
|
|
cbe4b8 |
if (app->sw >= app->sh && sw < sh) {
|
|
|
cbe4b8 |
// landscape -> portrait
|
|
|
cbe4b8 |
kx *= ((double)WIDTH_SCALE_P)/((double)WIDTH_SCALE);
|
|
|
cbe4b8 |
ky *= ((double)HEIGHT_SCALE_P)/((double)HEIGHT_SCALE);
|
|
|
cbe4b8 |
}
|
|
|
cbe4b8 |
#endif
|
|
|
cbe4b8 |
|
|
|
cbe4b8 |
// scale window corners
|
|
|
cbe4b8 |
int x0 = (int)round( app->x*kx );
|
|
|
cbe4b8 |
int y0 = (int)round( app->y*ky );
|
|
|
cbe4b8 |
int x1 = (int)round( (app->x + app->w)*kx );
|
|
|
cbe4b8 |
int y1 = (int)round( (app->y + app->h)*ky );
|
|
|
56756f |
int w = x1 - x0;
|
|
|
56756f |
int h = y1 - y0;
|
|
|
56756f |
#endif
|
|
|
cbe4b8 |
|
|
|
e3897e |
app->sw = sw;
|
|
|
e3897e |
app->sh = sh;
|
|
|
51e530 |
appMove(app, x0, y0, w, h, 0);
|
|
|
e3897e |
}
|
|
|
e3897e |
|
|
|
e3897e |
|
|
|
51e530 |
void appMove(App *app, int x, int y, int w, int h, int force) {
|
|
|
db2d04 |
if (w < MIN_WIDTH) w = MIN_WIDTH;
|
|
|
db2d04 |
if (h < MIN_HEIGHT) h = MIN_HEIGHT;
|
|
|
dbe1a1 |
|
|
|
dbe1a1 |
unsigned int mask = 0;
|
|
|
dbe1a1 |
XWindowChanges ch = {};
|
|
|
51e530 |
if (force || app->x != x) ch.x = x, mask |= CWX;
|
|
|
51e530 |
if (force || app->y != y) ch.y = y, mask |= CWY;
|
|
|
51e530 |
if (force || app->w != w) ch.width = w, mask |= CWWidth;
|
|
|
51e530 |
if (force || app->h != h) ch.height = h, mask |= CWHeight;
|
|
|
dbe1a1 |
if (!mask) return;
|
|
|
dbe1a1 |
|
|
|
51e530 |
LOGDBG("app: move: x=%d, y=%d, w=%d, h=%d", x, y, w, h);
|
|
|
dbe1a1 |
XConfigureWindow(app->dpy, app->win, mask, &ch);
|
|
|
db2d04 |
XFlush(app->dpy);
|
|
|
452870 |
}
|
|
|
452870 |
|
|
|
452870 |
|
|
|
452870 |
void appInvalidateRect(App *app, int x, int y, int w, int h) {
|
|
|
452870 |
LOGDBG("app: invalidate rect: x=%d, y=%d, w=%d, h=%d", x, y, w, h);
|
|
|
452870 |
rectIntersect(&x, &y, &w, &h, 0, 0, app->w, app->h);
|
|
|
452870 |
rectMerge(&app->irx, &app->iry, &app->irw, &app->irh, x, y, w, h);
|
|
|
d3e9d7 |
LOGDBG("app: invalidate rect: summary x=%d, y=%d, w=%d, h=%d", app->irx, app->iry, app->irw, app->irh);
|
|
|
452870 |
}
|
|
|
452870 |
|