/* * This file is part of firk's window manager library (libfwm) * Copyright (C) 2023 firk * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #define LIBFWM_INTERNAL #define LIBFWM_INTERNAL_2 #include "libfwm.h" Display *display; Screen *screen; Window rootwin; Atom atoms[atoms_length]; unsigned long colors[16]; static char logprefix[32]; extern void logprint(char const * fmt, ...) { char buf[110]; va_list arg; va_start(arg, fmt); vsnprintf(buf, sizeof(buf), fmt, arg); va_end(arg); fprintf(stderr, "%s%s\n", logprefix, buf); } extern void failprint(char const * fmt, ...) { char buf[110]; va_list arg; va_start(arg, fmt); vsnprintf(buf, sizeof(buf), fmt, arg); va_end(arg); fprintf(stderr, "%s(fatal) %s\n", logprefix, buf); exit(-1); } static void init_colors(void) { XColor c; unsigned int n; for(n=0;n<16;n++) { c.flags = DoRed|DoGreen|DoBlue; c.red = ((n&8)?0x5500:0) + ((n&4)?0xAA00:0); c.green = ((n&8)?0x5500:0) + ((n&2)?0xAA00:0); c.blue = ((n&8)?0x5500:0) + ((n&1)?0xAA00:0); if(n==6) c.green=0x5500; if(!XAllocColor(display, DefaultColormapOfScreen(screen), &c)) logprint("color[%d] alloc failed", n); colors[n] = c.pixel; } } static void free_colors(void) { XFreeColors(display, DefaultColormapOfScreen(screen), colors, 16, 0); } extern void LIBFWM_Init(char const *program_name, char const *display_name) { size_t n; if(program_name && (n=strlen(program_name))) { if(n>sizeof(logprefix)-3) n = sizeof(logprefix)-3; bcopy(program_name, logprefix, n); logprefix[n++] = ':'; logprefix[n++] = ' '; logprefix[n++] = 0; } else logprefix[0] = 0; if(!display && !(display = XOpenDisplay(display_name))) failprint("XOpenDisplay(%s) failed", display_name); if(!(screen = DefaultScreenOfDisplay(display))) failprint("DefaultScreenOfDisplay() failed"); rootwin = RootWindowOfScreen(screen); init_colors(); if(!XInternAtoms(display, (char**)atom_names, atoms_length, False, atoms)) failprint("XInternAtoms() failed"); } extern unsigned long base_color(unsigned char c) { return colors[c&15]; } static int set_atom_string(Window w, Atom atom, char const *st, int len) { return XChangeProperty(display, w, atom, atom, 8, PropModeReplace, (unsigned char const*)st, len); } extern int LIBFWM_SetWClass(Window w, unsigned char cat, char const *wcl) { char tmp[32]; size_t len; tmp[0] = cat; if(!wcl) len = 0; else { len = strnlen(wcl, 31); bcopy(wcl, tmp+1, len); } return set_atom_string(w, atom_FWM_WCLASS, tmp, len+1); } extern Window LIBFWM_CreateTrayIcon(int is_wide, char const *class, unsigned long background_pixel, long event_mask) { XSetWindowAttributes wa; Window win; if(!background_pixel) background_pixel = base_color(BLACK); if(event_mask==-1) event_mask = ExposureMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|LeaveWindowMask; bzero(&wa, sizeof(wa)); wa.background_pixel = background_pixel; wa.override_redirect = False; /* may set to True when FWM missing, but need to manage x,y manually */ wa.event_mask = event_mask; win = XCreateWindow(display, rootwin, 0, 0, is_wide?42:16, 16, 0, /* dummy x0,y0,w,h,bwidth */ CopyFromParent, InputOutput, CopyFromParent, CWBackPixel|CWOverrideRedirect|CWEventMask, &wa); if(!LIBFWM_SetWClass(win, is_wide?WCLASS_TRAYWIDE:WCLASS_TRAYICON, class)) logprint("CreateTrayIcon():SetWClass() failed"); XSetStandardProperties(display, win, class, class, None, NULL, 0, NULL); return win; } extern Window LIBFWM_CreateWidgetWindow(unsigned char type, char const *class, char const *title, int x0, int y0, unsigned int w, unsigned int h, unsigned int bw, unsigned long background_pixel, unsigned long border_pixel, long event_mask, Bool override_redirect) { XSetWindowAttributes wa; Window win; if(!background_pixel) background_pixel = base_color(BLACK); if(event_mask==-1) event_mask = ExposureMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|LeaveWindowMask|StructureNotifyMask; bzero(&wa, sizeof(wa)); wa.background_pixel = background_pixel; wa.border_pixel = border_pixel; wa.override_redirect = override_redirect; wa.event_mask = event_mask; win = XCreateWindow(display, rootwin, x0, y0, w, h, bw, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel|CWBorderPixel|CWOverrideRedirect|CWEventMask, &wa); if(!LIBFWM_SetWClass(win, type, class)) logprint("CreateWidgetWindow():SetWClass() failed"); XSetStandardProperties(display, win, title, title, None, NULL, 0, NULL); return win; } extern unsigned long get_ms(void) { static int mode; struct timeval tv; struct timespec ts; switch(mode) { case 0: if(clock_gettime(CLOCK_MONOTONIC, &ts)>=0) { mode = 1; return ((unsigned long)ts.tv_sec)*1000+((unsigned long)ts.tv_nsec)/1000000; } if(gettimeofday(&tv, NULL)>=0) { mode = 2; return ((unsigned long)tv.tv_sec)*1000+((unsigned long)tv.tv_usec)/1000; } mode = 3; break; case 1: clock_gettime(CLOCK_MONOTONIC, &ts); return ((unsigned long)ts.tv_sec)*1000+((unsigned long)ts.tv_nsec)/1000000; case 2: gettimeofday(&tv, NULL); return ((unsigned long)tv.tv_sec)*1000+((unsigned long)tv.tv_usec)/1000; } return ((unsigned long)time(NULL))*1000; } static int poll_in(unsigned long timeout) { struct pollfd pfd; int r, to, er; er = 0; to = timeout; while(to<0 || timeout!=(unsigned)to) { er=1; timeout/=2; to=timeout; } pfd.fd = ConnectionNumber(display); pfd.events = POLLIN; pfd.revents = 0; r = poll(&pfd, 1, timeout); if(!r && er) { errno=EINTR; return -1; } if(r<=0) return r; if(pfd.revents & (POLLIN|POLLHUP)) return 2; return 1; /* <- most likely this shouldn't happen */ } /* this was just socket waiting code, but it is not what we need static int poll_nointr(unsigned long until) { unsigned long diff; int r; while(1) { diff = until - get_ms(); if(((long)diff)<0) diff = 0; r = poll_in(diff); if(r==2) return 2; if(!diff) return 0; } } extern int LIBFWM_WaitEventTimeout(unsigned int timeout, int nointr) { if(!nointr) return poll_in(timeout); return poll_nointr(get_ms()+timeout); } extern int LIBFWM_WaitEventUntil(unsigned long until, int nointr) { unsigned long diff; if(!nointr) { diff = until - get_ms(); if(((long)diff)<0) diff = 0; return poll_in(diff); } return poll_nointr(until); } */ static int poll_nointr(unsigned long now, unsigned long until) { unsigned long diff; int r; while(1) { diff = until - now; if(((long)diff)<=0) break; r = poll_in(diff); if(r<0 && errno!=EINTR) usleep(10000); if(XPending(display)) return 1; now = get_ms(); } if(XPending(display)) return 1; return 0; } extern int LIBFWM_WaitEventTimeout(unsigned int timeout, int nointr) { unsigned long now, until, diff; now = get_ms(); until = now + timeout; if(XPending(display)) return 1; if(!timeout) return 0; if(!nointr) { diff = until - get_ms(); if(((long)diff)>0) poll_in(diff); if(XPending(display)) return 1; return 0; } now = get_ms(); return poll_nointr(now, now+timeout); } extern int LIBFWM_WaitEventUntil(unsigned long until, int nointr) { unsigned long now, diff; diff = until - get_ms(); if(XPending(display)) return 1; if(((long)diff)<=0) return 0; if(!nointr) { diff = until - get_ms(); if(((long)diff)>0) poll_in(diff); if(XPending(display)) return 1; return 0; } now = get_ms(); return poll_nointr(now, until); }