/* audio volume control for x11 (c) firk 2020-2021 please respect the authorship feel free to use this code for anything */ #include #include #include #include #include #include #include #include "mixer.h" Display * display; Screen * screen; static winprops wp0; static Window rootwin, miniwin; static unsigned long colors[16]; static GC gc; static int X0, Y0; static unsigned int W0, H0; static int sw, sh, redraw_needed; 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)) { fprintf(stderr, "color[%d] alloc failed\n", n); exit(-1); } colors[n] = c.pixel; } } static void free_colors(void) { XFreeColors(display, DefaultColormapOfScreen(screen), colors, 16, 0); } extern unsigned long base_color(unsigned char c) { return colors[c&15]; } static int calc_sizes(int _sw, int _sh) { int x, y; sw = _sw; sh = _sw; if((x=wp0.x0)<0) x = x+sw-wp0.w+1; if((y=wp0.y0)<0) y = y+sh-wp0.h+1; if(x==X0 && y==Y0 && wp0.w==(int)W0 && wp0.h==(int)H0) return 0; W0 = wp0.w; H0 = wp0.h; X0 = x; Y0 = y; return 1; } extern void x11_init(char const * display_name, winprops *wp) { XSetWindowAttributes wa; if(!(display = XOpenDisplay(display_name))) { fprintf(stderr, "fwm-mixer: XOpenDisplay(%s) failed\n", display_name); exit(-1); } if(!(screen = DefaultScreenOfDisplay(display))) { fprintf(stderr, "fwm-mixer: DefaultScreenOfDisplay() failed\n"); exit(-1); } wp0 = *wp; init_colors(); if(wp0.w<=0) wp0.w = 100; if(wp0.h<=0) wp0.h = 10; if(wp0.w>10000) wp0.w = 10000; if(wp0.h>10000) wp0.h = 10000; if(wp0.bw<0) wp0.bw = 0; if(wp0.bw>100) wp0.bw = 100; calc_sizes(WidthOfScreen(screen), HeightOfScreen(screen)); rootwin = DefaultRootWindow(display); wa.override_redirect = wp0.o?True:False; wa.event_mask = ExposureMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|LeaveWindowMask; wa.background_pixel = base_color(0); wa.border_pixel = base_color(15); miniwin = XCreateWindow(display, rootwin, X0, Y0, W0, H0, wp0.bw, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect|CWEventMask|CWBackPixel|CWBorderPixel, &wa); #if 0 miniwin = XCreateSimpleWindow(display, rootwin, 0, 0, W0, H0, 0/*border*/, base_color(15), base_color(0)); XChangeWindowAttributes(display, miniwin, CWOverrideRedirect|CWEventMask, &wa); #endif XSetStandardProperties(display, miniwin, "MIXER", "MIXER", None, NULL, 0, NULL); // TODO: is this same as wa.event_mask ? XSelectInput(display, miniwin, ExposureMask| // window redraw requests ButtonPressMask|ButtonReleaseMask| // mouse buttons click KeyPressMask|KeyReleaseMask|KeymapStateMask| // keyboard clicks / resync pressed state on getting focus PointerMotionMask| // mouse moving StructureNotifyMask // window moving/resizing ); XSelectInput(display, rootwin, StructureNotifyMask); gc = XCreateGC(display, miniwin, 0, NULL); XSetForeground(display, gc, base_color(15)); XSetBackground(display, gc, base_color(0)); #ifdef TASKBAR_FONT_NAME XSetFont(display, gc, XLoadFont(display, TASKBAR_FONT_NAME)); // TODO? #endif XClearWindow(display, miniwin); XMapRaised(display, miniwin); XSync(display, False); } static void do_redraw(void) { redraw_needed = 0; if(W0<3 || H0<3) return; XSetForeground(display, gc, base_color(0)); XFillRectangle(display, miniwin, gc, 0, 0, W0, H0); XSetForeground(display, gc, base_color(mute?7:15)); XFillRectangle(display, miniwin, gc, 1, 1, (W0-2)*volume/1000, H0-2); XSetForeground(display, gc, base_color(15)); XDrawRectangle(display, miniwin, gc, 0, 0, W0-1, H0-1); if(mute) { XDrawLine(display, miniwin, gc, 0, 0, W0-1, H0-1); XDrawLine(display, miniwin, gc, 0, H0-1, W0-1, 0); } } static unsigned int int_to_usize(int v) { if(v<=0) return 0; if(v>=1000) return 1000; return (unsigned int)v; } extern void x11_redraw(void) { redraw_needed = 1; } extern void x11_event_cycle(int do_wait) { XEvent ev; if(do_wait || XPending(display)) { XNextEvent(display, &ev); switch(ev.type) { case ConfigureNotify: if(ev.xconfigure.window==rootwin) { if(calc_sizes(ev.xconfigure.width, ev.xconfigure.height)) { XMoveWindow(display, miniwin, X0, Y0); } } if(ev.xconfigure.window==miniwin) { W0 = int_to_usize(ev.xconfigure.width); H0 = int_to_usize(ev.xconfigure.height); redraw_needed = 1; } break; case Expose: if(ev.xexpose.window==miniwin && ev.xexpose.count==0) redraw_needed = 1; break; case ButtonPress: if(W0<2 || ev.xbutton.x<0) return; ev.xbutton.x = ((unsigned long)ev.xbutton.x)*100/(W0-1); } key_hook_handler(&ev); } if(redraw_needed) do_redraw(); }