/* audio volume control for x11 (c) firk 2020-2023 please respect the authorship feel free to use this code for anything */ #include #include #include #include #include #include #include #include "mixer.h" static winprops wp0; static Window miniwin; static GC gc; static int X0, Y0; static unsigned int W0, H0; static int sw, sh, redraw_needed; 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(winprops const *wp) { XSetWindowAttributes wa; wp0 = *wp; if(wp0.o<2) { 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)); } else { W0 = 100; H0 = 10; } wa.override_redirect = (wp0.o==1)?True:False; wa.event_mask = 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 wa.background_pixel = base_color(BLACK); wa.border_pixel = base_color(WHITE); miniwin = XCreateWindow(display, rootwin, X0, Y0, W0, H0, wp0.bw, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect|CWEventMask|CWBackPixel|CWBorderPixel, &wa); if(!LIBFWM_SetWClass(miniwin, WCLASS_WIDGET, "fwm-mixer")) logprint("SetWClass() failed"); XSetStandardProperties(display, miniwin, "MIXER", "MIXER", None, NULL, 0, NULL); XSelectInput(display, rootwin, StructureNotifyMask); gc = XCreateGC(display, miniwin, 0, NULL); XSetForeground(display, gc, base_color(WHITE)); XSetBackground(display, gc, base_color(BLACK)); #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(wp0.o<2 && 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))*10; } key_hook_handler(&ev); } if(redraw_needed) do_redraw(); }