├── .gitignore ├── CHANGES ├── COPYING ├── README.md ├── auto ├── config.mk ├── configure ├── configure.mk ├── makefile ├── tests.mk └── tests │ ├── test_asan.c │ ├── test_cairo_lib.c │ ├── test_cc.c │ ├── test_fc_lib.c │ ├── test_ft_lib.c │ ├── test_x11_lib.c │ ├── test_xcb-ewmh_lib.c │ ├── test_xcb-icccm_lib.c │ ├── test_xcb-randr_lib.c │ ├── test_xcb-util_lib.c │ ├── test_xcb_lib.c │ └── test_xlib_xcb_lib.c ├── conf └── rieman.conf ├── doc ├── rieman.1 ├── s1.png └── s2.png ├── pkg ├── docker │ └── Dockerfile └── rpm │ └── rieman.spec ├── scripts ├── migrate_to_conf.sh └── xml2conf.sh ├── skins ├── blue │ ├── missing_icon.png │ └── rieman_skin.conf ├── bordered │ ├── desktop-active-border.png │ ├── missing_icon.png │ ├── pager-border.png │ ├── rieman_skin.conf │ ├── viewport-active-border.png │ ├── viewport-border.png │ ├── window-active-border.png │ ├── window-attention-border.png │ └── window-border.png ├── default │ ├── missing_icon.png │ └── rieman_skin.conf ├── light │ ├── missing_icon.png │ └── rieman_skin.conf └── transparent │ ├── missing_icon.png │ ├── rieman_skin.conf │ ├── viewport-active-border.png │ └── viewport-border.png ├── src ├── rie_config.c ├── rie_config.h ├── rie_control.c ├── rie_control.h ├── rie_event.c ├── rie_event.h ├── rie_external.c ├── rie_external.h ├── rie_font.c ├── rie_font.h ├── rie_gfx.h ├── rie_gfx_cairo.c ├── rie_log.c ├── rie_log.h ├── rie_render.c ├── rie_render.h ├── rie_skin.c ├── rie_skin.h ├── rie_test.c ├── rie_util.c ├── rie_util.h ├── rie_window.c ├── rie_window.h ├── rie_xcb.c ├── rie_xcb.h ├── rieman.c └── rieman.h └── tests ├── conf ├── bad-2.conf ├── bad-key-2.conf ├── bad-key-3.conf ├── bad-key-4.conf ├── bad-key-5.conf ├── bad-key-validate.conf ├── bad-key.conf ├── bad.conf ├── c1.conf ├── c2.conf ├── c3.conf ├── default.conf ├── noroot.conf ├── skin-1.conf ├── skin-2.conf ├── skin-3.conf ├── skin-bad-key-2.conf ├── skin-bad-key-3.conf └── skin-bad-key.conf ├── rieman_tests.py └── skins ├── skin-1 ├── img.png ├── missing_icon.png └── rieman_skin.conf ├── skin-3 ├── img.png ├── missing_icon.png └── rieman_skin.conf ├── skin-bad-key-2 ├── img.png ├── missing_icon.png └── rieman_skin.conf ├── skin-bad-key-3 ├── img.png ├── missing_icon.png └── rieman_skin.conf └── skin-bad-key ├── img.png ├── missing_icon.png └── rieman_skin.conf /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | version 1.2.0: 2021-01-03 2 | 3 | * changed configuration file format due to removing libxml dependency 4 | now files use '.conf' extension and use simple key-value syntax. 5 | use scripts/migrate_to_conf.sh to convert existing configuration files 6 | into new format 7 | 8 | + Tiling support: for WMs lacking it 9 | Since rieman knows about all windows and can send commands to them, 10 | it was simple to add 'tile windows on click at specific desktop' 11 | feature. See man for details. 12 | 13 | + Control socket support 14 | you can send commands to running instance via UNIX socket. 15 | Everything that could be done with signals and more. 16 | More commands are expected in future versions. 17 | Why? Not all WMs can deal with 2D-desktops grid and provide commands 18 | to switch desktop in horizontal/vertical direction. 19 | 20 | + NET_WM_STRUT family of atoms are supported. Rieman may now be configured 21 | to reserve space on screen in compatible WMs. 22 | Now it is also possible to set window type to 'dock'. 23 | Window position handling is improved. 24 | 25 | + X11 screen to use is now configurable 26 | 27 | + Root window geometry is now tracked 28 | 29 | + Psedo-transparency works properly with multiple monitors 30 | 31 | * Lot of various small bugfixes 32 | 33 | version 1.1.0: was never released 34 | * version skipped due to breaking changes introduced 35 | 36 | version 1.0.0: 2017-12-26 37 | + bugfixes and cleanup 38 | 39 | version 0.9.1: 2017-12-24 40 | * first released version 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rieman - X11 desktop pager 2 | ========================== 3 | 4 | The desktop pager is a progam that: 5 | 6 | * configures layout of virtual desktops 7 | * displays virtual desktops and windows on it 8 | * switches between desktops/windows per user requests 9 | 10 | ![Default theme](doc/s1.png?raw=true "Default theme") | ![Transparent theme](doc/s2.png?raw=true "Transparent theme") 11 | 12 | INSTALLATION 13 | ------------ 14 | 15 | ### Dependencies 16 | 17 | You will need build tools: 18 | 19 | * c99 compiler (gcc and clang tested) 20 | * gnu make 21 | * pkg-config to detect libraries 22 | 23 | and some libraries: 24 | 25 | * X11 - for X11, as a core dependency 26 | * xcb - almost all work with X11 and NETWM spec, many of them: 27 | * xlib-xcb 28 | * xcb-util 29 | * xcb-ewmh 30 | * xcb-icccm 31 | * xcb-randr 32 | * cairo - 2D drawing 33 | * fontconfig - find font by name 34 | * freetype2 - work with fonts itself 35 | 36 | also, you will need some fonts, for example: 37 | 38 | * media-fonts/droid 39 | 40 | fonts from the package are mentioned in a default configuration file; 41 | feel free to use anything you like/have available on your system. 42 | 43 | ### Configuring: 44 | 45 | ``` 46 | $ ./auto/configure [--prefix=] [--help] ... 47 | ``` 48 | 49 | Various additional options are accepted, like compiler options and data 50 | directories, refer to the builtin help for details. 51 | 52 | ### Building: 53 | 54 | ``` 55 | $ make 56 | ``` 57 | 58 | ### Installing 59 | 60 | ``` 61 | $ make install 62 | ``` 63 | 64 | this will install software to directories, previously configured. 65 | To remove, just run 66 | ``` 67 | $ make uninstall 68 | ``` 69 | 70 | you will be prompted before deleting anything. 71 | 72 | RTFM: 73 | ----- 74 | 75 | The rieman comes with a man page: 76 | 77 | ``` 78 | $ man rieman 79 | ``` 80 | -------------------------------------------------------------------------------- /auto/config.mk: -------------------------------------------------------------------------------- 1 | CFLAGS=-Wall -Werror -g -ggdb -Ibuild/ 2 | LDFLAGS= 3 | 4 | ASAN_CFLAGS=-fsanitize=address 5 | ASAN_LDFLAGS=-fsanitize=address 6 | 7 | # initial set of libraries, others are added by autotests 8 | LIBS=-lm 9 | 10 | .PHONY: rieman 11 | 12 | rieman: build/rieman 13 | -------------------------------------------------------------------------------- /auto/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | help() 4 | { 5 | cat <> build/config.mk </dev/null 2>&1 165 | if [ $? -eq 127 ]; then 166 | echo "pkg-config binary is not found in PATH" 167 | echo "pkg-config is required to detect libraries, please install" 168 | exit 1 169 | fi 170 | 171 | # actual autotests are implemented in make: 172 | make -f auto/configure.mk -j4 makefile 173 | 174 | -------------------------------------------------------------------------------- /auto/configure.mk: -------------------------------------------------------------------------------- 1 | include build/config.mk 2 | 3 | testbins := 4 | include auto/tests.mk 5 | 6 | ifeq ($(ENABLE_ASAN),yes) 7 | $(eval $(call test-compiler-flags,test_asan,$(ASAN_CFLAGS),$(ASAN_LDFLAGS))) 8 | endif 9 | 10 | $(eval $(call pkg-test,test_x11_lib,x11,X11)) 11 | $(eval $(call pkg-test,test_xlib_xcb_lib,x11-xcb,XLIB_XCB)) 12 | $(eval $(call pkg-test,test_xcb_lib,xcb,XCB)) 13 | $(eval $(call pkg-test,test_xcb-util_lib,xcb-util,XCB_UTIL)) 14 | $(eval $(call pkg-test,test_xcb-icccm_lib,xcb-icccm,XCB_ICCCM)) 15 | $(eval $(call pkg-test,test_xcb-ewmh_lib,xcb-ewmh,XCB_EWMH)) 16 | $(eval $(call pkg-test,test_xcb-randr_lib,xcb-randr,XCB_RANDR)) 17 | $(eval $(call pkg-test,test_cairo_lib,cairo,CAIRO)) 18 | $(eval $(call pkg-test,test_fc_lib,fontconfig,FONTCONFIG)) 19 | $(eval $(call pkg-test,test_ft_lib,freetype2,FREETYPE)) 20 | 21 | result=$(if $(findstring yes,$1),$2: yes,$2: no) 22 | oresult=$(if $1,$2: $1) 23 | 24 | $(testbins): build/test_cc 25 | 26 | makefile: $(testbins) 27 | @cp auto/makefile $@ 28 | @echo "" 29 | @echo "--- rieman configured succesfuly ---" 30 | @echo " prefix: $(PREFIX)" 31 | @echo " $(call result,$(TESTS),tests)" 32 | @echo " $(call result,$(DEBUG),debug)" 33 | @echo " $(call result,$(COVERAGE),coverage)" 34 | @echo " $(call oresult,$(CC),compiler)" 35 | @echo " $(call result,$(ENABLE_ASAN),enable asan)" 36 | 37 | -------------------------------------------------------------------------------- /auto/makefile: -------------------------------------------------------------------------------- 1 | include build/config.mk 2 | include build/test_*.mk 3 | 4 | .PHONY: install uninstall clean distclean test coverage 5 | 6 | VPATH = src/ 7 | 8 | SRCS:=src/rie_event.c \ 9 | src/rie_log.c \ 10 | src/rie_window.c \ 11 | src/rie_skin.c \ 12 | src/rie_render.c \ 13 | src/rie_gfx_cairo.c \ 14 | src/rie_external.c \ 15 | src/rieman.c \ 16 | src/rie_config.c \ 17 | src/rie_xcb.c \ 18 | src/rie_util.c \ 19 | src/rie_font.c \ 20 | src/rie_control.c 21 | 22 | ifeq ($(DEBUG),yes) 23 | # for readable cores 24 | CFLAGS += -O0 25 | # for backtraces 26 | LDFLAGS += -rdynamic 27 | else 28 | CFLAGS += -O2 29 | endif 30 | 31 | ifeq ($(TESTS),yes) 32 | LIBS += -lpthread 33 | SRCS += src/rie_test.c 34 | endif 35 | 36 | ifeq ($(COVERAGE),yes) 37 | CFLAGS += -fprofile-arcs -ftest-coverage 38 | LIBS += -lgcov 39 | endif 40 | 41 | DEPFLAGS = -MT $@ -MMD -MP -MF build/$*.Td 42 | POSTCOMPILE = mv -f build/$*.Td build/$*.d 43 | 44 | src-to-obj = $(subst src/,build/,$(subst .c,.o,$(filter %.c,$1))) 45 | src-to-dep = $(subst src/,build/,$(subst .c,.d,$(filter %.c,$1))) 46 | 47 | # intentionally simplistic, don't expected to work everywhere 48 | OS:=$(shell uname -r -o -m) 49 | 50 | # if building from a cloned sources, revision is embedded 51 | REV=$(shell if [ -d .git ]; then git describe --tags; else echo "pkg"; fi) 52 | 53 | build/config.h: $(testbins) 54 | @echo "#ifndef __CONFIG_H__" > $@ 55 | @echo "#define __CONFIG_H__" >> $@ 56 | @cat build/*.res >> $@ 57 | @if [ x"$(DEBUG)" = x"yes" ]; then echo "#define RIE_DEBUG" >> $@; fi 58 | @if [ x"$(TESTS)" = x"yes" ]; then echo "#define RIE_TESTS" >> $@; fi 59 | @echo "#define RIE_PREFIX \"$(PREFIX)\"" >> $@ 60 | @echo "#define RIE_BINDIR \"$(BINDIR)\"" >> $@ 61 | @echo "#define RIE_MANDIR \"$(MANDIR)\"" >> $@ 62 | @echo "#define RIE_DOCDIR \"$(DOCDIR)\"" >> $@ 63 | @echo "#define RIE_DATADIR \"$(DATADIR)\"" >> $@ 64 | @echo "#define RIE_CC \"$(CC)\"" >> $@ 65 | @echo "#define RIE_CFLAGS \"$(CFLAGS)\"" >> $@ 66 | @echo "#define RIE_LDFLAGS \"$(LDFLAGS)\"" >> $@ 67 | @echo "#define RIE_BUILD_OS \"$(OS)\"" >> $@ 68 | @echo "#define RIE_REV \"rev.$(REV)\"" >> $@ 69 | @echo "#endif" >> $@ 70 | 71 | build/%.d: ; 72 | .PRECIOUS: build/%.d 73 | 74 | build/%.o: %.c build/config.h build/%.d 75 | $(CC) $(CFLAGS) $(DEPFLAGS) -c $< -o $@ 76 | @$(POSTCOMPILE) 77 | 78 | build/rieman: $(call src-to-obj,$(SRCS)) 79 | $(LINK.c) $^ $(LDFLAGS) $(LOADLIBES) $(LDLIBS) -o $@ $(LIBS) 80 | @echo "--- $(notdir $@) built ok ---" 81 | 82 | distclean: 83 | @rm -rf build rieman-$(VER).tar.gz rieman.log 84 | @rm makefile 85 | @echo "--- full cleanup done ---" 86 | 87 | clean: 88 | @rm -f build/rie* 89 | @echo "--- cleanup done ---" 90 | 91 | install: build/rieman 92 | mkdir -p $(DESTDIR)$(BINDIR) 93 | mkdir -p $(DESTDIR)$(DATADIR) 94 | mkdir -p $(DESTDIR)$(MANDIR) 95 | mkdir -p $(DESTDIR)$(DOCDIR) 96 | install -m 755 ./build/rieman $(DESTDIR)$(BINDIR) 97 | install -m 644 ./conf/rieman.conf $(DESTDIR)$(DATADIR)/rieman.conf 98 | tar cf - ./skins | (cd $(DESTDIR)$(DATADIR); tar xf -) 99 | install -m 644 ./doc/rieman.1 $(DESTDIR)$(MANDIR) 100 | install -m 644 ./README.md $(DESTDIR)$(DOCDIR) 101 | install -m 644 ./COPYING $(DESTDIR)$(DOCDIR) 102 | 103 | uninstall: 104 | rm -i $(DESTDIR)$(BINDIR)/rieman 105 | rm -i $(DESTDIR)$(MANDIR)/rieman.1 106 | rm -ir $(DESTDIR)$(DATADIR) 107 | rm -ir $(DESTDIR)$(DOCDIR) 108 | 109 | test: build/rieman 110 | @echo "--- check if binary has tests enabled ---" 111 | @if ! ./build/rieman -vv | grep "tests: yes" > /dev/null ; then \ 112 | printf "%s\n" " - Builtin tests are not enabled:"; \ 113 | printf "%s\n" " please add '--with-tests' option to configure"; \ 114 | exit 1; \ 115 | fi 116 | @echo "--- running external tests ---" 117 | @python ./tests/rieman_tests.py 118 | @echo "--- running internal tests ---" 119 | @RIE_TEST_ENABLE=1 ./build/rieman -c ./tests/conf/default.conf; true 120 | 121 | coverage: build/rieman 122 | @lcov --directory build --zerocounters > /dev/null 2>&1 123 | @echo " + coverage counters zeroed, starting tests..." 124 | lcov -b . -c -i -d ./build -o build/lcov.base 125 | @python tests/rieman_tests.py 126 | @echo " + running internal tests..." 127 | @RIE_TEST_ENABLE=1 ./build/rieman 2>&1 | egrep -v "\-...-"; true 128 | @echo " + collecting coverage..." 129 | @lcov --directory build -b build --capture -o build/lcov.run 130 | @lcov -a build/lcov.base -a build/lcov.run -o build/lcov.total 131 | @lcov -e build/lcov.total "`pwd`/*" -o build/lcov.filtered 132 | @lcov -r build/lcov.filtered src/rie_test.c -o build/lcov.final 133 | @genhtml -o build/report build/lcov.final > build/genhtml.log 2>&1 134 | @echo " + coverage report ready: 'build/report/index.html'" 135 | 136 | # --- Tarball, named for a current version --- 137 | 138 | TARBALL=auto conf doc pkg skins src tests README.md COPYING 139 | 140 | VER=$(shell sed -n 's/.*RIEMAN_VERSION.*"\(.*\)"/\1/p' src/rieman.h) 141 | 142 | tarball: rieman-$(VER).tar.gz 143 | 144 | rieman-$(VER).tar.gz: $(TARBALL) 145 | @echo "-- creating tarball for version $(VER) --" 146 | @tar --transform 's,^,rieman-$(VER)/,' -c -v -z -f $@ $^ 147 | @echo "-- $@ created ok --" 148 | 149 | -include $(call src-to-dep,$(SRCS)) 150 | -------------------------------------------------------------------------------- /auto/tests.mk: -------------------------------------------------------------------------------- 1 | build/test_cc: auto/tests/test_cc.c 2 | @printf "%s" "Checking for working C compiler..." 3 | @if $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) > $@.log 2>&1; \ 4 | then \ 5 | echo "ok"; \ 6 | else \ 7 | echo "no"; exit 1; \ 8 | fi 9 | 10 | # test of CFLAGS and LDFLAGS combination 11 | # $(call tests-cflags,name,cflags,ldflags) 12 | define test-compiler-flags 13 | testbins += build/$1.mk 14 | 15 | build/$1.mk: auto/tests/$1.c 16 | @if $(CC) $(CFLAGS) $2 -o build/$1 $$< $(LDFLAGS) $3 > $$@.log 2>&1; \ 17 | then \ 18 | echo "Checking if compiler supports $2...yes"; \ 19 | echo "CFLAGS += $2" > $$@; \ 20 | echo "LDFLAGS += $3" >> $$@; \ 21 | else \ 22 | echo "Checking if compiler supports $2...no, ignored"; \ 23 | fi 24 | @touch $$@ 25 | endef 26 | 27 | 28 | # test for optional library: show message if not passed 29 | # $(call lib-test-opt,name, title,cflags,ldflags,defname) 30 | define lib-test-opt 31 | 32 | testbins += build/$1.mk 33 | 34 | build/$1.mk: auto/tests/$1.c 35 | @if $(CC) $(CFLAGS) $3 -o $$@ $$< $(LDFLAGS) $4 > $$@.log 2>&1; \ 36 | then \ 37 | echo "Checking for $2...found"; \ 38 | echo "CFLAGS += $3" > $$@; \ 39 | echo "LIBS += $4" >> $$@; \ 40 | echo "#define RIE_HAVE_$5" > build/$1.res; \ 41 | else \ 42 | echo "Checking for $2...not found, related features disabled"; \ 43 | fi 44 | @touch $$@ 45 | endef 46 | 47 | 48 | pcf=$(shell pkg-config --cflags $1 2>/dev/null) 49 | plf=$(shell pkg-config --libs $1 2>/dev/null) 50 | 51 | # test for required library with pkg-confi: fails if not passed 52 | # $(call pkg-test,autotest,pkg,define) 53 | define pkg-test 54 | 55 | testbins += build/$1.mk 56 | 57 | build/$1.mk: auto/tests/$1.c 58 | @if $(CC) $(CFLAGS) $(call pcf,$2) -o $$@ $$< $(LDFLAGS) $(call plf,$2) \ 59 | > $$@.log 2>&1; \ 60 | then \ 61 | echo "Checking for pkg '$2'...found"; \ 62 | echo "CFLAGS += $(call pcf,$2)" > $$@; \ 63 | echo "LIBS += $(call plf,$2)" >> $$@; \ 64 | else \ 65 | echo "Checking for pkg '$2'...fail"; \ 66 | false; \ 67 | fi 68 | @echo "#define RIE_HAVE_$3" > $$@.res 69 | endef 70 | -------------------------------------------------------------------------------- /auto/tests/test_asan.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /auto/tests/test_cairo_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) cairo_create(NULL); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_cc.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /auto/tests/test_fc_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) FcInitLoadConfigAndFonts(); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_ft_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include FT_FREETYPE_H 3 | 4 | int main() 5 | { 6 | (void) FT_Init_FreeType(NULL); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /auto/tests/test_x11_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) XOpenDisplay(""); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xcb-ewmh_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) xcb_ewmh_init_atoms(NULL, NULL); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xcb-icccm_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) xcb_icccm_set_wm_hints(NULL, 0, NULL); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xcb-randr_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) xcb_randr_get_screen_resources(NULL, 0); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xcb-util_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) xcb_event_get_error_label(0); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xcb_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | (void) xcb_get_setup(NULL); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /auto/tests/test_xlib_xcb_lib.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() 3 | { 4 | (void) XGetXCBConnection(NULL); 5 | 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /conf/rieman.conf: -------------------------------------------------------------------------------- 1 | # ------------------------ 2 | # rieman 1.2 configuration 3 | # ------------------------ 4 | 5 | # size of a single desktop in pixels; if one dimension (width or height) 6 | # is zero then the other dimension is proportional to real resolution 7 | geometry.width 50 8 | geometry.height 0 9 | 10 | #subset.enabled true # only show subset of desktops tied to monitor 11 | #subset.output HDMI-0 # use specific RandR output for geometry 12 | #subset.start_desktop 4 # first desktop to show (zero-based) 13 | #subset.ndesktops 6 # number of desktops to show 14 | 15 | layout.wrap 2 16 | layout.corner topleft 17 | layout.orientation horizontal 18 | 19 | appearance.skin default 20 | 21 | # displays desktop number in the desktop background 22 | appearance.desktop_text true 23 | appearance.desktop_text.content number 24 | 25 | # displays name of a desktop below it 26 | appearance.desktop_pad true 27 | appearance.desktop_pad.position above # | below 28 | appearance.desktop_pad.margin 2 29 | 30 | # displays window icons 31 | appearance.window_icon true 32 | 33 | # display viewports grid inside desktop 34 | appearance.viewports true 35 | 36 | # display list of desktop's minimized windows under desktop 37 | appearance.minitray true 38 | 39 | # put into dock/slit 40 | window.withdrawn false 41 | 42 | # sets type of window to dock 43 | window.dock true 44 | 45 | # do not show pager in taskbar 46 | window.skip_taskbar true 47 | 48 | # do not show in pagers 49 | window.skip_pager true 50 | 51 | # appear on all desktops 52 | window.sticky true 53 | 54 | # startup position on the screen and corner offset 55 | window.position topright 56 | window.position.dx 0 57 | window.position.dy 0 58 | 59 | # window layer - below/above/normal 60 | window.layer above 61 | 62 | # NET_WM_STRUT/NET_WM_STRUT_PARTIAL to reserve space for the window 63 | # on desktop borders 64 | window.strut true 65 | window.strut.left 0 66 | window.strut.left_start_y 0 67 | window.strut.left_end_y 0 68 | window.strut.right 0 69 | window.strut.right_start_y 0 70 | window.strut.right_end_y 0 71 | window.strut.top 0 72 | window.strut.top_start_x 0 73 | window.strut.top_end_x 0 74 | window.strut.bottom 0 75 | window.strut.bottom_start_x 0 76 | window.strut.bottom_end_x 0 77 | 78 | # switches the desktop when clicked by it 79 | actions.change_desktop.mouse_button left 80 | actions.tile_windows.mouse_button right 81 | 82 | # rieman will listen for commands from this DGRAM unix socket 83 | #control.socket /path/to/.rieman.sock 84 | -------------------------------------------------------------------------------- /doc/rieman.1: -------------------------------------------------------------------------------- 1 | .TH rieman 1 "2023-02-27" "GNU/Linux" "User's Manual" 2 | 3 | .SH "NAME" 4 | rieman \- The X11 desktop pager 5 | 6 | .SH "SYNOPSIS" 7 | .B rieman 8 | [ 9 | .B \-h 10 | ] 11 | [ 12 | .B \-v[v] 13 | ] 14 | [ 15 | .B \-c 16 | config 17 | ] 18 | [ 19 | .B \-w 20 | ] 21 | [ 22 | .B \-l logfile 23 | ] 24 | 25 | .SH "DESCRIPTION" 26 | .PP 27 | .B rieman 28 | is X11 desktop pager: a program that display each existing virtual 29 | desktop with its windows and allows to switch between them. 30 | 31 | .SS "FEATURES" 32 | .IP \(bu 4 33 | NetWM compliant 34 | .IP \(bu 4 35 | Large desktop/Viewports support 36 | .IP \(bu 4 37 | Ability to set desktops geometry (_NET_DESKTOP_GEOMETRY) to arrange 38 | desktops in a configurable 2D grid 39 | .IP \(bu 4 40 | Displays name of desktops and windows in a configurable info area 41 | .IP \(bu 4 42 | Displays desktop number 43 | .IP \(bu 4 44 | Window selection/focusing when switching 45 | .IP \(bu 4 46 | Configurable look for different window states (active/inactive/requires attention) 47 | .IP \(bu 4 48 | Mini per-desktop tray for hidden windows 49 | .IP \(bu 4 50 | Supports display of application window icons 51 | .IP \(bu 4 52 | Pseudo-transparency support 53 | .IP \(bu 4 54 | Theme (aka skin) support. Any element may be customized. 55 | .IP \(bu 4 56 | Configuration reload 57 | .IP \(bu 4 58 | Simple tiling for desktop windows (1.1) 59 | .IP \(bu 4 60 | Control commands via configuration socket (1.2) 61 | .IP \(bu 4 62 | Per-output desktop subset display (1.3) 63 | 64 | 65 | .SH "OPTIONS" 66 | .TP 67 | .B \-help 68 | Displays short message with command-line options synopsis. 69 | .TP 70 | .B \-v 71 | displays version 72 | 73 | .TP 74 | .B \-vv 75 | displays version and build-time configuration 76 | 77 | .TP 78 | .B \-w 79 | .B rieman 80 | starts up in withdrawn mode so it can go into slit (aka dock). Exact 81 | effect is defined by a running window manager. 82 | 83 | .TP 84 | .B \-c 85 | use alternate 86 | .I config 87 | file. This overrides configuration lookup scheme. 88 | 89 | .TP 90 | .B \-l 91 | use 92 | .I logfile 93 | instead of sdout/stderr 94 | 95 | .SH "SIGNALS" 96 | .PP 97 | 98 | To reload configuration, send 99 | .B SIGUSR1 100 | to the rieman process. It will re-read all configuration files and apply 101 | changes. 102 | 103 | .SH "ENVIRONMENT VARIABLES" 104 | .PP 105 | 106 | The used X11 screen is taken from the 107 | .B DISPLAY 108 | environment variable. To run rieman on the second screen run: 109 | .TP 110 | $ DISPLAY=:0.1 rieman 111 | 112 | .SH "FILES" 113 | 114 | .PP 115 | The main configuration files is named rieman.conf. 116 | A skin is a directory containing image files and a 117 | rieman_skin.conf configuration file. All paths inside 118 | skin configuration file are relative to its directory. 119 | 120 | Files are looked up until found in the following locations: 121 | 122 | .TP 123 | .I ./conf/rieman.conf 124 | .TP 125 | .I ./skins//rieman_skin.conf 126 | 127 | current directory configuration (i.e. executing from the build directory) 128 | 129 | .TP 130 | .I 131 | $XDG_CONFIG_HOME/rieman/rieman.conf 132 | .TP 133 | .I $XDG_DATA_HOME/rieman/skins//rieman_skin.conf 134 | 135 | location as defined by freedesktop.org 136 | 137 | .TP 138 | .I ~/.config/rieman.conf 139 | .TP 140 | .I ~/.local/share/rieman/skins//rieman_skin.conf 141 | 142 | home directory 143 | 144 | .TP 145 | .I /usr/local/share/rieman/rieman.conf 146 | .TP 147 | .I /usr/local/share/rieman/skins//rieman_skin.conf 148 | 149 | system-wide, /usr/local/share/rieman is the default datadir 150 | 151 | .SH "CONFIGURATION" 152 | 153 | Configuration file format: 154 | 155 | .IP \(bu 4 156 | everything from '#' to end of line is a comment and ignored 157 | 158 | .IP \(bu 4 159 | each line contains key and value separated by whitespace 160 | 161 | .IP \(bu 4 162 | value is not escaped and quoting is not required. leading and trailing 163 | spaces in the value are ignored. See conf/rieman.conf for example. 164 | 165 | Configuration keys are: 166 | 167 | .TP 168 | .I geometry.width, geometry.height <0 | n> 169 | 170 | Defines size of a single on-screen desktop cell in pixels. Zero height leads 171 | to autocalculated setting based on screen aspect ratio. 172 | 173 | .TP 174 | .I layout.wrap 175 | layout.corner 176 | layout.orientation 177 | 178 | The wrap parameter defines length of a row or column (depending on orientation). 179 | The corner is one of "topleft", "topright", "bottomleft" or "bottomright" and 180 | defines how desktops are arranged. See _NET_DESKTOP_LAYOUT for details. 181 | 182 | .TP 183 | .I subset.enabled 184 | 185 | .I subset.output 186 | 187 | .I subset.start_desktop 188 | 189 | .I subset.ndesktops 190 | 191 | The "subsets" mode enables special mode, in which: 192 | 193 | 1) only a subset of virtual desktops is shown (start...start + ndesktops). 194 | 195 | 2) desktop geometry is determined by specified RandR output resolution. 196 | 197 | This is useful in multi-monitor configuration, where each monitor has its 198 | own set of virtual desktops, and desktop size is a summ of monitor sizes. 199 | The mode allows to run multiple instance of rieman, each attached to 200 | specific monitor and showing only desktops visible on the specific monitor. 201 | 202 | The feature was tested with awesome WM. Note that root window property 203 | _NET_CURRENT_DESKTOP allows to be set only once, so you cannot have active 204 | desktop highlighted in all rieman instances (in awesome's terms, this is 205 | currently selected tag, to which we have on access from outside). 206 | 207 | .TP 208 | .I appearance.skin 209 | 210 | Sets the skin name to use. 211 | 212 | .TP 213 | .I appearance.desktop_text 214 | appearance.desktop_text.content 215 | 216 | If set, desktop number or name is displayed in the center of a desktop cell 217 | 218 | .TP 219 | .I appearance.desktop_pad 220 | appearance.desktop_pad.position 221 | 222 | If set, little pad is drawn above or below desktop cell, which is used 223 | to display desktop name (and for other purposes as well, like windows 224 | names and icons for hidden windows) 225 | 226 | .TP 227 | .I appearance.window_icon 228 | 229 | If set, window icon is displayed additionally in a window rectangle 230 | 231 | .TP 232 | .I appearance.viewports 233 | 234 | If set, viewports are displayed as a grid inside a desktop cell 235 | 236 | .TP 237 | .I appearance.minitray 238 | 239 | If set, icons of a hidden windows are displayed in area, where desktop 240 | name is shown; click on such an icon restores the window 241 | 242 | .TP 243 | .I window.withdrawn 244 | starts the pager in withdrawn state to put into dock/slit 245 | 246 | .TP 247 | .I window.dock 248 | sets window type to _NET_WM_WINDOW_TYPE_DOCK. 249 | Useful for WMs that expect it. 250 | 251 | .TP 252 | .I window.skip_taskbar 253 | 254 | If set, pager window is not shown in the taskbar 255 | 256 | .TP 257 | .I window.skip_pager 258 | 259 | If set, pager window is not shown in pagers 260 | 261 | .TP 262 | .I window.sticky 263 | 264 | If set, pager window appears on all desktops 265 | 266 | .TP 267 | .I window.position 268 | window.dx 269 | window.dy 270 | 271 | Defines startup position on the screen. Note that in withdrawn mode 272 | window may be reparented and the setting will define position inside 273 | dock 274 | 275 | Optional dx and dy attributes control offset from x/y border at specified 276 | corner. By default, offsets are zero. 277 | 278 | .TP 279 | .I window.layer 280 | 281 | Defines window layer 282 | 283 | .TP 284 | .I window.struts 285 | 286 | .I window.struts.left 287 | 288 | .I window.struts.left_start_y 289 | 290 | .I window.struts.left_end_y 291 | 292 | .I window.struts.right right_start_y 293 | 294 | .I window.struts.right_end_y 295 | 296 | .I window.struts.top top_start_x 297 | 298 | .I window.struts.top_end_x 299 | 300 | .I window.struts.bottom 301 | 302 | .I window.struts.bottom_start_x 303 | 304 | .I window.struts.bottom_end 305 | 306 | If enabled, defines values for_NET_WM_STRUT/_NET_WM_STRUT_PARTIAL which 307 | reserve place on screen borders for the window. 308 | 309 | .TP 310 | .I actions.change_desktop 311 | actions.change_desktop.mouse_button 312 | 313 | If set, mouse events are processed, allowing to switch desktops and focus 314 | windows. The mouse_button arguments selects the desired button. 315 | 316 | .TP 317 | .I actions.tile_windows 318 | actions.tile_windows.mouse_button 319 | 320 | If set, mouse events are processed, allowing to tile windows on clicked 321 | desktop, using different tiling methods in a loop. Currently supported 322 | method is "fair" ported from the awesome WM, in horizontal and vertical modes. 323 | The mouse_button arguments selects the desired button. 324 | 325 | .TP 326 | .I control socket 327 | 328 | If set, rieman will listen for commands on UNIX DGRAM socket at specified 329 | path. To send command, run the rieman executable with -m option and provide 330 | path to socket and comand, for example: 331 | 332 | .I $ rieman -m /path/to/ctl.sock exit 333 | 334 | .SS "Recognized commands" 335 | 336 | .IP \(bu 4 337 | reload - reload configuration, see description above 338 | .IP \(bu 4 339 | exit - terminate the process 340 | .IP \(bu 4 341 | switch_desktop_left/right/up/down - switch desktop in 2D grid coordinates 342 | .IP \(bu 4 343 | switch_desktop_prev/next - switch to prev/next desktop by order 344 | .IP \(bu 4 345 | tile_current_desktop - tile windows on current desktop, one layout per command 346 | 347 | 348 | .SH "SKIN CONFIGURATION" 349 | 350 | The skin configuration defines following elements: 351 | .IP \(bu 4 352 | fonts - font specification for pager elements 353 | .IP \(bu 4 354 | backgrounds - textures specifications for pager elements 355 | .IP \(bu 4 356 | borders - borders specification for pager elements 357 | .IP \(bu 4 358 | icons - icons settings 359 | 360 | .TP 361 | The font attributes are: 362 | .TP 363 | .I fonts..face 364 | fonts..size 365 | fonts..color <[0x]base16-value> 366 | 367 | .I face 368 | is passed to fontconfig, check corresponding docs. Example "Droid Sans:10:Bold". 369 | .I size 370 | is the actual size in pixels that will be used to show it on screen. 371 | .I color 372 | is a hex number. 373 | 374 | .TP 375 | The backgrounds attributes are: 376 | .TP 377 | .I backgrounds..type 378 | backgrounds..alpha <0..1> 379 | backgrounds..src 380 | backgrounds..color <[0x]base16-value> 381 | 382 | For 383 | .I image, 384 | src attribute must be specified with a path to PNG image, or a special value 385 | .I ":root:" 386 | which will use existing root background. If type is 387 | .I color, use specified color 388 | 389 | 390 | The 391 | .I alpha 392 | attribute defines transparency of a texture. 393 | 394 | Following items are configurable in backgrounds: pager, desktop, desktop-pad, 395 | desktop-active, viewport, viewport-active, window, window-active, 396 | window-attention. 397 | 398 | .TP 399 | The borders attributes are: 400 | .TP 401 | .I borders..width 402 | borders..type 403 | borders..alpha <0..1> 404 | borders. tiles | 405 | borders..color <[0x]base16-value> 406 | 407 | Defines border around some element. If 408 | .I width 409 | is zero, no border is applied. Border can be either 410 | .I color 411 | or 412 | .I image. 413 | Image border is a texture with tiles. Texture is 4x4 tiles, each 414 | .I width 415 | pixels square. 416 | The 417 | .I alpha 418 | parameter is identical to those of 419 | .I background 420 | 421 | Following items are configurable in borders: pager, 422 | desktop-active, viewport, viewport-active, window, window-active, 423 | window-attention. 424 | 425 | .TP 426 | Stylable pager elemnts: 427 | 428 | .TP 429 | .I pager 430 | 431 | Defines the most bottom element for the whole pager. Transparent skins will 432 | use image and src=":root:" as background. 433 | 434 | .TP 435 | .I desktop, desktop-active 436 | 437 | Desktop cell; -active is for currently selected desktop and a desktop under mouse. 438 | 439 | .TP 440 | .I desktop-pad 441 | 442 | Area below or above the desktop cell reserverd for name/minitray 443 | 444 | .TP 445 | .I viewport, viewport-active 446 | 447 | Viewport area inside desktop cell 448 | 449 | .TP 450 | .I window, window-active, window-attention 451 | 452 | Windows in the pager. The 'active' is for focused windows and windows under mouse. 453 | The 'attention' is for windows that have 'REQUIRE_ATTENTION' state property, for 454 | example some messaging app with arrived message pending, or newly created 455 | window. 456 | 457 | .TP 458 | .I icons.window.alpha <0..1> 459 | icons.window.fallback 460 | 461 | Defines alpha level for window icons and image to use for windows that have 462 | no icons 463 | 464 | .SH CHANGES 465 | 466 | In version 1.2 all XML configuration was replaced with "conf" simple plaintext 467 | format, both for configuration and skins. 468 | Old files (configuration and skins) can be converted to new format using 469 | script/migrate_to_conf.sh script (xsltproc utility is required) 470 | 471 | Version 1.1 of skins removed /colors section with list of colordef's that 472 | could be referenced from skin. Instead, color is specified directly, 473 | using "color" attribute with hexadecimal value. 474 | 475 | .SH "AUTHORS" 476 | 477 | The rieman was written by Vladimir Khomutov. 478 | 479 | .SH "SEE ALSO" 480 | .BR xprop (1), 481 | .BR xwininfo (1), 482 | .TP 483 | .BR https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html 484 | .TP 485 | .BR https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html 486 | .TP 487 | .BR https://www.freedesktop.org/software/fontconfig/fontconfig-user 488 | -------------------------------------------------------------------------------- /doc/s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/doc/s1.png -------------------------------------------------------------------------------- /doc/s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/doc/s2.png -------------------------------------------------------------------------------- /pkg/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN apt-get update 6 | RUN apt-get install -qy git build-essential pkg-config 7 | RUN apt-get install -qy libcairo2 \ 8 | libcairo2-dev \ 9 | libfontconfig-dev \ 10 | libfontconfig1 \ 11 | libfreetype-dev \ 12 | libfreetype6 \ 13 | libx11-6 \ 14 | libx11-dev \ 15 | libx11-xcb-dev \ 16 | libx11-xcb1 \ 17 | libxcb-ewmh-dev \ 18 | libxcb-ewmh2 \ 19 | libxcb-icccm4 \ 20 | libxcb-icccm4-dev \ 21 | libxcb-util-dev \ 22 | libxcb-util1 \ 23 | libxcb1 \ 24 | libxcb1-dev \ 25 | xcb 26 | 27 | RUN git clone https://github.com/vl409/rieman.git 28 | 29 | RUN cd rieman && \ 30 | ./auto/configure --with-debug --with-tests && \ 31 | make && echo "ALL GOOD" 32 | 33 | 34 | -------------------------------------------------------------------------------- /pkg/rpm/rieman.spec: -------------------------------------------------------------------------------- 1 | # rpm specfile for: SuSe, Fedora 2 | 3 | %define name rieman 4 | %define release 0 5 | %define version 4 6 | 7 | BuildRoot: %{_tmppath}/%{name}-%{version}-build 8 | BuildRequires: tar make gcc 9 | Summary: Rieman the Pager 10 | License: GPL-3 11 | Name: %{name} 12 | Version: %{version} 13 | Release: %{release} 14 | Source: %{name}-%{version}.tar.gz 15 | Prefix: /usr/local 16 | Group: x11/misc 17 | URL: http://github.com 18 | 19 | %description 20 | TBD. 21 | 22 | %build 23 | tar xvf ../SOURCES/%{name}-%{version}.tar.gz 24 | cd %{name}-%{version} 25 | make PREFIX=$RPM_BUILD_ROOT/usr/local release 26 | 27 | %install 28 | cd %{name}-%{version} 29 | make install 30 | 31 | %files 32 | /usr/local/bin/rieman 33 | /usr/local/share/rieman 34 | 35 | %clean 36 | rm -rf "$RPM_BUILD_ROOT" 37 | -------------------------------------------------------------------------------- /scripts/migrate_to_conf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export LANG=C 4 | 5 | # tries to find all XML rieman configs and convert them to new dotconf format 6 | 7 | prefix=$(rieman -vv |grep prefix | awk '{ print $3 }' | sed 's/"//g') 8 | 9 | locations="$XDG_CONFIG_HOME/rieman \ 10 | $HOME/.config/rieman \ 11 | $prefix/share/rieman 12 | $XDG_DATA_HOME/rieman/skins \ 13 | $HOME/.local/share/rieman/skins \ 14 | $prefix/usr/local/share/rieman/skins" 15 | 16 | srcs= 17 | 18 | for d in $(printf "%s\n" $locations | sort -u) 19 | do 20 | if [ -e $d ]; then 21 | srcs="$srcs $(find $d -type f -name '*xml')" 22 | fi 23 | done 24 | 25 | echo 26 | echo "Found rieman XML files:" 27 | echo 28 | 29 | count=0 30 | for s in $srcs 31 | do 32 | echo $s 33 | count=$(($count + 1)) 34 | done 35 | 36 | if [ $count = 0 ]; then 37 | echo "No rieman XML files found (prefix='$prefix' HOME='$HOME' XDG_CONFIG_HOME='$XDG_CONFIG_HOME'), exiting" 38 | exit 0 39 | fi 40 | 41 | echo 42 | read -p "Type YES to proceed: " agree 43 | 44 | if [ "x$agree" != "xYES" ]; then 45 | echo "Bye, nothing to do" 46 | exit 47 | fi 48 | 49 | for s in $srcs 50 | do 51 | tgt=$(echo $s | sed 's/xml/conf/g') 52 | printf "%s" "Converting '$s' to '$tgt'..." 53 | ./scripts/xml2conf.sh $s > $tgt 54 | if [ $? -ne 0 ]; then 55 | echo "error" 56 | echo "please run ./scripts/xml2conf.sh '$s' manually and check what's going on" 57 | echo 58 | else 59 | echo "ok" 60 | fi 61 | done 62 | 63 | echo 64 | echo "If everything works fine, you can now remove no longer needed XML files:" 65 | echo 66 | 67 | for s in $srcs 68 | do 69 | echo $s 70 | done 71 | 72 | -------------------------------------------------------------------------------- /scripts/xml2conf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -lt 1 ]; then 4 | echo "$0: converts rieman XML config into plain text .conf format" 5 | echo "Usage: $0 " 6 | exit 1 7 | fi 8 | 9 | xsltproc -V > /dev/null 2>&1 10 | if [ $? -ne 0 ]; then 11 | echo "xsltproc is not found, exiting" 12 | exit 1 13 | fi 14 | 15 | xsltproc - $1 << EOF | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | # rieman skin: 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | .. 54 | 55 | 56 | 57 | 58 | 59 | .. 60 | 61 | 62 | 63 | 64 | 65 | .. 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | # rieman config 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | . 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | EOF 110 | sed -e 's/\.enable//g' -e 's/\.value//g' -e 's/\.name//g' -e 's/rieman-conf.//g' 111 | -------------------------------------------------------------------------------- /skins/blue/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/blue/missing_icon.png -------------------------------------------------------------------------------- /skins/blue/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: blue 2 | 3 | fonts.desktop-name.size 10 4 | fonts.desktop-name.face Droid Sans:10 5 | fonts.desktop-name.color 3152e4 6 | 7 | fonts.window-name.size 10 8 | fonts.window-name.face Liberation Sans:10:Bold 9 | fonts.window-name.color 3152e4 10 | 11 | fonts.desktop-number.size 20 12 | fonts.desktop-number.face Clockopia 13 | fonts.desktop-number.color aab1ca1 14 | 15 | backgrounds.pager.type color 16 | backgrounds.pager.color 1f3c57 17 | 18 | backgrounds.desktop.type color 19 | backgrounds.desktop.color 1f3c57 20 | 21 | backgrounds.desktop-pad.type color 22 | backgrounds.desktop-pad.color 062045 23 | 24 | backgrounds.desktop-active.type color 25 | backgrounds.desktop-active.color 334d66 26 | 27 | backgrounds.viewport.type color 28 | backgrounds.viewport.color 2c5479 29 | 30 | backgrounds.viewport-active.type color 31 | backgrounds.viewport-active.color 4483be 32 | 33 | backgrounds.window.type color 34 | backgrounds.window.color aab1ca0 35 | backgrounds.window.alpha 0.5 36 | 37 | backgrounds.window-active.type color 38 | backgrounds.window-active.color 566191 39 | backgrounds.window-active.alpha 0.5 40 | 41 | backgrounds.window-attention.type color 42 | backgrounds.window-attention.color 7f8baf 43 | 44 | borders.pager.width 1 45 | borders.pager.type color 46 | borders.pager.color aab1ca1 47 | 48 | borders.desktop-active.width 1 49 | borders.desktop-active.type color 50 | borders.desktop-active.color 3152e4 51 | 52 | borders.viewport.width 1 53 | borders.viewport.type color 54 | borders.viewport.color 062045 55 | 56 | borders.viewport-active.width 1 57 | borders.viewport-active.type color 58 | borders.viewport-active.color aab1ca1 59 | 60 | borders.window.width 1 61 | borders.window.type color 62 | borders.window.color 566191 63 | 64 | borders.window-active.width 1 65 | borders.window-active.type color 66 | borders.window-active.color aab1ca1 67 | 68 | borders.window-attention.width 1 69 | borders.window-attention.type color 70 | borders.window-attention.color aab1ca 71 | 72 | icons.window.alpha 1.0 73 | icons.window.fallback missing_icon.png 74 | 75 | -------------------------------------------------------------------------------- /skins/bordered/desktop-active-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/desktop-active-border.png -------------------------------------------------------------------------------- /skins/bordered/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/missing_icon.png -------------------------------------------------------------------------------- /skins/bordered/pager-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/pager-border.png -------------------------------------------------------------------------------- /skins/bordered/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: bordered 2 | 3 | fonts.desktop-name.size 10 4 | fonts.desktop-name.face Droid Sans:10 5 | fonts.desktop-name.color ffffff 6 | 7 | fonts.window-name.size 10 8 | fonts.window-name.face Liberation Sans:10:Bold 9 | fonts.window-name.color ffffff 10 | 11 | fonts.desktop-number.size 20 12 | fonts.desktop-number.face Clockopia 13 | fonts.desktop-number.color 317256 14 | 15 | backgrounds.pager.type color 16 | backgrounds.pager.color 52bf90 17 | 18 | backgrounds.desktop.type color 19 | backgrounds.desktop.color 52bf90 20 | 21 | backgrounds.desktop-pad.type color 22 | backgrounds.desktop-pad.color 419873 23 | 24 | backgrounds.desktop-active.type color 25 | backgrounds.desktop-active.color 49ab81 26 | 27 | backgrounds.viewport.type color 28 | backgrounds.viewport.color 74cba6 29 | 30 | backgrounds.viewport-active.type color 31 | backgrounds.viewport-active.color 419873 32 | 33 | backgrounds.window.type color 34 | backgrounds.window.color 398564 35 | backgrounds.window.alpha 0.5 36 | 37 | backgrounds.window-active.type color 38 | backgrounds.window-active.color 317256 39 | backgrounds.window-active.alpha 0.5 40 | 41 | backgrounds.window-attention.type color 42 | backgrounds.window-attention.color 224f3c 43 | backgrounds.window-attention.alpha 1.0 44 | 45 | borders.pager.width 5 46 | borders.pager.type image 47 | borders.pager.src pager-border.png 48 | 49 | borders.desktop-active.width 5 50 | borders.desktop-active.type image 51 | borders.desktop-active.src desktop-active-border.png 52 | 53 | borders.viewport.width 5 54 | borders.viewport.type image 55 | borders.viewport.src viewport-border.png 56 | 57 | borders.viewport-active.width 5 58 | borders.viewport-active.type image 59 | borders.viewport-active.src viewport-active-border.png 60 | 61 | borders.window.width 3 62 | borders.window.type image 63 | borders.window.src window-border.png 64 | 65 | borders.window-active.width 3 66 | borders.window-active.type image 67 | borders.window-active.src window-active-border.png 68 | 69 | borders.window-attention.width 3 70 | borders.window-attention.type image 71 | borders.window-attention.src window-attention-border.png 72 | 73 | icons.window.alpha 1.0 74 | icons.window.fallback missing_icon.png 75 | 76 | -------------------------------------------------------------------------------- /skins/bordered/viewport-active-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/viewport-active-border.png -------------------------------------------------------------------------------- /skins/bordered/viewport-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/viewport-border.png -------------------------------------------------------------------------------- /skins/bordered/window-active-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/window-active-border.png -------------------------------------------------------------------------------- /skins/bordered/window-attention-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/window-attention-border.png -------------------------------------------------------------------------------- /skins/bordered/window-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/bordered/window-border.png -------------------------------------------------------------------------------- /skins/default/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/default/missing_icon.png -------------------------------------------------------------------------------- /skins/default/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: default 2 | 3 | fonts.desktop-name.size 10 4 | fonts.desktop-name.face Droid Sans:10 5 | fonts.desktop-name.color ffffff 6 | 7 | fonts.window-name.size 10 8 | fonts.window-name.face Liberation Sans:10:Bold 9 | fonts.window-name.color ffffff 10 | 11 | fonts.desktop-number.size 20 12 | fonts.desktop-number.face Clockopia 13 | fonts.desktop-number.color 317256 14 | 15 | backgrounds.pager.type color 16 | backgrounds.pager.color 52bf90 17 | 18 | backgrounds.desktop.type color 19 | backgrounds.desktop.color 52bf90 20 | 21 | backgrounds.desktop-pad.type color 22 | backgrounds.desktop-pad.color 419873 23 | 24 | backgrounds.desktop-active.type color 25 | backgrounds.desktop-active.color 49ab81 26 | 27 | backgrounds.viewport.type color 28 | backgrounds.viewport.color 74cba6 29 | 30 | backgrounds.viewport-active.type color 31 | backgrounds.viewport-active.color 419873 32 | 33 | backgrounds.window.type color 34 | backgrounds.window.color 398564 35 | backgrounds.window.alpha 0.5 36 | 37 | backgrounds.window-active.type color 38 | backgrounds.window-active.color 317256 39 | backgrounds.window-active.alpha 0.5 40 | 41 | backgrounds.window-attention.type color 42 | backgrounds.window-attention.color 224f3c 43 | backgrounds.window-attention.alpha 1.0 44 | 45 | borders.pager.width 1 46 | borders.pager.type color 47 | borders.pager.color 317256 48 | 49 | borders.desktop-active.width 1 50 | borders.desktop-active.type color 51 | borders.desktop-active.color ffff00 52 | 53 | borders.viewport.width 1 54 | borders.viewport.type color 55 | borders.viewport.color ffffff 56 | 57 | borders.viewport-active.width 1 58 | borders.viewport-active.type color 59 | borders.viewport-active.color ffff00 60 | 61 | borders.window.width 1 62 | borders.window.type color 63 | borders.window.color 1a3d2e 64 | 65 | borders.window-active.width 1 66 | borders.window-active.type color 67 | borders.window-active.color 52bf90 68 | 69 | borders.window-attention.width 1 70 | borders.window-attention.type color 71 | borders.window-attention.color ffffff 72 | 73 | icons.window.alpha 1.0 74 | icons.window.fallback missing_icon.png 75 | 76 | -------------------------------------------------------------------------------- /skins/light/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/light/missing_icon.png -------------------------------------------------------------------------------- /skins/light/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: light 2 | 3 | fonts.desktop-name.size 10 4 | fonts.desktop-name.face Droid Sans:10 5 | fonts.desktop-name.color d1efb5 6 | 7 | fonts.window-name.size 10 8 | fonts.window-name.face Liberation Sans:10:Bold 9 | fonts.window-name.color d1efb5 10 | 11 | fonts.desktop-number.size 20 12 | fonts.desktop-number.face Clockopia 13 | fonts.desktop-number.color 7d7d5a 14 | 15 | backgrounds.pager.type color 16 | backgrounds.pager.color d8d692 17 | 18 | backgrounds.desktop.type color 19 | backgrounds.desktop.color d8d692 20 | 21 | backgrounds.desktop-pad.type color 22 | backgrounds.desktop-pad.color c2c183 23 | 24 | backgrounds.desktop-active.type color 25 | backgrounds.desktop-active.color eeeca8 26 | 27 | backgrounds.viewport.type color 28 | backgrounds.viewport.color 59cfcc 29 | 30 | backgrounds.viewport-active.type color 31 | backgrounds.viewport-active.color 3ca7a3 32 | 33 | backgrounds.window.type color 34 | backgrounds.window.color a0a174 35 | backgrounds.window.alpha 0.5 36 | 37 | backgrounds.window-active.type color 38 | backgrounds.window-active.color 97b9b9 39 | backgrounds.window-active.alpha 0.5 40 | 41 | backgrounds.window-attention.type color 42 | backgrounds.window-attention.color a8cecd 43 | 44 | borders.pager.width 1 45 | borders.pager.type color 46 | borders.pager.color 7d7d5a 47 | 48 | borders.desktop-active.width 1 49 | borders.desktop-active.type color 50 | borders.desktop-active.color d1efb5 51 | 52 | borders.viewport.width 1 53 | borders.viewport.type color 54 | borders.viewport.color c2c183 55 | 56 | borders.viewport-active.width 1 57 | borders.viewport-active.type color 58 | borders.viewport-active.color 7d7d5a 59 | 60 | borders.window.width 1 61 | borders.window.type color 62 | borders.window.color 97b9b9 63 | 64 | borders.window-active.width 1 65 | borders.window-active.type color 66 | borders.window-active.color 7d7d5a 67 | 68 | borders.window-attention.width 1 69 | borders.window-attention.type color 70 | borders.window-attention.color bee4e3 71 | 72 | icons.window.alpha 1.0 73 | icons.window.fallback missing_icon.png 74 | 75 | -------------------------------------------------------------------------------- /skins/transparent/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/transparent/missing_icon.png -------------------------------------------------------------------------------- /skins/transparent/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | 2 | fonts.desktop-name.face Droid Sans:10 3 | fonts.desktop-name.size 10 4 | fonts.desktop-name.color ffffff 5 | 6 | fonts.window-name.face Droid Sans:10 7 | fonts.window-name.size 10 8 | fonts.window-name.color ffffff 9 | 10 | fonts.desktop-number.face Clockopia 11 | fonts.desktop-number.size 20 12 | fonts.desktop-number.color 000000 13 | 14 | backgrounds.pager.type image 15 | backgrounds.pager.src :root: 16 | 17 | backgrounds.desktop.type color 18 | backgrounds.desktop.color 000000 19 | backgrounds.desktop.alpha 0.08 20 | 21 | backgrounds.desktop-pad.type color 22 | backgrounds.desktop-pad.color 000000 23 | backgrounds.desktop-pad.alpha 0.4 24 | 25 | backgrounds.desktop-active.type color 26 | backgrounds.desktop-active.color 000000 27 | backgrounds.desktop-active.alpha 0.3 28 | 29 | backgrounds.viewport.type color 30 | backgrounds.viewport.color 0000ff 31 | backgrounds.viewport.alpha 0.1 32 | 33 | backgrounds.viewport-active.type color 34 | backgrounds.viewport-active.color 666666 35 | backgrounds.viewport-active.alpha 0.5 36 | 37 | backgrounds.window.type color 38 | backgrounds.window.color 000000 39 | backgrounds.window.alpha 0.4 40 | 41 | backgrounds.window-active.type color 42 | backgrounds.window-active.color 0000ff 43 | backgrounds.window-active.alpha 0.12 44 | 45 | backgrounds.window-attention.type color 46 | backgrounds.window-attention.color 0000ff 47 | backgrounds.window-attention.alpha 0.3 48 | 49 | borders.pager.width 1 50 | borders.pager.type color 51 | borders.pager.color ffffff 52 | borders.pager.alpha 0.2 53 | 54 | borders.desktop-active.width 1 55 | borders.desktop-active.type color 56 | borders.desktop-active.color 000000 57 | 58 | borders.viewport.width 5 59 | borders.viewport.type image 60 | borders.viewport.src viewport-border.png 61 | 62 | borders.viewport-active.width 5 63 | borders.viewport-active.type image 64 | borders.viewport-active.src viewport-active-border.png 65 | borders.viewport-active.alpha 0.8 66 | 67 | borders.window.width 1 68 | borders.window.type color 69 | borders.window.color 666666 70 | borders.window.alpha 0.4 71 | 72 | borders.window-active.width 1 73 | borders.window-active.type color 74 | borders.window-active.color 000000 75 | 76 | borders.window-attention.width 1 77 | borders.window-attention.type color 78 | borders.window-attention.color ffffff 79 | 80 | icons.window.alpha 1.0 81 | icons.window.fallback missing_icon.png 82 | -------------------------------------------------------------------------------- /skins/transparent/viewport-active-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/transparent/viewport-active-border.png -------------------------------------------------------------------------------- /skins/transparent/viewport-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/skins/transparent/viewport-border.png -------------------------------------------------------------------------------- /src/rie_config.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2020 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | #include "rie_config.h" 24 | 25 | #include 26 | #include 27 | #include /* open */ 28 | #include 29 | #include 30 | #include /* strcasecmp */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define rie_conf_log_error(key, err) \ 37 | rie_conf_log_error_real( __FILE__, __LINE__, key, err) 38 | 39 | typedef int (*rie_conf_type_parse_pt)(char *str, rie_conf_value_t *res); 40 | 41 | void rie_conf_quiet_stub(void *ctx, const char *msg, ...); 42 | 43 | /* low level type convertors */ 44 | static int rie_conf_to_str(char* str, rie_conf_value_t *res); 45 | static int rie_conf_to_uint32(char* str, rie_conf_value_t *res); 46 | static int rie_conf_to_dbl(char* str, rie_conf_value_t *res); 47 | static int rie_conf_to_bool(char* str, rie_conf_value_t *res); 48 | 49 | static int rie_conf_set_value(rie_conf_type_t type, rie_conf_value_t *val, 50 | void *res); 51 | 52 | static int rie_locate_file(char (*found)[FILENAME_MAX], 53 | char (*paths)[FILENAME_MAX], size_t nelts); 54 | 55 | static int rie_conf_process_item(rie_conf_item_t *spec, void *ctx, 56 | char *value); 57 | static int rie_conf_init_defaults(rie_conf_item_t *cf, void *ctx); 58 | static int rie_conf_handle_key(char *key, char*arg, 59 | rie_conf_item_t *cf, void *ctx); 60 | static int rie_conf_parse(char *filename, rie_conf_item_t *spec, void *ctx); 61 | 62 | 63 | rie_conf_type_parse_pt rie_conf_type_parser[] = { 64 | rie_conf_to_str, /* RIE_CTYPE_STR */ 65 | rie_conf_to_uint32, /* RIE_CTYPE_UINT32 */ 66 | rie_conf_to_dbl, /* RIE_CTYPE_DBL */ 67 | rie_conf_to_bool, /* RIE_CTYPE_BOOL */ 68 | }; 69 | 70 | static void 71 | rie_conf_log_error_real(char *file, int line, char *key, char *error) 72 | { 73 | if (key) { 74 | rie_log_sys_error_real(file, line, 0, "%s while processing key '%s'", 75 | error, key); 76 | 77 | } else { 78 | rie_log_sys_error_real(file, line, 0, "%s", error); 79 | } 80 | } 81 | 82 | 83 | void 84 | rie_conf_quiet_stub(void *ctx, const char *msg, ...) 85 | { 86 | /* nothing here */ 87 | } 88 | 89 | 90 | static int 91 | rie_conf_to_str(char *str, rie_conf_value_t *res) 92 | { 93 | char *p; 94 | 95 | p = strdup(str); 96 | if (p == NULL) { 97 | rie_log_error0(errno, "strdup"); 98 | return RIE_ERROR; 99 | } 100 | 101 | res->str = p; 102 | 103 | return RIE_OK; 104 | } 105 | 106 | 107 | static int 108 | rie_conf_to_uint32(char* str, rie_conf_value_t *res) 109 | { 110 | long val; 111 | char *endptr; 112 | 113 | errno = 0; 114 | val = strtol(str, &endptr, 0); 115 | 116 | if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) 117 | || (errno != 0 && val == 0)) 118 | { 119 | rie_log_error0(errno, "conversion to uint32 failed: strtol()"); 120 | return RIE_ERROR; 121 | } 122 | 123 | if (endptr == str) { 124 | rie_log_error0(0, "conversion to uint32 failed: no digits"); 125 | return RIE_ERROR; 126 | } 127 | 128 | if (val < 0) { 129 | rie_log_error0(0, "conversion to uint32 failed: negative value"); 130 | return RIE_ERROR; 131 | } 132 | 133 | if (val > UINT32_MAX) { 134 | rie_log_error0(0, "conversion to uint32 failed: value too big"); 135 | return RIE_ERROR; 136 | } 137 | 138 | res->number = (uint32_t) val; 139 | 140 | return RIE_OK; 141 | } 142 | 143 | 144 | static int 145 | rie_conf_to_dbl(char* str, rie_conf_value_t *res) 146 | { 147 | double val; 148 | char *endptr; 149 | 150 | errno = 0; 151 | val = strtod(str, &endptr); 152 | 153 | if (errno) { 154 | rie_log_error(errno, "conversion of '%s' to double failed: strtod()", 155 | str); 156 | return RIE_ERROR; 157 | } 158 | 159 | if (endptr == str) { 160 | rie_log_error(0, "conversion of '%s' to double failed: no digits", str); 161 | return RIE_ERROR; 162 | } 163 | 164 | res->dbl = val; 165 | 166 | return RIE_OK; 167 | } 168 | 169 | 170 | static int 171 | rie_conf_to_bool(char *str, rie_conf_value_t *res) 172 | { 173 | if (strcasecmp(str, "true") == 0 || strcmp(str, "1") ==0) { 174 | res->number = 1; 175 | 176 | } else if (strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0) { 177 | res->number = 0; 178 | 179 | } else { 180 | rie_log_error(0, "conversion to bool failed: unknown value '%s'", str); 181 | return RIE_ERROR; 182 | } 183 | 184 | return RIE_OK; 185 | } 186 | 187 | 188 | static int 189 | rie_conf_set_value(rie_conf_type_t type, rie_conf_value_t *val, void *res) 190 | { 191 | switch (type) { 192 | case RIE_CTYPE_STR: 193 | *((char **) res) = val->str; 194 | break; 195 | case RIE_CTYPE_BOOL: /* bool is stored as uint32 */ 196 | case RIE_CTYPE_UINT32: 197 | *((uint32_t *) res) = val->number; 198 | break; 199 | case RIE_CTYPE_DBL: 200 | *((double *) res) = val->dbl; 201 | break; 202 | default: 203 | /* unknown type, should not happen */ 204 | return RIE_ERROR; 205 | } 206 | 207 | return RIE_OK; 208 | } 209 | 210 | 211 | static int 212 | rie_locate_file(char (*found)[FILENAME_MAX], char (*paths)[FILENAME_MAX], 213 | size_t nelts) 214 | { 215 | int i, rc; 216 | struct stat fileinfo; 217 | 218 | for (i = 0; i < nelts; i++) { 219 | 220 | rie_debug("trying file %s", paths[i]); 221 | 222 | rc = stat(paths[i], &fileinfo); 223 | if (rc == -1) { 224 | continue; 225 | } 226 | 227 | if ((fileinfo.st_mode & S_IFMT) != S_IFREG) { 228 | rie_log_error(0, "file %s is not a regular file", paths[i]); 229 | return RIE_ERROR; 230 | } 231 | 232 | strcpy(*found, paths[i]); 233 | return RIE_OK; 234 | } 235 | 236 | return RIE_ERROR; 237 | } 238 | 239 | 240 | int 241 | rie_conf_set_variants(rie_conf_item_t *spec, void *value, void *res, 242 | char *key) 243 | { 244 | int i; 245 | char *s; 246 | uint32_t *out; 247 | 248 | rie_conf_map_t *map; 249 | 250 | out = res; 251 | s = *(char **)value; 252 | map = (rie_conf_map_t *) spec->data.ptr; 253 | 254 | for (i = 0; map[i].str; i++) { 255 | if (strcmp(map[i].str, s) == 0) { 256 | *out = map[i].number; 257 | free(s); /* no longer needed */ 258 | return RIE_OK; 259 | } 260 | } 261 | 262 | rie_log_error(0, "invalid variant '%s' of key '%s'", s, key); 263 | free(s); 264 | 265 | return RIE_ERROR; 266 | } 267 | 268 | 269 | int 270 | rie_locate_config(char (*found)[FILENAME_MAX], char *fname) 271 | { 272 | int last = 0; 273 | char *home; 274 | 275 | #if defined(RIE_TESTS) 276 | char locations[5][FILENAME_MAX] = { {0}, {0}, {0}, {0}, {0} }; 277 | sprintf(locations[last++], "./tests/conf/%s", fname); 278 | #else 279 | char locations[4][FILENAME_MAX] = { {0}, {0}, {0}, {0} }; 280 | #endif 281 | 282 | sprintf(locations[last++], "./conf/%s", fname); 283 | 284 | home = getenv("XDG_CONFIG_HOME"); 285 | if (home) { 286 | sprintf(locations[last++], "%s/rieman/%s", home, fname); 287 | } 288 | 289 | home = getenv("HOME"); 290 | if (home) { 291 | sprintf(locations[last++], "%s/.config/rieman/%s", home, fname); 292 | } 293 | 294 | sprintf(locations[last++], "%s/%s", RIE_DATADIR, fname); 295 | 296 | return rie_locate_file(found, locations, last); 297 | } 298 | 299 | 300 | int 301 | rie_locate_skin(char (*found)[FILENAME_MAX], char *fname) 302 | { 303 | int last; 304 | char *home; 305 | 306 | char locations[4][FILENAME_MAX] = { {0}, {0}, {0} }; 307 | 308 | last = 0; 309 | 310 | sprintf(locations[last++], "./skins/%s/rieman_skin.conf", fname); 311 | 312 | home = getenv("XDG_DATA_HOME"); 313 | if (home) { 314 | sprintf(locations[last++], "%s/rieman/skins/%s/rieman_skin.conf", 315 | home, fname); 316 | } 317 | 318 | home = getenv("HOME"); 319 | if (home) { 320 | sprintf(locations[last++], 321 | "%s/.local/share/rieman/skins/%s/rieman_skin.conf", 322 | home, fname); 323 | } 324 | 325 | sprintf(locations[last++], "%s/skins/%s/rieman_skin.conf", RIE_DATADIR, 326 | fname); 327 | 328 | return rie_locate_file(found, locations, last); 329 | } 330 | 331 | 332 | static int 333 | rie_conf_process_item(rie_conf_item_t *spec, void *ctx, char *value) 334 | { 335 | int rc; 336 | void *res; 337 | rie_conf_value_t box; 338 | 339 | /* convert string value of key into destination type */ 340 | if (rie_conf_type_parser[spec->type](value, &box) != RIE_OK) { 341 | rie_conf_log_error(spec->name, "failed to convert key value"); 342 | return RIE_ERROR; 343 | } 344 | 345 | res = (char *) ctx + spec->offset; 346 | 347 | /* save converted value into destination according to type */ 348 | if (spec->convert) { 349 | rc = spec->convert(spec, &box.data, res, spec->name); 350 | 351 | } else { 352 | rc = rie_conf_set_value(spec->type, &box, res); 353 | } 354 | 355 | if (rc != RIE_OK) { 356 | return RIE_ERROR; 357 | } 358 | 359 | spec->initialized = 1; 360 | 361 | return RIE_OK; 362 | 363 | } 364 | 365 | 366 | static int 367 | rie_conf_init_defaults(rie_conf_item_t *cf, void *ctx) 368 | { 369 | char *value; 370 | rie_conf_item_t *spec; 371 | 372 | for (spec = cf; spec->name; spec++) { 373 | if (spec->initialized) { 374 | continue; 375 | } 376 | 377 | if (spec->dflt == NULL) { 378 | rie_conf_log_error(spec->name, 379 | "value not set in config and no default"); 380 | return RIE_ERROR; 381 | } 382 | 383 | value = (char *) spec->dflt; 384 | 385 | if (rie_conf_process_item(spec, ctx, value) != RIE_OK) { 386 | return RIE_ERROR; 387 | } 388 | } 389 | 390 | return RIE_OK; 391 | } 392 | 393 | 394 | static int 395 | rie_conf_handle_key(char *key, char *arg, rie_conf_item_t *cf, void *ctx) 396 | { 397 | rie_conf_item_t *spec; 398 | 399 | rie_debug("processing key \"%s\" arg \"%s\"", key, arg); 400 | 401 | for (spec = cf; spec->name; spec++) { 402 | if (strcmp(spec->name, key) == 0) { 403 | 404 | if (rie_conf_process_item(spec, ctx, arg) != RIE_OK) { 405 | return RIE_ERROR; 406 | } 407 | 408 | return RIE_OK; 409 | } 410 | } 411 | 412 | rie_log_error(0, "unknown configuration directive: \"%s\"", key); 413 | 414 | return RIE_ERROR; 415 | } 416 | 417 | 418 | static inline void 419 | rie_conf_strip_tail(char *tail) 420 | { 421 | char *p; 422 | p = tail - 1; 423 | 424 | while (isspace(*p)) { 425 | *p = 0; 426 | p--; 427 | } 428 | } 429 | 430 | 431 | static int 432 | rie_conf_parse(char *filename, rie_conf_item_t *spec, void *ctx) 433 | { 434 | int fd; 435 | char *buf, *p, *end, *key, *arg, *lpos, *errmsg; 436 | 437 | size_t lnum; 438 | ssize_t n; 439 | struct stat sb; 440 | 441 | enum { 442 | ST_PRE, 443 | ST_KEY, 444 | ST_DIV, 445 | ST_ARG, 446 | ST_TAIL 447 | } state; 448 | 449 | buf = NULL; 450 | key = NULL; 451 | arg = NULL; 452 | errmsg = NULL; 453 | 454 | fd = open(filename, O_RDONLY); 455 | if (fd == -1) { 456 | rie_log_error(errno, "open(\"%s\") failed", filename); 457 | return RIE_ERROR; 458 | } 459 | 460 | if (fstat(fd, &sb) == -1) { 461 | rie_log_error(errno, "fstat(\"%s\") failed", filename); 462 | goto failed; 463 | } 464 | 465 | buf = malloc(sb.st_size +1); 466 | if (buf == NULL) { 467 | rie_log_error(errno, "malloc"); 468 | goto failed; 469 | } 470 | 471 | n = read(fd, buf, sb.st_size); 472 | if (n == -1) { 473 | rie_log_error(errno, "read(\"%s\") failed", filename); 474 | goto failed; 475 | } 476 | 477 | if (n != sb.st_size) { 478 | rie_log_error(0, "cannot read full file"); 479 | goto failed; 480 | } 481 | 482 | p = buf; 483 | end = p + n; 484 | lnum = 1; 485 | lpos = p; 486 | 487 | state = ST_PRE; 488 | 489 | while (p < end) { 490 | 491 | switch (state) { 492 | case ST_PRE: 493 | 494 | if (*p == '#') { 495 | state = ST_TAIL; 496 | 497 | } else if (isalnum(*p) || ispunct(*p)) { 498 | state = ST_KEY; 499 | key = p; 500 | 501 | } else if (*p == '\n') { 502 | lnum++; 503 | lpos = p + 1; 504 | 505 | } else if (!(isspace(*p))) { 506 | errmsg = "unexpected symbol while looking for key"; 507 | goto failed; 508 | } 509 | 510 | break; 511 | 512 | case ST_KEY: 513 | 514 | if (*p == '\n' || *p == '#') { 515 | errmsg = "key without value"; 516 | goto failed; 517 | 518 | } else if (isspace(*p)) { 519 | *p = 0; 520 | state = ST_DIV; 521 | 522 | } else if (isalnum(*p) || ispunct(*p)) { 523 | /* consuming key... */ 524 | 525 | } else { 526 | errmsg = "unexpected symbol while processing key"; 527 | goto failed; 528 | } 529 | 530 | break; 531 | 532 | case ST_DIV: 533 | 534 | if (*p == '\n' || *p == '#') { 535 | errmsg = "key without value"; 536 | goto failed; 537 | 538 | } else if (isalnum(*p) || ispunct(*p)) { 539 | state = ST_ARG; 540 | arg = p; 541 | 542 | } else if (!isspace(*p)) { 543 | errmsg = "unexpected symbol while looking for argument"; 544 | goto failed; 545 | } 546 | 547 | break; 548 | 549 | case ST_ARG: 550 | 551 | if (*p == '\n') { 552 | state = ST_PRE; 553 | lnum++; 554 | lpos = p + 1; 555 | 556 | *p = 0; 557 | rie_conf_strip_tail(p); 558 | 559 | if (rie_conf_handle_key(key, arg, spec, ctx) != RIE_OK) { 560 | goto failed; 561 | } 562 | 563 | } else if (*p == '#') { 564 | *p = 0; 565 | state = ST_TAIL; 566 | rie_conf_strip_tail(p); 567 | 568 | if (rie_conf_handle_key(key, arg, spec, ctx) != RIE_OK) { 569 | goto failed; 570 | } 571 | } else if (isalnum(*p) || ispunct(*p) || isspace(*p)) { 572 | 573 | /* consuming key */ 574 | 575 | } else { 576 | errmsg = "unexpected symbol while processing argument"; 577 | goto failed; 578 | } 579 | 580 | break; 581 | 582 | case ST_TAIL: 583 | if (*p == '\n') { 584 | state = ST_PRE; 585 | lnum++; 586 | lpos = p + 1; 587 | } 588 | break; 589 | } 590 | 591 | p++; 592 | } 593 | 594 | switch (state) { 595 | case ST_PRE: 596 | case ST_TAIL: 597 | /* all good, nothing to do */ 598 | break; 599 | 600 | case ST_KEY: 601 | case ST_DIV: 602 | errmsg = "key without value"; 603 | goto failed; 604 | 605 | case ST_ARG: 606 | *p = 0; 607 | rie_conf_strip_tail(p); 608 | 609 | if (rie_conf_handle_key(key, arg, spec, ctx) != RIE_OK) { 610 | goto failed; 611 | } 612 | break; 613 | } 614 | 615 | (void) close(fd); 616 | free(buf); 617 | 618 | return RIE_OK; 619 | 620 | failed: 621 | 622 | if (errmsg) { 623 | rie_log_error(0, "syntax error at line %ld pos %ld: %s", 624 | lnum, p - lpos, errmsg); 625 | } 626 | 627 | close(fd); 628 | 629 | if (buf) { 630 | free(buf); 631 | } 632 | 633 | return RIE_ERROR; 634 | } 635 | 636 | 637 | int 638 | rie_conf_load(char *conf_file, rie_conf_meta_t *meta, void *ctx) 639 | { 640 | rie_conf_item_t *spec; 641 | 642 | for (spec = meta->spec; spec->name; spec++) { 643 | spec->initialized = 0; 644 | } 645 | 646 | if (rie_conf_parse(conf_file, meta->spec, ctx) != RIE_OK) { 647 | rie_conf_cleanup(meta, ctx); 648 | return RIE_ERROR; 649 | } 650 | 651 | return rie_conf_init_defaults(meta->spec, ctx); 652 | } 653 | 654 | 655 | void 656 | rie_conf_cleanup(rie_conf_meta_t *meta, void *ctx) 657 | { 658 | char **conf_key, *mem; 659 | void *res; 660 | rie_conf_item_t *spec; 661 | 662 | for (spec = meta->spec; spec->name; spec++) { 663 | if (!spec->initialized) { 664 | continue; 665 | } 666 | 667 | if (spec->type == RIE_CTYPE_STR && spec->convert == NULL) { 668 | /* converters free source string by themselves */ 669 | 670 | res = (char *) ctx + spec->offset; 671 | 672 | conf_key = (char **) res; 673 | mem = *conf_key; 674 | 675 | if (mem) { 676 | free(mem); 677 | *conf_key = NULL; 678 | } 679 | } 680 | 681 | spec->initialized = 0; 682 | } 683 | } 684 | -------------------------------------------------------------------------------- /src/rie_config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2019 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_CONFIG_H__ 23 | #define __RIE_CONFIG_H__ 24 | 25 | #include "rieman.h" 26 | 27 | #include 28 | #include /* for off_t */ 29 | 30 | #define RIE_CONF_MAIN 0x1 /* this is the main configuration file */ 31 | #define RIE_CONF_SKIN 0x2 /* this is the skin configuration file */ 32 | 33 | typedef enum { 34 | RIE_CTYPE_STR, 35 | RIE_CTYPE_UINT32, 36 | RIE_CTYPE_DBL, 37 | RIE_CTYPE_BOOL, 38 | } rie_conf_type_t; 39 | 40 | typedef union { 41 | char *str; 42 | uint32_t number; 43 | double dbl; 44 | void *data; 45 | } rie_conf_value_t; 46 | 47 | typedef struct rie_conf_item_s rie_conf_item_t; 48 | 49 | typedef int (*rie_conf_convert_pt)(rie_conf_item_t *spec, void *value, 50 | void *res, char *key); 51 | 52 | struct rie_conf_item_s { 53 | char *name; /* key, i.e. /root/some/thing */ 54 | rie_conf_type_t type; /* destination type */ 55 | char *dflt; /* default value as string */ 56 | off_t offset; /* memory offset in context */ 57 | rie_conf_convert_pt convert; /* convertor function for custom types */ 58 | union { /* extra args for convertor */ 59 | void *ptr; 60 | uint32_t u32; 61 | } data; 62 | int initialized; 63 | }; 64 | 65 | typedef struct { 66 | char *str; 67 | uint32_t number; 68 | } rie_conf_map_t; 69 | 70 | typedef struct { 71 | char *xpath; 72 | rie_conf_item_t *spec; 73 | } rie_conf_ref_t; 74 | 75 | 76 | int rie_conf_byte_to_double(rie_conf_item_t *spec, void *value, void *res, 77 | char *key); 78 | int rie_conf_set_variants(rie_conf_item_t *spec, void *value, void *res, 79 | char *key); 80 | int rie_conf_set_variants_mask(rie_conf_item_t *spec, void *value, void *res, 81 | char *key); 82 | int rie_conf_set_mask(rie_conf_item_t *spec, void *value, void *res, 83 | char *key); 84 | 85 | int rie_locate_config(char (*found)[FILENAME_MAX], char *fname); 86 | int rie_locate_skin(char (*found)[FILENAME_MAX], char *fname); 87 | 88 | int rie_conf_load(char *conf_file, rie_conf_meta_t *meta, void *ctx); 89 | void rie_conf_cleanup(rie_conf_meta_t *meta, void *ctx); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/rie_control.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2020-2022 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | #define RIE_CMD_BUF 128 35 | #define rie_cmd_name(str) str, sizeof(str) - 1 36 | 37 | struct rie_control_s { 38 | int fd; /* socket descriptor */ 39 | struct sockaddr_un sa; 40 | char *path; 41 | void *data; /* context for pager */ 42 | }; 43 | 44 | typedef struct { 45 | const char *name; 46 | size_t len; 47 | rie_command_t action; 48 | } rie_cmd_desc_t; 49 | 50 | static rie_cmd_desc_t rie_control_messages[] = { 51 | { rie_cmd_name("reload"), RIE_CMD_RELOAD}, 52 | { rie_cmd_name("exit"), RIE_CMD_EXIT }, 53 | { rie_cmd_name("switch_desktop_left"), RIE_CMD_SWITCH_DESKTOP_LEFT }, 54 | { rie_cmd_name("switch_desktop_right"), RIE_CMD_SWITCH_DESKTOP_RIGHT }, 55 | { rie_cmd_name("switch_desktop_up"), RIE_CMD_SWITCH_DESKTOP_UP}, 56 | { rie_cmd_name("switch_desktop_down"), RIE_CMD_SWITCH_DESKTOP_DOWN }, 57 | { rie_cmd_name("switch_desktop_prev"), RIE_CMD_SWITCH_DESKTOP_PREV }, 58 | { rie_cmd_name("switch_desktop_next"), RIE_CMD_SWITCH_DESKTOP_NEXT }, 59 | { rie_cmd_name("tile_current_desktop"), RIE_CMD_TILE_CURRENT_DESKTOP }, 60 | { NULL, 0, 0 } 61 | }; 62 | 63 | 64 | rie_control_t * 65 | rie_control_new(rie_settings_t *cfg, void *data) 66 | { 67 | int rc; 68 | size_t len; 69 | rie_control_t *ctl; 70 | 71 | len = strlen(cfg->control_socket_path); 72 | if (len - 1 >= sizeof(struct sockaddr_un)) { 73 | rie_log_error(0, "socket path is too long"); 74 | return NULL; 75 | } 76 | 77 | ctl = malloc(sizeof(rie_control_t)); 78 | if (ctl == NULL) { 79 | return NULL; 80 | } 81 | 82 | rie_memzero(ctl, sizeof(struct rie_control_s)); 83 | 84 | ctl->data = data; 85 | ctl->path = cfg->control_socket_path; 86 | 87 | ctl->fd = socket(AF_UNIX, SOCK_DGRAM, 0); 88 | if (ctl->fd == -1) { 89 | rie_log_error(errno, "failed to create socket"); 90 | free(ctl); 91 | return NULL; 92 | } 93 | 94 | rc = fcntl(ctl->fd, F_SETFL, O_NONBLOCK); 95 | if (rc == -1) { 96 | rie_log_error(errno, "failed set socket non-blocking"); 97 | free(ctl); 98 | return NULL; 99 | } 100 | 101 | ctl->sa.sun_family = AF_UNIX; 102 | strcpy(ctl->sa.sun_path, ctl->path); 103 | 104 | (void) unlink(ctl->path); 105 | 106 | rc = bind(ctl->fd, (struct sockaddr *) &ctl->sa, 107 | sizeof(struct sockaddr_un)); 108 | if (rc == -1) { 109 | rie_log_error(errno, "failed to bind socket"); 110 | (void) close(ctl->fd); 111 | free(ctl); 112 | return NULL; 113 | } 114 | 115 | rie_log("listening for user commands at '%s'", ctl->path); 116 | 117 | return ctl; 118 | } 119 | 120 | 121 | /* 122 | * "-m" switch handler, exits immediately after this, 123 | * thus no cleanup, logs, etc 124 | */ 125 | int 126 | rie_control_send_message(char *sockpath, char *msg) 127 | { 128 | int sock; 129 | ssize_t n; 130 | socklen_t slen; 131 | 132 | struct sockaddr_un sa; 133 | 134 | if (strlen(msg) > sizeof(sa.sun_path) - 1) { 135 | fprintf(stderr, "socket name too long\n"); 136 | return RIE_ERROR; 137 | } 138 | 139 | sock = socket(AF_UNIX, SOCK_DGRAM, 0); 140 | if (sock == -1) { 141 | fprintf(stderr, "failed to create socket: %s\n", 142 | strerror(errno)); 143 | return RIE_ERROR; 144 | } 145 | 146 | rie_memzero(&sa, sizeof(struct sockaddr_un)); 147 | 148 | slen = sizeof(struct sockaddr_un); 149 | sa.sun_family = AF_UNIX; 150 | strcpy(sa.sun_path, sockpath); 151 | 152 | n = sendto(sock, msg, strlen(msg), 0, (struct sockaddr *) &sa, slen); 153 | if (n == -1) { 154 | fprintf(stderr, "failed to send control message: %s\n", 155 | strerror(errno)); 156 | return RIE_ERROR; 157 | } 158 | 159 | if (n != strlen(msg)) { 160 | fprintf(stderr, "short write while sending control message: %s\n", 161 | strerror(errno)); 162 | return RIE_ERROR; 163 | } 164 | 165 | return RIE_OK; 166 | } 167 | 168 | 169 | int 170 | rie_control_get_fd(rie_control_t *ctl) 171 | { 172 | return ctl ? ctl->fd : -1; 173 | } 174 | 175 | 176 | int 177 | rie_control_handle_socket_event(rie_control_t *ctl) 178 | { 179 | int found; 180 | ssize_t n; 181 | rie_cmd_desc_t *cmd; 182 | 183 | char buf[RIE_CMD_BUF]; 184 | 185 | do { 186 | 187 | errno = 0; 188 | n = recv(ctl->fd, buf, RIE_CMD_BUF, 0); 189 | 190 | if (errno == EAGAIN) { 191 | return RIE_OK; 192 | } 193 | 194 | if (n < 0) { 195 | rie_log_error(errno, "read() on control socket failed"); 196 | return RIE_ERROR; 197 | } 198 | 199 | found = 0; 200 | 201 | for (cmd = rie_control_messages; cmd->name; cmd++) { 202 | if (n == cmd->len && strncmp(buf, cmd->name, n) == 0) { 203 | rie_log("remote command: \"%.*s\", running", (int) n, buf); 204 | rie_pager_run_cmd(ctl->data, cmd->action); 205 | found = 1; 206 | break; 207 | } 208 | } 209 | 210 | if (!found) { 211 | rie_log("unknown remote command: \"%.*s\", ignored", (int) n, buf); 212 | } 213 | 214 | } while (1); 215 | 216 | return RIE_OK; 217 | } 218 | 219 | 220 | void 221 | rie_control_delete(rie_control_t *ctl, int final) 222 | { 223 | if (ctl == NULL) { 224 | return; 225 | } 226 | 227 | if (ctl->fd != -1) { 228 | (void) close(ctl->fd); 229 | } 230 | 231 | if (final) { 232 | (void) unlink(ctl->path); 233 | } 234 | 235 | free(ctl); 236 | } 237 | 238 | -------------------------------------------------------------------------------- /src/rie_control.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2020 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_CONTROL_H__ 23 | #define __RIE_CONTROL_H__ 24 | 25 | rie_control_t *rie_control_new(rie_settings_t *cfg, void *data); 26 | 27 | void rie_control_delete(rie_control_t *ctl, int final); 28 | 29 | int rie_control_get_fd(rie_control_t *ctl); 30 | 31 | int rie_control_handle_socket_event(rie_control_t *ctl); 32 | 33 | int rie_control_send_message(char *sockpath, char *msg); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/rie_event.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_EVENT_H__ 23 | #define __RIE_EVENT_H__ 24 | 25 | #include "rieman.h" 26 | 27 | #include 28 | 29 | int rie_event_init(rie_t *pager); 30 | void rie_event_cleanup(rie_t *pager); 31 | int rie_event_loop(rie_t *pager, sigset_t *sigmask); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/rie_external.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | 24 | #include 25 | #include 26 | 27 | 28 | int 29 | rie_external_get_desktop_geometry(uint32_t *w, uint32_t *h) 30 | { 31 | /* assume xdpyinfo is in PATH, runnable, etc... */ 32 | static char *cmd = "xdpyinfo |awk '/dimension/ { print $2\"x\" }'"; 33 | 34 | FILE *fp; 35 | char buf[16], *token; 36 | 37 | fp = popen(cmd, "r"); 38 | if (fp == NULL) { 39 | rie_log_error0(errno, "failed to run xdpyinfo"); 40 | return RIE_ERROR; 41 | } 42 | 43 | if (fgets(buf, 16, fp) == NULL) { 44 | pclose(fp); 45 | return RIE_ERROR; 46 | } 47 | 48 | pclose(fp); 49 | 50 | /* simplistic parsing, assuming result is like '800x600x' */ 51 | 52 | token = strtok(buf, "x"); 53 | if (token == NULL) { 54 | rie_log_error0(0, "failed to parse results of xdpyinfo"); 55 | return RIE_ERROR; 56 | } 57 | 58 | *w = atoi(token); 59 | 60 | token = strtok(NULL, "x"); 61 | if (token == NULL) { 62 | rie_log_error0(0, "failed to parse results of xdpyinfo"); 63 | return RIE_ERROR; 64 | } 65 | 66 | *h = atoi(token); 67 | 68 | return RIE_OK; 69 | } 70 | -------------------------------------------------------------------------------- /src/rie_external.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_EXTERNAL_H__ 23 | #define __RIE_EXTERNAL_H__ 24 | 25 | #include "rieman.h" 26 | 27 | int rie_external_get_desktop_geometry(uint32_t *w, uint32_t *h); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/rie_font.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | 30 | struct rie_font_ctx_s { 31 | FcConfig *fontconf; 32 | FT_Library ft_library; 33 | }; 34 | 35 | 36 | rie_font_ctx_t * 37 | rie_font_ctx_new() 38 | { 39 | rie_font_ctx_t *font_ctx; 40 | 41 | font_ctx = malloc(sizeof(rie_font_ctx_t)); 42 | if (font_ctx == NULL) { 43 | rie_log_error0(errno, "malloc()"); 44 | return NULL; 45 | } 46 | 47 | font_ctx->fontconf = FcInitLoadConfigAndFonts(); 48 | if (font_ctx->fontconf == NULL) { 49 | rie_log_error0(0, "FcInitLoadConfigAndFonts() failed"); 50 | return NULL; 51 | } 52 | 53 | if (FT_Init_FreeType(&font_ctx->ft_library)) { 54 | rie_log_error0(0, "FT_Init_FreeType() failed"); 55 | return NULL; 56 | } 57 | 58 | return font_ctx; 59 | } 60 | 61 | 62 | void 63 | rie_font_ctx_cleanup(rie_font_ctx_t *font_ctx) 64 | { 65 | FcConfigDestroy(font_ctx->fontconf); 66 | free(font_ctx); 67 | } 68 | 69 | 70 | int 71 | rie_font_init(rie_font_ctx_t* font_ctx, rie_fc_t *fc) 72 | { 73 | FT_Face ft_face; 74 | FcChar8 *file; 75 | FcResult r; 76 | FcPattern *font; 77 | FcPattern *pat; 78 | 79 | cairo_status_t cs; 80 | cairo_font_face_t *ff; 81 | 82 | pat = FcNameParse((const FcChar8*)(fc->face)); 83 | if (pat == NULL) { 84 | rie_log_error(0, "FcNameParse(\"%s\") failed", fc->face); 85 | return RIE_ERROR; 86 | } 87 | 88 | if (FcConfigSubstitute(font_ctx->fontconf, pat, FcMatchPattern) != FcTrue) { 89 | rie_log_error(0, "FcConfigSubstitute() failed for face %s", fc->face); 90 | return RIE_ERROR; 91 | } 92 | 93 | FcDefaultSubstitute(pat); 94 | 95 | font = FcFontMatch(font_ctx->fontconf, pat, &r); 96 | if (font == NULL) { 97 | rie_log_error(0, "FcFontMatch() failed for face %s", fc->face); 98 | return RIE_ERROR; 99 | } 100 | 101 | if (FcPatternGetString(font, FC_FILE, 0, &file) != FcResultMatch) { 102 | FcPatternDestroy(font); 103 | rie_log_error(0, "FcPatternGetString(\"%s\") failed", file); 104 | return RIE_ERROR; 105 | } 106 | 107 | FcPatternDestroy(pat); 108 | 109 | if (FT_New_Face(font_ctx->ft_library, (char *) file, 0, &ft_face)) { 110 | rie_log_error(0, "FT_New_Face() for file \"%s\" failed", file); 111 | return RIE_ERROR; 112 | } 113 | 114 | rie_log("Using \"%s\" for specified font \"%s\"", file, fc->face); 115 | 116 | /* 'file' is allocated somewhere inside 'font' */ 117 | FcPatternDestroy(font); 118 | 119 | ff = cairo_ft_font_face_create_for_ft_face(ft_face, 0); 120 | 121 | cs = cairo_font_face_status(ff); 122 | if (cs != CAIRO_STATUS_SUCCESS) { 123 | rie_log_str_error0(cairo_status_to_string(cs), 124 | "cairo_ft_font_face_create_for_ft_face()"); 125 | return RIE_ERROR; 126 | } 127 | 128 | cs = cairo_font_face_set_user_data(ff, 129 | (cairo_user_data_key_t *) ft_face, 130 | ft_face, (cairo_destroy_func_t) FT_Done_Face); 131 | 132 | if (cs != CAIRO_STATUS_SUCCESS) { 133 | rie_log_str_error0(cairo_status_to_string(cs), 134 | "cairo_font_face_set_user_data()"); 135 | cairo_font_face_destroy(ff); 136 | FT_Done_Face(ft_face); 137 | return RIE_ERROR; 138 | } 139 | 140 | fc->font = (rie_font_t *) ff; 141 | 142 | return RIE_OK; 143 | } 144 | 145 | 146 | void 147 | rie_font_cleanup(rie_font_t *font) 148 | { 149 | cairo_font_face_destroy((cairo_font_face_t*)font); 150 | } 151 | -------------------------------------------------------------------------------- /src/rie_font.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_FONT_H__ 23 | #define __RIE_FONT_H__ 24 | 25 | typedef struct rie_font_ctx_s rie_font_ctx_t; 26 | typedef struct rie_font_s rie_font_t; 27 | 28 | #include "rie_gfx.h" 29 | 30 | typedef struct { 31 | char *face; 32 | int points; 33 | double alpha; 34 | rie_color_t color; 35 | rie_font_t *font; 36 | } rie_fc_t; 37 | 38 | rie_font_ctx_t *rie_font_ctx_new(); 39 | void rie_font_ctx_cleanup(rie_font_ctx_t *font_ctx); 40 | int rie_font_init(rie_font_ctx_t* font_ctx, rie_fc_t *fc); 41 | void rie_font_cleanup(rie_font_t *font); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/rie_gfx.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_GFX_H__ 23 | #define __RIE_GFX_H__ 24 | 25 | #define rie_gfx_xy_inside_rect(dx, dy, box) \ 26 | (((dx) > (box)->x) && ((dx) < (box)->x + (box)->w) \ 27 | && ((dy) > (box)->y) && ((dy) < (box)->y + (box)->h)) 28 | 29 | typedef struct rie_surface_s rie_surface_t; 30 | typedef struct rie_pattern_s rie_pattern_t; 31 | typedef struct rie_texture_s rie_texture_t; 32 | 33 | typedef struct { 34 | int32_t x; 35 | int32_t y; 36 | uint32_t w; 37 | uint32_t h; 38 | } rie_rect_t; 39 | 40 | typedef struct rie_clip_s rie_clip_t; 41 | 42 | struct rie_clip_s { 43 | rie_rect_t *box; 44 | rie_clip_t *parent; 45 | }; 46 | 47 | typedef struct { 48 | double r; 49 | double g; 50 | double b; 51 | } rie_color_t; 52 | 53 | struct rie_image_s { 54 | rie_rect_t box; /* geometry */ 55 | rie_surface_t *tx; 56 | }; 57 | 58 | #include "rie_font.h" 59 | 60 | rie_gfx_t *rie_gfx_new(rie_xcb_t *xcb); 61 | void rie_gfx_delete(rie_gfx_t *gc); 62 | 63 | void rie_gfx_resize(rie_gfx_t *gc, int w, int h); 64 | 65 | void rie_gfx_render_start(rie_gfx_t *gc); 66 | void rie_gfx_render_done(rie_gfx_t *gc); 67 | 68 | int rie_gfx_render_patch(rie_gfx_t *gc, rie_texture_t *tspec, rie_rect_t *dst, 69 | rie_rect_t *src, rie_clip_t *clip); 70 | int rie_gfx_render_texture(rie_gfx_t *gc, rie_texture_t *tspec, rie_rect_t *dst, 71 | rie_clip_t *clip); 72 | int rie_gfx_draw_text(rie_gfx_t *gc, rie_fc_t *fc, char *text, rie_rect_t *box, 73 | rie_clip_t *clip); 74 | rie_rect_t rie_gfx_text_bounding_box(rie_gfx_t *gc, rie_fc_t *fc, char *text); 75 | 76 | rie_surface_t *rie_gfx_surface_from_png(char *filename, int *w, int *h); 77 | rie_surface_t *rie_gfx_surface_from_clip(rie_surface_t *surface, int x, int y, 78 | int w, int h); 79 | rie_surface_t *rie_gfx_surface_from_icon(rie_gfx_t *gc, void *data, 80 | int w, int h); 81 | rie_surface_t *rie_gfx_surface_from_zpixmap(rie_gfx_t *gc, uint32_t *data, 82 | int w, int h); 83 | void rie_gfx_surface_free(rie_surface_t *surface); 84 | 85 | rie_pattern_t *rie_gfx_pattern_from_surface(rie_surface_t *surface); 86 | void rie_gfx_pattern_free(rie_pattern_t *pat); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/rie_gfx_cairo.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | #include "rie_xcb.h" 24 | #include "rie_skin.h" 25 | 26 | #include 27 | 28 | #define rie_gfx_set_source_rgba(cr, col, a) \ 29 | cairo_set_source_rgba(cr, (col)->r, (col)->g, (col)->b, a); 30 | 31 | #define CS(x) ((cairo_surface_t*) (x)) 32 | 33 | 34 | struct rie_gfx_s { 35 | cairo_t *cr; 36 | cairo_surface_t *surface; 37 | }; 38 | 39 | 40 | rie_gfx_t * 41 | rie_gfx_new(rie_xcb_t *xcb) 42 | { 43 | rie_gfx_t *gc; 44 | 45 | xcb_window_t win; 46 | xcb_visualtype_t *visual; 47 | xcb_connection_t *c; 48 | 49 | cairo_status_t cs; 50 | 51 | gc = malloc(sizeof(rie_gfx_t)); 52 | if (gc == NULL) { 53 | rie_log_error0(errno, "malloc"); 54 | return NULL; 55 | } 56 | 57 | visual = rie_xcb_root_visual(xcb); 58 | if (visual == NULL) { 59 | return NULL; 60 | } 61 | 62 | c = rie_xcb_get_connection(xcb); 63 | win = rie_xcb_get_window(xcb); 64 | 65 | gc->surface = cairo_xcb_surface_create(c, win, visual, 1, 1); 66 | 67 | cs = cairo_surface_status(gc->surface); 68 | if (cs != CAIRO_STATUS_SUCCESS) { 69 | rie_log_str_error0(cairo_status_to_string(cs), 70 | "cairo_xcb_surface_create()"); 71 | return NULL; 72 | } 73 | 74 | gc->cr = cairo_create(gc->surface); 75 | 76 | cs = cairo_status(gc->cr); 77 | if (cs != CAIRO_STATUS_SUCCESS) { 78 | rie_log_str_error0(cairo_status_to_string(cs), "cairo_create()"); 79 | return NULL; 80 | } 81 | 82 | /* referenced by cairo context, no need to maintain separately */ 83 | cairo_surface_destroy(gc->surface); 84 | 85 | return gc; 86 | } 87 | 88 | 89 | void 90 | rie_gfx_delete(rie_gfx_t *gc) 91 | { 92 | cairo_destroy(gc->cr); 93 | free(gc); 94 | } 95 | 96 | 97 | void 98 | rie_gfx_render_start(rie_gfx_t *gc) 99 | { 100 | cairo_push_group(gc->cr); 101 | } 102 | 103 | void 104 | rie_gfx_render_done(rie_gfx_t *gc) 105 | { 106 | cairo_pop_group_to_source(gc->cr); 107 | 108 | cairo_paint(gc->cr); 109 | cairo_surface_flush(gc->surface); 110 | } 111 | 112 | void 113 | rie_gfx_resize(rie_gfx_t *gc, int w, int h) 114 | { 115 | cairo_xcb_surface_set_size(gc->surface, w, h); 116 | } 117 | 118 | void 119 | rie_gfx_surface_free(rie_surface_t *surface) 120 | { 121 | cairo_surface_destroy((cairo_surface_t*)surface); 122 | } 123 | 124 | 125 | int 126 | rie_gfx_render_patch(rie_gfx_t *gc, rie_texture_t *tspec, rie_rect_t *dst, 127 | rie_rect_t *src, rie_clip_t *clip) 128 | { 129 | rie_pattern_t *pat; 130 | 131 | if (tspec->type == RIE_TX_TYPE_NONE) { 132 | return RIE_OK; 133 | } 134 | 135 | if (dst->w == 0 || dst->h == 0) { 136 | /* do not try to render invisible items */ 137 | return RIE_OK; 138 | } 139 | 140 | cairo_save(gc->cr); 141 | 142 | while (clip) { 143 | cairo_rectangle(gc->cr, clip->box->x, clip->box->y, 144 | clip->box->w, clip->box->h); 145 | cairo_clip(gc->cr); 146 | clip = clip->parent; 147 | } 148 | 149 | if (src) { 150 | /* root background, must be tiled and shifted */ 151 | 152 | pat = rie_gfx_pattern_from_surface(tspec->tx); 153 | 154 | cairo_rectangle(gc->cr, dst->x, dst->y, dst->w, dst->h); 155 | cairo_translate(gc->cr, -src->x, -src->y); 156 | 157 | cairo_set_source(gc->cr, (cairo_pattern_t *) pat); 158 | 159 | } else { 160 | 161 | cairo_set_source_surface(gc->cr, CS(tspec->tx), dst->x, dst->y); 162 | 163 | cairo_rectangle(gc->cr, 0, 0, dst->w, dst->h); 164 | } 165 | 166 | cairo_clip(gc->cr); 167 | 168 | cairo_paint_with_alpha(gc->cr, tspec->alpha); 169 | 170 | cairo_restore(gc->cr); 171 | 172 | return RIE_OK; 173 | } 174 | 175 | 176 | int 177 | rie_gfx_render_texture(rie_gfx_t *gc, rie_texture_t *tspec, rie_rect_t *dst, 178 | rie_clip_t *clip) 179 | { 180 | double dx, dy; 181 | 182 | if (tspec->type == RIE_TX_TYPE_NONE) { 183 | return RIE_OK; 184 | } 185 | 186 | if (dst->w == 0 || dst->h == 0) { 187 | /* do not try to render invisible items */ 188 | return RIE_OK; 189 | } 190 | 191 | cairo_save(gc->cr); 192 | 193 | while (clip) { 194 | cairo_rectangle(gc->cr, clip->box->x, clip->box->y, 195 | clip->box->w, clip->box->h); 196 | cairo_clip(gc->cr); 197 | clip = clip->parent; 198 | } 199 | 200 | if (tspec->type == RIE_TX_TYPE_COLOR) { 201 | 202 | rie_gfx_set_source_rgba(gc->cr, &tspec->color, tspec->alpha); 203 | cairo_rectangle(gc->cr, dst->x, dst->y, dst->w, dst->h); 204 | cairo_fill(gc->cr); 205 | 206 | 207 | } else if (tspec->type == RIE_TX_TYPE_PATTERN) { 208 | cairo_translate(gc->cr, dst->x, dst->y); 209 | cairo_rectangle(gc->cr, 0, 0, dst->w, dst->h); 210 | 211 | cairo_set_source(gc->cr, (cairo_pattern_t *) tspec->pat); 212 | 213 | cairo_clip(gc->cr); 214 | cairo_paint_with_alpha(gc->cr, tspec->alpha); 215 | 216 | } else { 217 | 218 | /* RIE_TX_TYPE_TEXTURE */ 219 | 220 | dx = cairo_image_surface_get_width(CS(tspec->tx)); 221 | dy = cairo_image_surface_get_height(CS(tspec->tx)); 222 | 223 | dx = ((double)dst->w)/dx; 224 | dy = ((double)dst->h)/dy; 225 | 226 | if (dx == 0 || dy == 0) { 227 | /* cairo_scale() will reject such values and put context 228 | * into an error state, preventing further drawing 229 | */ 230 | cairo_restore(gc->cr); 231 | return RIE_OK; 232 | } 233 | 234 | cairo_push_group(gc->cr); 235 | 236 | cairo_translate(gc->cr, dst->x, dst->y); 237 | cairo_rectangle(gc->cr, 0, 0, dst->w, dst->h); 238 | cairo_scale(gc->cr, dx, dy); 239 | cairo_set_source_surface(gc->cr, CS(tspec->tx), 0, 0); 240 | 241 | cairo_fill(gc->cr); 242 | 243 | cairo_pop_group_to_source(gc->cr); 244 | 245 | cairo_paint_with_alpha(gc->cr, tspec->alpha); 246 | } 247 | 248 | cairo_restore(gc->cr); 249 | 250 | return RIE_OK; 251 | } 252 | 253 | 254 | int 255 | rie_gfx_draw_text(rie_gfx_t *gc, rie_fc_t *fc, char *text, rie_rect_t *box, 256 | rie_clip_t *clip) 257 | { 258 | cairo_text_extents_t te; 259 | 260 | cairo_save(gc->cr); 261 | 262 | while (clip) { 263 | cairo_rectangle(gc->cr, clip->box->x, clip->box->y, 264 | clip->box->w, clip->box->h); 265 | cairo_clip(gc->cr); 266 | clip = clip->parent; 267 | } 268 | 269 | cairo_set_font_face(gc->cr, (cairo_font_face_t *) fc->font); 270 | cairo_set_font_size(gc->cr, fc->points); 271 | cairo_text_extents(gc->cr, text, &te); 272 | rie_gfx_set_source_rgba(gc->cr, &fc->color, fc->alpha); 273 | 274 | 275 | cairo_move_to(gc->cr, box->x - te.x_bearing, box->y - te.y_bearing); 276 | cairo_show_text(gc->cr, text); 277 | cairo_restore(gc->cr); 278 | 279 | return RIE_OK; 280 | } 281 | 282 | 283 | rie_rect_t 284 | rie_gfx_text_bounding_box(rie_gfx_t *gc, rie_fc_t *fc, char *text) 285 | { 286 | rie_rect_t res; 287 | cairo_text_extents_t bb; 288 | 289 | cairo_save(gc->cr); 290 | cairo_set_font_face(gc->cr, (cairo_font_face_t *) fc->font); 291 | cairo_set_font_size(gc->cr, fc->points); 292 | cairo_text_extents(gc->cr, text, &bb); 293 | cairo_restore(gc->cr); 294 | 295 | res.w = bb.width; 296 | res.h = bb.height; 297 | res.x = bb.x_bearing; 298 | res.y = bb.y_bearing; 299 | 300 | return res; 301 | } 302 | 303 | 304 | static void 305 | rie_gfx_pre_multiply_alpha(uint32_t *pixels, size_t size) 306 | { 307 | int i; 308 | double alpha; 309 | uint32_t a, r, g, b; 310 | 311 | for (i = 0; i < size; i++) { 312 | 313 | a = (pixels[i] & 0xFF000000) >> 24; 314 | r = (pixels[i] & 0x00FF0000) >> 16; 315 | g = (pixels[i] & 0x0000FF00) >> 8; 316 | b = (pixels[i] & 0x000000FF); 317 | 318 | alpha = ((double) a) / 255; 319 | 320 | r *= alpha; 321 | g *= alpha; 322 | b *= alpha; 323 | 324 | pixels[i] = (a << 24) | (r << 16) | (g << 8) | b; 325 | } 326 | } 327 | 328 | 329 | rie_surface_t * 330 | rie_gfx_surface_from_icon(rie_gfx_t *gc, void *data, int w, int h) 331 | { 332 | uint32_t *pixels; 333 | cairo_status_t cs; 334 | cairo_surface_t *surface; 335 | 336 | /* 337 | * NET_WM_ICON: 338 | * 339 | * This is an array of 32bit packed CARDINAL ARGB with high byte being A, 340 | * low byte being B. The first two cardinals are width, height. Data is in 341 | * rows, left to right and top to bottom. 342 | */ 343 | surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); 344 | cs = cairo_surface_status(surface); 345 | if (cs != CAIRO_STATUS_SUCCESS) { 346 | rie_log_str_error0(cairo_status_to_string(cs), 347 | "cairo_image_surface_create()"); 348 | return NULL; 349 | } 350 | 351 | pixels = (uint32_t *) cairo_image_surface_get_data(surface); 352 | 353 | memcpy(pixels, data, (w * h) * sizeof(uint32_t)); 354 | 355 | /* cairo expects pre-multiplied alpha */ 356 | rie_gfx_pre_multiply_alpha(pixels, (w * h)); 357 | 358 | cairo_surface_mark_dirty(surface); 359 | 360 | return (rie_surface_t *) surface; 361 | } 362 | 363 | 364 | rie_surface_t * 365 | rie_gfx_surface_from_zpixmap(rie_gfx_t *gc, uint32_t *data, int w, int h) 366 | { 367 | uint32_t *pixels; 368 | cairo_status_t cs; 369 | cairo_surface_t *surface; 370 | 371 | int x,y; 372 | 373 | surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,w, h); 374 | 375 | cs = cairo_surface_status(surface); 376 | if (cs != CAIRO_STATUS_SUCCESS) { 377 | rie_log_str_error0(cairo_status_to_string(cs), 378 | "cairo_image_surface_create()"); 379 | return NULL; 380 | } 381 | 382 | pixels = (uint32_t *) cairo_image_surface_get_data(surface); 383 | 384 | /* convert 24-bit RGB to 32-bit ARGB */ 385 | for (y = 0; y < h; y++) { 386 | for (x = 0; x < w; x++) { 387 | 388 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 389 | #error TODO: extend to 32 with alpha properly on BE 390 | #else 391 | pixels[y * w + x] = data[y * w + x] | 0xFF000000; 392 | #endif 393 | } 394 | } 395 | 396 | cairo_surface_mark_dirty(surface); 397 | 398 | return (rie_surface_t *) surface; 399 | } 400 | 401 | 402 | rie_surface_t * 403 | rie_gfx_surface_from_png(char *fname, int *w, int *h) 404 | { 405 | cairo_status_t cs; 406 | cairo_surface_t *surface; 407 | 408 | surface = cairo_image_surface_create_from_png(fname); 409 | 410 | cs = cairo_surface_status(surface); 411 | if (cs != CAIRO_STATUS_SUCCESS) { 412 | rie_log_str_error(cairo_status_to_string(cs), 413 | "cairo_image_surface_create_from_png('%s')", fname); 414 | return NULL; 415 | } 416 | 417 | if (w) { 418 | *w = cairo_image_surface_get_width(surface); 419 | } 420 | 421 | if (h) { 422 | *h = cairo_image_surface_get_height(surface); 423 | } 424 | 425 | return (rie_surface_t *) surface; 426 | } 427 | 428 | 429 | rie_surface_t * 430 | rie_gfx_surface_from_clip(rie_surface_t *surface, int x, int y, int w, int h) 431 | { 432 | cairo_status_t cs; 433 | cairo_surface_t *tx; 434 | 435 | tx = cairo_surface_create_for_rectangle(CS(surface), x, y, w, h); 436 | 437 | cs = cairo_surface_status(tx); 438 | if (cs != CAIRO_STATUS_SUCCESS) { 439 | rie_log_str_error0(cairo_status_to_string(cs), 440 | "cairo_surface_create_for_rectangle()"); 441 | return NULL; 442 | } 443 | 444 | return (rie_surface_t *) tx; 445 | } 446 | 447 | 448 | rie_pattern_t * 449 | rie_gfx_pattern_from_surface(rie_surface_t *surface) 450 | { 451 | cairo_status_t cs; 452 | cairo_pattern_t *pat; 453 | 454 | pat = cairo_pattern_create_for_surface(CS(surface)); 455 | 456 | cs = cairo_pattern_status(pat); 457 | if (cs != CAIRO_STATUS_SUCCESS) { 458 | rie_log_str_error0(cairo_status_to_string(cs), 459 | "cairo_pattern_create_for_surface()"); 460 | return NULL; 461 | } 462 | 463 | cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT); 464 | 465 | return (rie_pattern_t *) pat; 466 | } 467 | 468 | 469 | void 470 | rie_gfx_pattern_free(rie_pattern_t *pat) 471 | { 472 | cairo_pattern_destroy((cairo_pattern_t *)pat); 473 | } 474 | -------------------------------------------------------------------------------- /src/rie_log.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2022 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | #include "rie_external.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | struct rie_log_s { 35 | char *name; 36 | FILE *file; 37 | #if defined(RIE_DEBUG) 38 | rie_array_t backtrace; 39 | #endif 40 | }; 41 | 42 | 43 | static rie_log_t *rie_logp; 44 | 45 | rie_log_t * 46 | rie_log_new(char *filename) 47 | { 48 | int err; 49 | 50 | rie_logp = malloc(sizeof(rie_log_t)); 51 | if (rie_logp == NULL) { 52 | return NULL; 53 | }; 54 | 55 | rie_memzero(rie_logp, sizeof(rie_log_t)); 56 | 57 | if (filename == NULL) { 58 | rie_logp->file = stdout; 59 | return rie_logp; 60 | } 61 | 62 | rie_logp->file = fopen(filename, "w"); 63 | if (rie_logp->file == NULL) { 64 | err = errno; 65 | fprintf(stdout, "failed to open '%s' for logging: %s, using stdout\n", 66 | filename, strerror(err)); 67 | 68 | free(rie_logp); 69 | return NULL; 70 | } 71 | 72 | rie_logp->name = filename; 73 | 74 | (void) setvbuf(rie_logp->file, NULL, _IOLBF, 0); 75 | 76 | return rie_logp; 77 | } 78 | 79 | 80 | void 81 | rie_log_delete(rie_log_t *log) 82 | { 83 | fflush(log->file); 84 | 85 | if (log->file != stdout) { 86 | fclose(log->file); 87 | } 88 | #if defined(RIE_DEBUG) 89 | if (log->backtrace.nitems) { 90 | free(log->backtrace.data); 91 | log->backtrace.nitems = 0; 92 | } 93 | #endif 94 | free(log); 95 | } 96 | 97 | 98 | static void 99 | rie_log_cat(int out, char *filename) 100 | { 101 | int fd, err; 102 | char buf[4096]; 103 | size_t n; 104 | 105 | fd = -1; 106 | 107 | fd = open(filename, 0); 108 | if (-1 == fd) { 109 | goto failed; 110 | } 111 | 112 | while ((n = read(fd, buf, 4096))) { 113 | 114 | if (-1 == n) { 115 | goto failed; 116 | } 117 | 118 | n = write(out, buf, n); 119 | if (-1 == n) { 120 | goto failed; 121 | } 122 | } 123 | 124 | (void) close(fd); 125 | 126 | return; 127 | 128 | failed: 129 | 130 | err = errno; 131 | 132 | fprintf(stderr, ">> failed to display log file '%s' due to error '%s'\n", 133 | filename, strerror(err)); 134 | 135 | if (-1 != fd) { 136 | (void) close(fd); 137 | } 138 | } 139 | 140 | 141 | void 142 | rie_log_dump() 143 | { 144 | if (rie_logp->name) { 145 | fprintf(stderr, ">> error log below:\n"); 146 | rie_log_cat(fileno(stderr), rie_logp->name); 147 | } 148 | fflush(stdout); 149 | } 150 | 151 | 152 | static inline void 153 | rie_log_time_prefix(FILE *out) 154 | { 155 | int err; 156 | time_t tm; 157 | struct tm *ti; 158 | 159 | char buf[80]; 160 | 161 | tm = time(NULL); 162 | ti = localtime(&tm); 163 | 164 | if (strftime(buf, sizeof(buf), "[%F %H:%M:%S %Z]", ti) == 0) { 165 | err = errno; 166 | fprintf(rie_logp->file, "[time fail: strftime(): %s]", strerror(err)); 167 | return; 168 | } 169 | 170 | fprintf(out, "%s", buf); 171 | } 172 | 173 | 174 | void 175 | rie_log_sys_error_real(char *file, int line, int err, char *fmt, ...) 176 | { 177 | va_list ap; 178 | 179 | rie_log_time_prefix(rie_logp->file); 180 | 181 | fprintf(rie_logp->file, " -err- "); 182 | 183 | va_start(ap, fmt); 184 | (void) vfprintf(rie_logp->file, fmt, ap); 185 | va_end(ap); 186 | 187 | if (err) { 188 | fprintf(rie_logp->file, " - %s", strerror(err)); 189 | } 190 | 191 | fprintf(rie_logp->file, ", at %s:%d\n", file, line); 192 | 193 | #if defined (RIE_DEBUG) 194 | (void) rie_backtrace_save(&rie_logp->backtrace); 195 | #endif 196 | 197 | } 198 | 199 | void 200 | rie_log_str_error_real_v(char *file, int line, const char *err, char *fmt, 201 | va_list ap) 202 | { 203 | rie_log_time_prefix(rie_logp->file); 204 | 205 | fprintf(rie_logp->file, " -err- "); 206 | 207 | (void) vfprintf(rie_logp->file, fmt, ap); 208 | 209 | fprintf(rie_logp->file, " - %s, at %s:%d\n", err, file, line); 210 | 211 | #if defined (RIE_DEBUG) 212 | (void) rie_backtrace_save(&rie_logp->backtrace); 213 | #endif 214 | } 215 | 216 | 217 | void 218 | rie_log_str_error_real(char *file, int line, const char *err, 219 | char *fmt, ...) 220 | { 221 | va_list ap; 222 | 223 | va_start(ap, fmt); 224 | rie_log_str_error_real_v(file, line, err, fmt, ap); 225 | va_end(ap); 226 | } 227 | 228 | 229 | void 230 | rie_debug_real(char *fmt, ...) 231 | { 232 | va_list ap; 233 | 234 | rie_log_time_prefix(rie_logp->file); 235 | 236 | fprintf(rie_logp->file, " -dbg- "); 237 | 238 | va_start(ap, fmt); 239 | (void) vfprintf(rie_logp->file, fmt, ap); 240 | va_end(ap); 241 | 242 | fprintf(rie_logp->file, "\n"); 243 | } 244 | 245 | 246 | void 247 | rie_log(char *fmt, ...) 248 | { 249 | va_list ap; 250 | 251 | rie_log_time_prefix(rie_logp->file); 252 | 253 | fprintf(rie_logp->file, " -log- "); 254 | 255 | va_start(ap, fmt); 256 | (void) vfprintf(rie_logp->file, fmt, ap); 257 | va_end(ap); 258 | 259 | fprintf(rie_logp->file, "\n"); 260 | } 261 | 262 | 263 | #if defined(RIE_DEBUG) 264 | 265 | void 266 | rie_log_backtrace() 267 | { 268 | int i; 269 | char **frames; 270 | size_t n; 271 | 272 | frames = rie_logp->backtrace.data; 273 | n = rie_logp->backtrace.nitems; 274 | 275 | if (n < 2) { 276 | rie_debug0("backtrace is too short, skipping"); 277 | return; 278 | } 279 | 280 | /* skip this function itself and our caller - some kind of log_error() */ 281 | for (i = 2; i < n; i++) { 282 | rie_debug("frame %2d : %s", n - i, frames[i]); 283 | } 284 | } 285 | 286 | #endif 287 | -------------------------------------------------------------------------------- /src/rie_log.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_LOG_H_ 23 | #define __RIE_LOG_H_ 24 | 25 | #include "rieman.h" 26 | 27 | #include 28 | 29 | 30 | #define rie_log_error(err, fmt, ...) \ 31 | rie_log_sys_error_real( __FILE__, __LINE__, err, fmt, ##__VA_ARGS__) 32 | 33 | #define rie_log_str_error(str, fmt, ...) \ 34 | rie_log_str_error_real(__FILE__, __LINE__, str, fmt, ##__VA_ARGS__) 35 | 36 | #define rie_log_error0(err, msg) rie_log_error(err, "%s", msg) 37 | 38 | #define rie_log_str_error0(str, msg) rie_log_str_error(str, "%s", msg) 39 | 40 | #if defined(RIE_DEBUG) 41 | 42 | #define rie_debug(fmt, ...) rie_debug_real(fmt, ##__VA_ARGS__) 43 | 44 | #define rie_debug0(msg) rie_debug("%s", msg) 45 | 46 | void rie_log_backtrace(); 47 | 48 | #else 49 | 50 | #define rie_debug(fmt, ...) 51 | 52 | #endif 53 | 54 | typedef struct rie_log_s rie_log_t; 55 | 56 | rie_log_t *rie_log_new(char *filename); 57 | void rie_log_delete(rie_log_t *log); 58 | void rie_log_dump(); 59 | 60 | void rie_log(char *fmt, ...); 61 | 62 | void rie_debug_real(char *fmt, ...); 63 | 64 | void rie_log_sys_error_real(char *file, int line, int err, 65 | char *fmt, ...); 66 | 67 | void rie_log_str_error_real(char *file, int line, const char *err, 68 | char *fmt, ...); 69 | 70 | void rie_log_str_error_real_v(char *file, int line, const char *err, char *fmt, 71 | va_list ap); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/rie_render.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_RENDER_H__ 23 | #define __RIE_RENDER_H__ 24 | 25 | int rie_render(rie_t *pager); 26 | int rie_desktop_by_coords(rie_t *pager, int x, int y); 27 | int rie_viewport_by_coords(rie_t *pager, int x, int y, int *new_x, int *new_y); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/rie_skin.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_SKIN_H__ 23 | #define __RIE_SKIN_H__ 24 | 25 | #include "rie_font.h" 26 | 27 | 28 | typedef enum { 29 | RIE_TX_BACKGROUND = 0, 30 | RIE_TX_DESKTOP, 31 | RIE_TX_CURRENT_DESKTOP, 32 | RIE_TX_DESKTOP_NAME_BG, 33 | RIE_TX_WINDOW, 34 | RIE_TX_WINDOW_FOCUSED, 35 | RIE_TX_WINDOW_ATTENTION, 36 | RIE_TX_MISSING_ICON, 37 | RIE_TX_VIEWPORT, 38 | RIE_TX_VIEWPORT_ACTIVE, 39 | RIE_TX_LAST 40 | } rie_skin_texture_t; 41 | 42 | typedef enum { 43 | RIE_FONT_DESKTOP_NAME = 0, 44 | RIE_FONT_WINDOW_NAME, 45 | RIE_FONT_DESKTOP_NUMBER, 46 | RIE_FONT_LAST, 47 | } rie_skin_font_t; 48 | 49 | typedef enum { 50 | RIE_TX_TYPE_NONE = 0, 51 | RIE_TX_TYPE_COLOR, 52 | RIE_TX_TYPE_TEXTURE, 53 | RIE_TX_TYPE_PATTERN, 54 | RIE_TX_TYPE_LAST 55 | } rie_skin_type_t; 56 | 57 | typedef enum { 58 | RIE_BORDER_PAGER, 59 | RIE_BORDER_DESKTOP_ACTIVE, 60 | RIE_BORDER_WINDOW, 61 | RIE_BORDER_WINDOW_FOCUSED, 62 | RIE_BORDER_WINDOW_ATTENTION, 63 | RIE_BORDER_VIEWPORT, 64 | RIE_BORDER_VIEWPORT_ACTIVE, 65 | RIE_BORDER_LAST 66 | } rie_skin_border_t; 67 | 68 | 69 | typedef enum { /* Frame layout: */ 70 | RIE_FRAME_TOP, 71 | RIE_FRAME_BOTTOM, /* +--+---+--+ */ 72 | RIE_FRAME_LEFT, /* |TL| T |TR| */ 73 | RIE_FRAME_RIGHT, /* |--+---+--+ */ 74 | RIE_FRAME_TOPLEFT, /* | L| * | R| */ 75 | RIE_FRAME_BOTTOMLEFT, /* +--+---+--+ */ 76 | RIE_FRAME_TOPRIGHT, /* |BL| B |BR| */ 77 | RIE_FRAME_BOTTOMRIGHT, /* +--+---+--+ */ 78 | RIE_FRAME_LAST 79 | } rie_frame_t; 80 | 81 | 82 | /* order corresponds to 4x4 grid position in image file */ 83 | typedef enum { 84 | RIE_GRID_CORNER_TOPLEFT, /* ┌ */ 85 | RIE_GRID_HORIZONTAL_TOP, /* ─ */ 86 | RIE_GRID_MIDDLE_TOP, /* ┬ */ 87 | RIE_GRID_CORNER_TOPRIGHT, /* ┐ */ 88 | 89 | RIE_GRID_VERTICAL_LEFT, /* │ */ 90 | RIE_GRID_CROSS, /* ┼ */ 91 | RIE_GRID_RESERVED1, 92 | RIE_GRID_VERTICAL_RIGHT, /* │ */ 93 | 94 | RIE_GRID_MIDDLE_LEFT, /* ├ */ 95 | RIE_GRID_RESERVED2, 96 | RIE_GRID_RESERVED3, 97 | RIE_GRID_MIDDLE_RIGHT, /* ┤ */ 98 | 99 | RIE_GRID_CORNER_BOTTOMLEFT, /* └ */ 100 | RIE_GRID_HORIZONTAL_BOTTOM, /* ─ */ 101 | RIE_GRID_MIDDLE_BOTTOM, /* ┴ */ 102 | RIE_GRID_CORNER_BOTTOMRIGHT, /* ┘ */ 103 | 104 | RIE_GRID_LAST 105 | } rie_grid_t; 106 | 107 | 108 | typedef struct { 109 | rie_rect_t box; 110 | rie_grid_t type; 111 | } rie_grid_elem_t; 112 | 113 | 114 | typedef union { 115 | rie_surface_t *surf; 116 | rie_pattern_t *pat; 117 | } rie_tile_t; 118 | 119 | typedef struct { 120 | rie_skin_type_t type; 121 | uint32_t w; 122 | rie_color_t color; 123 | rie_surface_t *tx; 124 | char *tile_src; 125 | double alpha; 126 | rie_tile_t tiles[RIE_GRID_LAST]; 127 | } rie_border_t; 128 | 129 | 130 | struct rie_texture_s { 131 | rie_skin_type_t type; 132 | rie_color_t color; 133 | rie_surface_t *tx; 134 | rie_pattern_t *pat; 135 | char *image; 136 | uint8_t img_is_root; 137 | rie_border_t *border; 138 | double alpha; 139 | char *tag; /* for debugging purposes */ 140 | }; 141 | 142 | 143 | rie_skin_t *rie_skin_new(char *name, rie_gfx_t *gc); 144 | void rie_skin_delete(rie_skin_t *skin); 145 | 146 | rie_texture_t *rie_skin_texture(rie_skin_t *skin, rie_skin_texture_t elem); 147 | rie_border_t *rie_skin_border(rie_skin_t *skin, rie_skin_border_t elem); 148 | rie_fc_t *rie_skin_font(rie_skin_t *skin, rie_skin_font_t elem); 149 | double rie_skin_icon_alpha(rie_skin_t *skin); 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /src/rie_util.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2022 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define RIE_BT_BUF_SIZE 64 33 | 34 | 35 | int 36 | rie_array_init(rie_array_t *array, size_t nitems, size_t item_len, 37 | rie_array_free_pt free_func) 38 | { 39 | array->data = malloc(nitems * item_len); 40 | if (array->data == NULL) { 41 | return RIE_ERROR; 42 | } 43 | 44 | array->nitems = nitems; 45 | 46 | rie_memzero(array->data, nitems * item_len); 47 | 48 | array->xfree = free_func; 49 | 50 | return RIE_OK; 51 | } 52 | 53 | void 54 | rie_array_wipe(rie_array_t *array) 55 | { 56 | if (array->xfree) { 57 | array->xfree(array->data, array->nitems); 58 | } 59 | 60 | free(array->data); 61 | array->data = NULL; 62 | } 63 | 64 | 65 | static void 66 | rie_util_free_str_list(void *data, size_t nitems) 67 | { 68 | char **strings = data; 69 | free(strings[0]); 70 | } 71 | 72 | 73 | int 74 | rie_str_list_to_array(char *in, size_t len, rie_array_t *res) 75 | { 76 | int i, j, n; 77 | char *data, *p; 78 | char **items; 79 | 80 | n = 0; 81 | 82 | data = malloc(len + 1); 83 | if (data == NULL) { 84 | rie_log_error0(errno, "malloc"); 85 | return RIE_ERROR; 86 | } 87 | 88 | memcpy(data, in, len); 89 | data[len] = 0; 90 | 91 | for (n = 0, i = 0; i < len; i++) { 92 | if (data[i] == 0) { 93 | n++; 94 | } 95 | } 96 | 97 | if (data[len - 1] != 0) { 98 | n++; 99 | } 100 | 101 | items = malloc(n * sizeof(char*)); 102 | if (items == NULL) { 103 | rie_log_error0(errno, "malloc"); 104 | return RIE_ERROR; 105 | } 106 | 107 | p = data; 108 | 109 | for (i = 0, j = 0; i < len; i++) { 110 | if (data[i] == 0) { 111 | items[j++] = p; 112 | p = &data[i + 1]; 113 | } 114 | } 115 | 116 | if (data[len - 1] != 0) { 117 | items[j] = p; 118 | } 119 | 120 | res->data = items; 121 | res->nitems = n; 122 | 123 | res->xfree = rie_util_free_str_list; 124 | 125 | return RIE_OK; 126 | } 127 | 128 | 129 | char * 130 | rie_mkpath(char *p1,...) 131 | { 132 | char *item, *res, *p; 133 | size_t len; 134 | 135 | va_list ap; 136 | 137 | va_start(ap, p1); 138 | 139 | len = sizeof("/") + strlen(p1); 140 | 141 | while ((item = va_arg(ap, char*))) { 142 | len += strlen(item) + 1; /* for slashes */ 143 | } 144 | va_end(ap); 145 | 146 | if (len > FILENAME_MAX) { 147 | rie_log_error0(0, "resulting string is too big"); 148 | return NULL; 149 | } 150 | 151 | res = malloc(len); 152 | if (res == NULL) { 153 | rie_log_error0(errno, "malloc"); 154 | return NULL; 155 | } 156 | 157 | p = res; 158 | 159 | len = strlen(p1); 160 | p = (char *) memcpy(p, p1, len) + len; 161 | 162 | va_start(ap, p1); 163 | 164 | while ((item = va_arg(ap, char*))) { 165 | *p++ = '/'; 166 | len = strlen(item); 167 | p = (char *) memcpy(p, item, len) + len; 168 | } 169 | va_end(ap); 170 | 171 | *p++ = 0; 172 | 173 | return res; 174 | } 175 | 176 | 177 | #if defined(RIE_DEBUG) 178 | 179 | int 180 | rie_backtrace_save(rie_array_t *bt) 181 | { 182 | void *buffer[RIE_BT_BUF_SIZE]; 183 | 184 | if (bt->nitems) { 185 | free(bt->data); 186 | } 187 | 188 | bt->nitems = backtrace(buffer, RIE_BT_BUF_SIZE); 189 | bt->data = backtrace_symbols(buffer, bt->nitems); 190 | if (bt->data == NULL) { 191 | rie_log_error0(errno, "backtrace_symbols"); 192 | bt->nitems = 0; 193 | return RIE_ERROR; 194 | } 195 | 196 | return RIE_OK; 197 | } 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /src/rie_util.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2022 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_UTIL_H__ 23 | #define __RIE_UTIL_H__ 24 | 25 | 26 | #define rie_swap(x, y, typ) \ 27 | do { typ tmp = x; x = y; y = tmp; } while (0) 28 | 29 | #define rie_memzero(ptr, size) \ 30 | memset(ptr, 0, size) 31 | 32 | /* get pointer to i-th element of array if present, or to first */ 33 | #define rie_array_get(arr, i, type) \ 34 | (i) < (arr)->nitems ? &(((type *)(arr)->data)[i]) \ 35 | : ((type *)((arr)->data)) 36 | 37 | typedef struct rie_array_s rie_array_t; 38 | 39 | typedef void (*rie_array_free_pt)(void *data, size_t nitems); 40 | 41 | struct rie_array_s { 42 | void *data; 43 | size_t nitems; 44 | rie_array_free_pt xfree; 45 | }; 46 | 47 | int rie_array_init(rie_array_t *array, size_t nitems, size_t item_len, 48 | rie_array_free_pt free_func); 49 | 50 | void rie_array_wipe(rie_array_t *array); 51 | 52 | int rie_str_list_to_array(char *in, size_t len, rie_array_t *res); 53 | 54 | char *rie_mkpath(char *p1,...); 55 | 56 | #if defined(RIE_DEBUG) 57 | int rie_backtrace_save(rie_array_t *bt); 58 | #endif 59 | 60 | static inline int 61 | rie_min(int a, int b) 62 | { 63 | if (a > b) { 64 | return b; 65 | } 66 | 67 | return a; 68 | } 69 | 70 | static inline int 71 | rie_max(int a, int b) 72 | { 73 | if (a < b) { 74 | return b; 75 | } 76 | 77 | return a; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/rie_window.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2025 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #include "rieman.h" 23 | #include "rie_xcb.h" 24 | 25 | #include 26 | 27 | static void rie_window_cleanup_winlist(void *windows, size_t nitems); 28 | static int rie_window_get_icon(rie_xcb_t *xcb, rie_gfx_t *gc, 29 | rie_window_t *window); 30 | static void rie_window_free_icons(void *data, size_t nitems); 31 | static int rie_window_center_resize(rie_t *pager, rie_window_t *win, 32 | rie_rect_t bb); 33 | static int rie_windows_tile_awesome_fair(rie_t *pager, int desk, 34 | rie_tile_fair_orientation_e orientation); 35 | 36 | static char *rie_window_missing_name = "-"; 37 | 38 | 39 | int 40 | rie_window_init_list(rie_array_t *windows, size_t len) 41 | { 42 | return rie_array_init(windows, len, sizeof(rie_window_t), 43 | rie_window_cleanup_winlist); 44 | } 45 | 46 | 47 | static void 48 | rie_window_cleanup_winlist(void *windows, size_t nitems) 49 | { 50 | int i; 51 | rie_window_t *win; 52 | 53 | win = (rie_window_t *) windows; 54 | 55 | for (i = 0; i < nitems; i++) { 56 | 57 | if (win[i].name && win[i].name != rie_window_missing_name) { 58 | free(win[i].name); 59 | } 60 | 61 | if (win[i].title && win[i].title != rie_window_missing_name) { 62 | free(win[i].title); 63 | } 64 | 65 | if (win[i].icons) { 66 | rie_array_wipe(win[i].icons); 67 | free(win[i].icons); 68 | win[i].icons = NULL; 69 | } 70 | } 71 | } 72 | 73 | 74 | rie_window_t * 75 | rie_window_lookup(rie_t *pager, uint32_t winid) 76 | { 77 | int i; 78 | rie_window_t *win; 79 | 80 | win = (rie_window_t *) pager->windows.data; 81 | 82 | for (i = 0; i < pager->windows.nitems; i++) { 83 | 84 | if (win[i].winid == winid) { 85 | return &win[i]; 86 | } 87 | } 88 | 89 | return NULL; 90 | } 91 | 92 | 93 | int 94 | rie_window_update_geometry(rie_t *pager) 95 | { 96 | int i, rc; 97 | rie_rect_t *vp; 98 | rie_window_t *win; 99 | xcb_window_t *root; 100 | 101 | win = pager->windows.data; 102 | 103 | for (i = 0; i < pager->windows.nitems; i++) { 104 | 105 | vp = rie_array_get(&pager->viewports, win[i].desktop, rie_rect_t); 106 | root = rie_array_get(&pager->virtual_roots, win[i].desktop, 107 | xcb_window_t); 108 | 109 | rc = rie_xcb_get_window_geometry(pager->xcb, &win[i].winid, root, 110 | &win[i].box, vp); 111 | if (rc != RIE_OK) { 112 | return rc; 113 | } 114 | } 115 | 116 | return RIE_OK; 117 | } 118 | 119 | 120 | int 121 | rie_window_query(rie_t *pager, rie_window_t *window, uint32_t winid) 122 | { 123 | int rc; 124 | char *textres; 125 | 126 | rie_xcb_t *xcb; 127 | rie_rect_t *vp; 128 | xcb_window_t *root; 129 | 130 | xcb = pager->xcb; 131 | 132 | rc = rie_xcb_property_get(xcb, winid, RIE_NET_WM_DESKTOP, 133 | XCB_ATOM_CARDINAL, &window->desktop); 134 | if (rc != RIE_OK) { 135 | return rc; 136 | } 137 | 138 | vp = rie_array_get(&pager->viewports, window->desktop, rie_rect_t); 139 | root = rie_array_get(&pager->virtual_roots, window->desktop, xcb_window_t); 140 | 141 | rc = rie_xcb_get_window_geometry(xcb, &winid, root, &window->box, vp); 142 | if (rc != RIE_OK) { 143 | return rc; 144 | } 145 | 146 | rc = rie_xcb_get_window_frame(pager->xcb, winid, &window->frame); 147 | if (rc != RIE_OK) { 148 | return rc; 149 | } 150 | 151 | window->winid = winid; 152 | 153 | rc = rie_xcb_property_get_utftext(xcb, winid, RIE_NET_WM_NAME, &textres); 154 | if (rc == RIE_ERROR) { 155 | return RIE_ERROR; 156 | 157 | } else if (rc != RIE_OK) { 158 | textres = rie_window_missing_name; /* property is unset */ 159 | } 160 | 161 | if (window->title && window->title != rie_window_missing_name) { 162 | free(window->title); 163 | } 164 | 165 | window->title = textres; 166 | 167 | rc = rie_xcb_property_get_utftext(xcb, winid, RIE_WM_CLASS, &textres); 168 | if (rc == RIE_ERROR) { 169 | return RIE_ERROR; 170 | 171 | } else if (rc != RIE_OK) { 172 | textres = rie_window_missing_name; /* property is unset */ 173 | } 174 | 175 | if (window->name && window->name != rie_window_missing_name) { 176 | free(window->name); 177 | } 178 | 179 | window->name = textres; 180 | 181 | rc = rie_window_get_icon(xcb, pager->gfx, window); 182 | if (rc != RIE_OK) { 183 | return rc; 184 | } 185 | 186 | rc = rie_xcb_get_window_type(pager->xcb, window, winid); 187 | if (rc == RIE_ERROR) { 188 | return RIE_ERROR; 189 | 190 | } else if (rc == RIE_NOTFOUND) { 191 | window->types = 0; 192 | } 193 | 194 | /* we want to receive events about this window changes */ 195 | (void) rie_xcb_update_event_mask(xcb, winid, SubstructureNotifyMask 196 | | StructureNotifyMask 197 | | PropertyChangeMask); 198 | 199 | /* result is ignored, as window may not exist */ 200 | 201 | return RIE_OK; 202 | } 203 | 204 | 205 | void 206 | rie_window_update_pager_focus(rie_t *pager) 207 | { 208 | int i, mx, my; 209 | 210 | rie_window_t *win, *last; 211 | 212 | pager->fwindow = NULL; 213 | 214 | mx = pager->m_x; 215 | my = pager->m_y; 216 | 217 | win = (rie_window_t *) pager->windows.data; 218 | last = NULL; 219 | 220 | for (i = 0; i < pager->windows.nitems; i++) { 221 | 222 | if (rie_gfx_xy_inside_rect(mx, my, &win[i].sbox) 223 | && !(win[i].state & RIE_WIN_STATE_HIDDEN)) 224 | { 225 | 226 | win[i].m_in = 1; 227 | pager->fwindow = &win[i]; 228 | 229 | /* stacking order: only top-level window must be selected */ 230 | if (last) { 231 | last->m_in = 0; 232 | } 233 | last = &win[i]; 234 | 235 | } else { 236 | win[i].m_in = 0; 237 | } 238 | } 239 | } 240 | 241 | 242 | static int 243 | rie_window_get_icon(rie_xcb_t *xcb, rie_gfx_t *gc, rie_window_t *window) 244 | { 245 | int rc, i; 246 | uint32_t *data, *last; 247 | 248 | rie_array_t res, *icons; 249 | rie_image_t *img; 250 | 251 | rie_memzero(&res, sizeof(rie_array_t)); 252 | 253 | rc = rie_xcb_property_get_array(xcb, window->winid, RIE_NET_WM_ICON, 254 | XCB_ATOM_CARDINAL, &res); 255 | if (rc == RIE_ERROR) { 256 | return RIE_ERROR; 257 | 258 | } else if (rc == RIE_NOTFOUND) { 259 | /* ignore missing icons */ 260 | return RIE_OK; 261 | } 262 | 263 | data = res.data; 264 | last = ((uint32_t *) res.data) + res.nitems; 265 | 266 | /* calculate number of icons in array */ 267 | for (i = 0; data < (last - 2); i++) { 268 | data += 2 + data[0] * data[1]; /* width, height, pixels[] */ 269 | } 270 | 271 | icons = malloc(sizeof(rie_array_t)); 272 | if (icons == NULL) { 273 | rie_log_error0(errno, "malloc"); 274 | rie_array_wipe(&res); 275 | return RIE_ERROR; 276 | } 277 | 278 | /* new per-window icons array */ 279 | if (rie_array_init(icons, i, sizeof(rie_image_t), rie_window_free_icons) 280 | != RIE_OK) 281 | { 282 | return RIE_ERROR; 283 | } 284 | 285 | data = res.data; 286 | img = icons->data; 287 | 288 | for (i = 0; i < icons->nitems; i++) { 289 | img[i].box.w = data[0]; 290 | img[i].box.h = data[1]; 291 | 292 | img[i].tx = rie_gfx_surface_from_icon(gc, &data[2], data[0], data[1]); 293 | if (img[i].tx == NULL) { 294 | goto failed; 295 | } 296 | 297 | data += 2 + data[0] * data[1]; 298 | } 299 | 300 | rie_array_wipe(&res); 301 | 302 | /* replace old array with a new one, deallocating old */ 303 | if (window->icons) { 304 | rie_array_wipe(window->icons); 305 | free(window->icons); 306 | } 307 | 308 | window->icons = icons; 309 | 310 | return RIE_OK; 311 | 312 | failed: 313 | 314 | rie_array_wipe(&res); 315 | rie_array_wipe(icons); 316 | free(icons); 317 | 318 | return RIE_OK; 319 | } 320 | 321 | 322 | static void 323 | rie_window_free_icons(void *data, size_t nitems) 324 | { 325 | rie_image_t *img = data; 326 | 327 | int i; 328 | 329 | for (i = 0; i < nitems; i++) { 330 | if (img[i].tx) { 331 | rie_gfx_surface_free(img[i].tx); 332 | img[i].tx = NULL; 333 | } 334 | } 335 | } 336 | 337 | 338 | static int 339 | rie_window_center_resize(rie_t *pager, rie_window_t *win, rie_rect_t bb) 340 | { 341 | rie_rect_t frame; 342 | 343 | frame = win->frame; 344 | 345 | bb.x += frame.x; 346 | bb.y += frame.y; 347 | bb.w -= (frame.x + frame.w); 348 | bb.h -= (frame.y + frame.h); 349 | 350 | return rie_xcb_moveresize_window(pager->xcb, win->winid, 351 | bb.x, bb.y, bb.w, bb.h); 352 | } 353 | 354 | 355 | int 356 | rie_windows_tile(rie_t *pager, int desk) 357 | { 358 | int rc; 359 | 360 | switch (pager->current_tile_mode) { 361 | case RIE_TILE_MODE_FAIR_EAST: 362 | rc = rie_windows_tile_awesome_fair(pager, desk, RIE_TILE_FAIR_EAST); 363 | break; 364 | 365 | case RIE_TILE_MODE_FAIR_WEST: 366 | rc = rie_windows_tile_awesome_fair(pager, desk, RIE_TILE_FAIR_WEST); 367 | break; 368 | 369 | default: 370 | pager->current_tile_mode = 0; 371 | return rie_windows_tile(pager, desk); 372 | } 373 | 374 | pager->current_tile_mode++; 375 | 376 | return rc; 377 | } 378 | 379 | 380 | static int 381 | rie_windows_tile_awesome_fair(rie_t *pager, int desk, 382 | rie_tile_fair_orientation_e orientation) 383 | { 384 | int i, k, n, nclients; 385 | 386 | uint32_t row, rows, lrows, col, cols, lcols; 387 | rie_rect_t *workarea, wa, g; 388 | rie_window_t *win, *c; 389 | rie_desktop_t *deskp; 390 | 391 | workarea = rie_array_get(&pager->workareas, desk, rie_rect_t); 392 | deskp = rie_array_get(&pager->desktops, desk, rie_desktop_t); 393 | 394 | nclients = deskp->nnormal; 395 | 396 | wa = *workarea; 397 | 398 | if (orientation == RIE_TILE_FAIR_EAST) { 399 | rie_swap(wa.w, wa.h, uint32_t); 400 | rie_swap(wa.x, wa.y, int); 401 | } 402 | 403 | rows = cols = 0; 404 | 405 | if (nclients == 2) { 406 | rows = 1; 407 | cols = 2; 408 | 409 | } else { 410 | rows = ceil(sqrt(nclients)); 411 | cols = ceil((double)nclients / rows); 412 | } 413 | 414 | win = pager->windows.data; 415 | for (i = 0, n = 0; i < pager->windows.nitems; i++) { 416 | if (win[i].dead 417 | || win[i].state & RIE_WIN_STATE_SKIP_PAGER 418 | || win[i].types & RIE_WINDOW_TYPE_DESKTOP 419 | || win[i].types & RIE_WINDOW_TYPE_DOCK 420 | || win[i].desktop != desk 421 | || win[i].state & RIE_WIN_STATE_HIDDEN) 422 | { 423 | continue; 424 | } 425 | 426 | c = &win[i]; 427 | 428 | n++; 429 | k = n - 1; 430 | 431 | row = col = 0; 432 | g.x = g.y = g.w = g.h = 0; 433 | 434 | row = k % rows; 435 | col = floor((double) k / rows); 436 | 437 | lrows = lcols = 0; 438 | 439 | if (k >= rows * cols - rows) { 440 | lrows = nclients - (rows * cols - rows); 441 | lcols = cols; 442 | 443 | } else { 444 | lrows = rows; 445 | lcols = cols; 446 | } 447 | 448 | if (row == lrows - 1) { 449 | g.h = wa.h - ceil((double) wa.h / lrows) * row; 450 | g.y = wa.h - g.h; 451 | 452 | } else { 453 | g.h = ceil((double) wa.h / lrows); 454 | g.y = g.h * row; 455 | } 456 | 457 | if (col == lcols - 1) { 458 | g.w = wa.w - ceil((double) wa.w / lcols) * col; 459 | g.x = wa.w - g.w; 460 | 461 | } else { 462 | g.w = ceil((double) wa.w / lcols); 463 | g.x = g.w * col; 464 | } 465 | 466 | g.h -= c->frame.w * 2; 467 | g.w -= c->frame.h * 2; 468 | 469 | g.y += wa.y; 470 | g.x += wa.x; 471 | 472 | if (orientation == RIE_TILE_FAIR_EAST) { 473 | rie_swap(g.w, g.h, uint32_t); 474 | rie_swap(g.x, g.y, int); 475 | } 476 | 477 | rie_window_center_resize(pager, &win[i], g); 478 | } 479 | 480 | return RIE_OK; 481 | } 482 | -------------------------------------------------------------------------------- /src/rie_window.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2025 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_WINDOW_H__ 23 | #define __RIE_WINDOW_H__ 24 | 25 | #include "rieman.h" 26 | 27 | typedef enum { 28 | RIE_TILE_FAIR_EAST, 29 | RIE_TILE_FAIR_WEST, 30 | } rie_tile_fair_orientation_e; 31 | 32 | 33 | struct rie_window_s { 34 | rie_rect_t box; /* real window corrdinates/size */ 35 | rie_rect_t sbox; /* scaled window inside pager */ 36 | rie_rect_t hbox; /* box of a hidden window on pad */ 37 | rie_rect_t frame; /* window manager decorations (real) */ 38 | 39 | char *name; 40 | char *title; 41 | uint32_t state; 42 | uint32_t desktop; 43 | uint32_t winid; 44 | uint32_t types; 45 | uint32_t hidden_idx; /* index in the list of hidden windows */ 46 | uint8_t focused; 47 | uint8_t m_in; /* mouse is over window *in pager* */ 48 | uint8_t dead; 49 | rie_array_t *icons; 50 | }; 51 | 52 | int rie_window_init_list(rie_array_t *windows, size_t len); 53 | 54 | rie_window_t *rie_window_lookup(rie_t *pager, uint32_t winid); 55 | 56 | int rie_window_update_geometry(rie_t *pager); 57 | int rie_window_query(rie_t *pager, rie_window_t *window, uint32_t winid); 58 | 59 | void rie_window_update_pager_focus(rie_t *pager); 60 | int rie_windows_tile(rie_t *pager, int desk); 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/rie_xcb.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2020 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIE_XCB_H_ 23 | #define __RIE_XCB_H_ 24 | 25 | #include "rieman.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | /* order must match atom names enum */ 37 | #define RIE_WIN_STATE_STICKY 0x1 38 | #define RIE_WIN_STATE_SKIP_TASKBAR 0x2 39 | #define RIE_WIN_STATE_SKIP_PAGER 0x4 40 | #define RIE_WIN_STATE_ABOVE 0x8 41 | #define RIE_WIN_STATE_BELOW 0x10 42 | #define RIE_WIN_STATE_HIDDEN 0x20 43 | #define RIE_WIN_STATE_SHADED 0x40 44 | #define RIE_WIN_STATE_MODAL 0x80 45 | #define RIE_WIN_STATE_MAXVERT 0x100 46 | #define RIE_WIN_STATE_MAXHOR 0x200 47 | #define RIE_WIN_STATE_FULLSCREEN 0x400 48 | #define RIE_WIN_STATE_ATTENTION 0x800 49 | 50 | 51 | #define RIE_WINDOW_TYPE_DESKTOP 0x1 52 | #define RIE_WINDOW_TYPE_DOCK 0x2 53 | #define RIE_WINDOW_TYPE_TOOLBAR 0x4 54 | #define RIE_WINDOW_TYPE_MENU 0x8 55 | #define RIE_WINDOW_TYPE_UTILITY 0x10 56 | #define RIE_WINDOW_TYPE_SPLASH 0x20 57 | #define RIE_WINDOW_TYPE_DIALOG 0x40 58 | #define RIE_WINDOW_TYPE_NORMAL 0x80 59 | 60 | #define rie_xcb_handle_error(err, fmt, ...) \ 61 | rie_xcb_handle_error_real(__FILE__, __LINE__, err, fmt, ##__VA_ARGS__) 62 | 63 | #define rie_xcb_handle_error0(err, msg) rie_xcb_handle_error(err, "%s", msg) 64 | 65 | #define rie_xcb_event_type(ev) ((ev)->response_type & ~0x80) 66 | uint8_t rie_xcb_randr_event(rie_xcb_t *xcb, uint8_t off); 67 | 68 | /* atom names are indexed by this enum */ 69 | typedef enum { 70 | RIE_UTF8_STRING = 0, 71 | RIE_COMPOUND_TEXT, 72 | RIE_STRING, 73 | 74 | RIE_WM_NAME, 75 | RIE_WM_CLASS, 76 | RIE_WM_DELETE_WINDOW, 77 | 78 | RIE_NET_DESKTOP_LAYOUT, 79 | RIE_NET_DESKTOP_NAMES, 80 | RIE_NET_NUMBER_OF_DESKTOPS, 81 | RIE_NET_CURRENT_DESKTOP, 82 | RIE_NET_CLIENT_LIST, 83 | RIE_NET_CLIENT_LIST_STACKING, 84 | RIE_NET_DESKTOP_GEOMETRY, 85 | RIE_NET_WORKAREA, 86 | RIE_NET_DESKTOP_VIEWPORT, 87 | RIE_NET_VIRTUAL_ROOTS, 88 | RIE_NET_ACTIVE_WINDOW, 89 | RIE_NET_WM_NAME, 90 | RIE_NET_WM_DESKTOP, 91 | 92 | RIE_NET_WM_WINDOW_TYPE, /* 1st WINDOW_TYPE - contigious list */ 93 | 94 | RIE_NET_WM_WINDOW_TYPE_DESKTOP, 95 | RIE_NET_WM_WINDOW_TYPE_DOCK, 96 | RIE_NET_WM_WINDOW_TYPE_TOOLBAR, 97 | RIE_NET_WM_WINDOW_TYPE_MENU, 98 | RIE_NET_WM_WINDOW_TYPE_UTILITY, 99 | RIE_NET_WM_WINDOW_TYPE_SPLASH, 100 | RIE_NET_WM_WINDOW_TYPE_DIALOG, 101 | RIE_NET_WM_WINDOW_TYPE_NORMAL, /* last WINDOW_TYPE */ 102 | 103 | RIE_NET_WM_STATE, 104 | 105 | RIE_NET_WM_STATE_STICKY, /* 1st WM_STATE - contigious list */ 106 | RIE_NET_WM_STATE_SKIP_TASKBAR, 107 | RIE_NET_WM_STATE_SKIP_PAGER, 108 | RIE_NET_WM_STATE_ABOVE, 109 | RIE_NET_WM_STATE_BELOW, 110 | RIE_NET_WM_STATE_HIDDEN, 111 | RIE_NET_WM_STATE_SHADED, 112 | RIE_NET_WM_STATE_MODAL, 113 | RIE_NET_WM_STATE_MAXIMIZED_VERT, 114 | RIE_NET_WM_STATE_MAXIMIZED_HORZ, 115 | RIE_NET_WM_STATE_FULLSCREEN, 116 | RIE_NET_WM_STATE_DEMANDS_ATTENTION, /* last WM_STATE */ 117 | 118 | RIE_NET_WM_ICON, 119 | RIE_XROOTPMAP_ID, 120 | RIE_MOTIF_WM_HINTS, 121 | 122 | RIE_NET_WM_STRUT, 123 | RIE_NET_WM_STRUT_PARTIAL, 124 | 125 | /* when adding, don't forget to update rie_atom_names[] */ 126 | RIE_ATOM_LAST 127 | } rie_atom_name_t; 128 | 129 | 130 | xcb_connection_t *rie_xcb_get_connection(rie_xcb_t *xcb); 131 | xcb_window_t rie_xcb_get_root(rie_xcb_t *xcb); 132 | xcb_window_t rie_xcb_get_window(rie_xcb_t *xcb); 133 | xcb_visualtype_t *rie_xcb_root_visual(rie_xcb_t *xcb); 134 | xcb_atom_t rie_xcb_atom(rie_xcb_t *xcb, rie_atom_name_t atom_name); 135 | xcb_ewmh_connection_t *rie_xcb_ewmh(rie_xcb_t *xcb); 136 | int rie_xcb_screen(rie_xcb_t *xcb); 137 | rie_rect_t rie_xcb_root_geom(rie_xcb_t *xcb); 138 | int rie_xcb_update_root_geom(rie_xcb_t *xcb); 139 | 140 | xcb_screen_t *rie_xcb_get_screen(xcb_connection_t *c, int screen); 141 | xcb_visualtype_t *rie_xcb_find_visual(xcb_connection_t *c, 142 | xcb_visualid_t visual); 143 | 144 | int rie_xcb_create_window(rie_xcb_t *xcb, int w, int h); 145 | 146 | int rie_xcb_get_window_geometry(rie_xcb_t *xcb, xcb_window_t *win, 147 | xcb_window_t *vroot, rie_rect_t *box, rie_rect_t *viewport); 148 | 149 | rie_xcb_t *rie_xcb_new(rie_settings_t *cfg); 150 | void rie_xcb_delete(rie_xcb_t *xcb); 151 | 152 | int rie_xcb_set_window_type(rie_xcb_t *xcb, xcb_window_t win, 153 | rie_atom_name_t type); 154 | int rie_xcb_set_window_hints(rie_xcb_t *xcb, rie_settings_t *cfg, 155 | uint32_t current_desktop); 156 | 157 | int rie_xcb_property_notify_atom(rie_xcb_t *xcb, 158 | xcb_property_notify_event_t *ev); 159 | 160 | xcb_generic_event_t *rie_xcb_next_event(rie_xcb_t *xcb); 161 | 162 | int rie_xcb_get_fd(rie_xcb_t *xcb); 163 | 164 | int rie_xcb_event_is_error(rie_xcb_t *xcb); 165 | 166 | int rie_xcb_property_get(rie_xcb_t *xcb, xcb_window_t win, 167 | unsigned int property, xcb_atom_t type, void *value); 168 | 169 | int rie_xcb_property_get_array(rie_xcb_t *xcb, xcb_window_t win, 170 | unsigned int property, xcb_atom_t type, rie_array_t *array); 171 | 172 | int rie_xcb_property_get_utftext(rie_xcb_t *xcb, xcb_window_t win, 173 | unsigned int property, char **value); 174 | 175 | int rie_xcb_property_get_array_utftext(rie_xcb_t *xcb, xcb_window_t win, 176 | unsigned int property, rie_array_t *array); 177 | 178 | int rie_xcb_property_set_array(rie_xcb_t *xcb, xcb_window_t win, 179 | xcb_atom_t property, xcb_atom_t xtype, rie_array_t *array); 180 | 181 | #if defined(RIE_TESTS) 182 | int rie_xcb_property_delete(rie_xcb_t *xcb, xcb_window_t win, 183 | unsigned int property); 184 | #endif 185 | 186 | int rie_xcb_client_message(rie_xcb_t *xcb, xcb_window_t target, 187 | xcb_window_t win, unsigned int atom, uint32_t *value, int cnt); 188 | 189 | int rie_xcb_update_event_mask(rie_xcb_t *xcb, xcb_window_t win, 190 | unsigned long mask); 191 | 192 | int rie_xcb_configure_window(rie_xcb_t *xcb, int x, int y, int w, int h); 193 | 194 | int rie_xcb_configure_window_ext(rie_xcb_t *xcb, xcb_window_t target, 195 | int x, int y, int w, int h); 196 | 197 | int rie_xcb_moveresize_window(rie_xcb_t *xcb, xcb_window_t target, 198 | int x, int y, int w, int h); 199 | 200 | int rie_xcb_get_window_frame(rie_xcb_t *xcb, xcb_window_t win, 201 | rie_rect_t *frame); 202 | 203 | void rie_xcb_flush(rie_xcb_t *xcb); 204 | 205 | int rie_xcb_get_root_pixmap(rie_xcb_t *xcb, rie_gfx_t *gc, rie_image_t *img); 206 | 207 | int rie_xcb_get_screen_pixmap(rie_xcb_t *xcb, rie_gfx_t *gc, xcb_drawable_t obj, 208 | rie_rect_t *box, rie_image_t *img); 209 | 210 | int rie_xcb_get_window_state(rie_xcb_t *xcb, rie_window_t *window, 211 | xcb_window_t xwin); 212 | 213 | int rie_xcb_get_window_type(rie_xcb_t *xcb, rie_window_t *window, 214 | xcb_window_t xwin); 215 | 216 | int rie_xcb_set_strut(rie_xcb_t *xcb, xcb_window_t win, rie_struts_t *struts); 217 | 218 | int rie_xcb_set_desktop(rie_xcb_t *xcb, uint32_t new_desk); 219 | 220 | int rie_xcb_set_desktop_viewport(rie_xcb_t *xcb, int x, int y); 221 | 222 | int rie_xcb_restore_hidden(rie_xcb_t *xcb, xcb_window_t win); 223 | 224 | int rie_xcb_set_focus(rie_xcb_t *xcb, xcb_window_t win); 225 | 226 | int rie_xcb_set_desktop_layout(rie_xcb_t *xcb, rie_settings_t *cfg); 227 | 228 | int rie_xcb_get_output(rie_xcb_t *xcb, char *name, rie_rect_t *geom); 229 | 230 | int rie_xcb_handle_error_real(char *file, int line, void *err, char *fmt, ...); 231 | 232 | #endif 233 | -------------------------------------------------------------------------------- /src/rieman.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2017-2023 Vladimir Homutov 4 | */ 5 | 6 | /* 7 | * This file is part of Rieman. 8 | * 9 | * Rieman is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * Rieman is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | */ 21 | 22 | #ifndef __RIEMAN_H__ 23 | #define __RIEMAN_H__ 24 | 25 | #include "config.h" 26 | 27 | /* SUS v3, POSIX 1003.1 2004 (POSIX 2001 + Corrigenda) */ 28 | #define _XOPEN_SOURCE 600 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | #define RIEMAN_VERSION "1.2.4" 36 | #define RIEMAN_TITLE "Rieman" 37 | 38 | typedef struct rie_conf_item_s rie_conf_item_t; 39 | typedef struct rie_settings_s rie_settings_t; 40 | typedef struct rie_control_s rie_control_t; 41 | typedef struct rie_window_s rie_window_t; 42 | typedef struct rie_image_s rie_image_t; 43 | typedef struct rie_skin_s rie_skin_t; 44 | typedef struct rie_xcb_s rie_xcb_t; 45 | typedef struct rie_gfx_s rie_gfx_t; 46 | typedef struct rie_s rie_t; 47 | 48 | #include "rie_util.h" 49 | #include "rie_log.h" 50 | #include "rie_gfx.h" 51 | #include "rie_window.h" 52 | #include "rie_control.h" 53 | 54 | enum rie_rc_e { 55 | RIE_OK = 0, 56 | RIE_ERROR = -1, 57 | RIE_NOTFOUND = -2 58 | }; 59 | 60 | enum rie_positions_e { 61 | RIE_POS_TOPRIGHT, 62 | RIE_POS_TOPLEFT, 63 | RIE_POS_BOTTOMLEFT, 64 | RIE_POS_BOTTOMRIGHT 65 | }; 66 | 67 | enum rie_pad_positions_e { 68 | RIE_PAD_POS_ABOVE, 69 | RIE_PAD_POS_BELOW 70 | }; 71 | 72 | enum rie_desktop_labels_e { 73 | RIE_DESKTOP_NUMBER, 74 | RIE_DESKTOP_NAME 75 | }; 76 | 77 | typedef enum { 78 | RIE_TILE_MODE_FAIR_EAST, 79 | RIE_TILE_MODE_FAIR_WEST, 80 | RIE_TILE_MODE_LAST 81 | } rie_tile_e; 82 | 83 | typedef enum { 84 | RIE_CMD_RELOAD, 85 | RIE_CMD_EXIT, 86 | RIE_CMD_SWITCH_DESKTOP_LEFT, 87 | RIE_CMD_SWITCH_DESKTOP_RIGHT, 88 | RIE_CMD_SWITCH_DESKTOP_DOWN, 89 | RIE_CMD_SWITCH_DESKTOP_UP, 90 | RIE_CMD_SWITCH_DESKTOP_PREV, 91 | RIE_CMD_SWITCH_DESKTOP_NEXT, 92 | RIE_CMD_TILE_CURRENT_DESKTOP 93 | } rie_command_t; 94 | 95 | typedef struct { 96 | uint32_t version_min; /* minimum supported version */ 97 | uint32_t version_max; /* maximum supported version */ 98 | rie_conf_item_t *spec; /* keys description */ 99 | char *conf_file; 100 | } rie_conf_meta_t; 101 | 102 | typedef struct { 103 | uint32_t enabled; 104 | uint32_t left; 105 | uint32_t left_start_y; 106 | uint32_t left_end_y; 107 | uint32_t right; 108 | uint32_t right_start_y; 109 | uint32_t right_end_y; 110 | uint32_t top; 111 | uint32_t top_start_x; 112 | uint32_t top_end_x; 113 | uint32_t bottom; 114 | uint32_t bottom_start_x; 115 | uint32_t bottom_end_x; 116 | } rie_struts_t; 117 | 118 | struct rie_settings_s { 119 | 120 | rie_conf_meta_t meta; /* configuration metadata */ 121 | char *skin; /* skin name */ 122 | char *control_socket_path; /* socket to listen for commands */ 123 | 124 | uint32_t withdrawn; 125 | uint32_t docked; 126 | uint32_t show_text; 127 | uint32_t show_window_icons; 128 | uint32_t show_viewports; 129 | uint32_t show_minitray; 130 | uint32_t show_pad; 131 | uint32_t pad_position; 132 | uint32_t pad_margin; 133 | uint32_t position; 134 | uint32_t pos_x_offset; 135 | uint32_t pos_y_offset; 136 | 137 | struct { 138 | uint32_t wrap; 139 | 140 | uint32_t w; /* pixels */ 141 | uint32_t h; /* pixels */ 142 | 143 | uint32_t orientation; /* _NET_DESKTOP_LAYOUT */ 144 | uint32_t corner; 145 | uint32_t content; 146 | } desktop; 147 | 148 | struct { 149 | uint32_t enabled; 150 | uint32_t start_desktop; /* first desktop on this out */ 151 | uint32_t ndesktops; /* number of desktops on this out */ 152 | 153 | char *output; /* RandR name of output */ 154 | 155 | /* 156 | * if subset mode is enabled, only desktops that attached 157 | * to specific monitor (RandR output) are shown/managed 158 | */ 159 | } subset; 160 | 161 | uint32_t enable_change_desktop_button; 162 | uint32_t change_desktop_button; 163 | uint32_t enable_tile_button; 164 | uint32_t tile_button; 165 | 166 | uint32_t skip_taskbar; /* initial window state flags */ 167 | uint32_t skip_pager; 168 | uint32_t sticky; 169 | uint32_t layer; 170 | 171 | rie_struts_t struts; 172 | }; 173 | 174 | typedef struct { 175 | uint32_t num; /* desktop number (real) */ 176 | rie_rect_t cell; /* bounding box of main cell */ 177 | rie_rect_t dbox; /* desktop representation */ 178 | rie_rect_t pad; /* pad for desktop name/icons */ 179 | uint32_t nhidden; /* number of hidden windows */ 180 | uint32_t nnormal; /* number of normal windows */ 181 | uint32_t lrow; 182 | uint32_t lcol; 183 | } rie_desktop_t; 184 | 185 | struct rie_s { 186 | 187 | rie_settings_t *cfg; /* configuration */ 188 | 189 | rie_log_t *log; 190 | rie_xcb_t *xcb; /* X11 connection context */ 191 | rie_gfx_t *gfx; /* graphics context */ 192 | rie_skin_t *skin; /* loaded skin object */ 193 | rie_control_t *ctl; /* remote control object */ 194 | 195 | rie_rect_t desktop_geom; /* full desktop size */ 196 | rie_image_t root_bg; /* root window background */ 197 | rie_desktop_t template; 198 | 199 | rie_rect_t monitor_geom; /* RandR output geometry */ 200 | 201 | rie_array_t windows; /* of rie_window_t */ 202 | rie_array_t desktop_names; /* of char * */ 203 | rie_array_t desktops; /* of rie_desktop_t */ 204 | rie_array_t workareas; /* of rie_rect_t */ 205 | rie_array_t viewports; /* of rie_rect_t */ 206 | rie_array_t virtual_roots; /* of xcb_window_t */ 207 | 208 | rie_array_t vdesktops; /* of rie_desktop_t* */ 209 | /* slice view of desktops */ 210 | 211 | uint32_t current_desktop; /* active desktop number */ 212 | uint32_t selected_desktop; /* currently selected by mouse */ 213 | rie_window_t *fwindow; /* currently focused window */ 214 | 215 | uint32_t nrows; /* current pager geometry */ 216 | uint32_t ncols; 217 | 218 | uint32_t vp_rows; /* viewports inside desktop */ 219 | uint32_t vp_cols; 220 | rie_rect_t vp; 221 | rie_rect_t selected_vp; /* currently selected by mouse */ 222 | 223 | int32_t m_x; /* current mouse position/state */ 224 | int32_t m_y; 225 | uint8_t m_in; 226 | uint8_t resize; /* 1 if event assumes resizing */ 227 | uint8_t render; /* 1 if event assumes rendering */ 228 | uint8_t exposed; /* 1 if window was exposed */ 229 | 230 | rie_tile_e current_tile_mode; 231 | }; 232 | 233 | rie_t *rie_pager_new(char *cfile, rie_log_t *log); 234 | void rie_pager_delete(rie_t *pager, int final); 235 | int rie_pager_init(rie_t *pager, rie_t *oldpager); 236 | void rie_pager_run_cmd(rie_t *pager, rie_command_t cmd); 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /tests/conf/bad-2.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key-2.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap ololo 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key-3.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key-4.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner unknown 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key-5.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer unknown-mask 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key-validate.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text not-a-boolean 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad-key.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text not-a-boolean 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/bad.conf: -------------------------------------------------------------------------------- 1 | # bad conf 2 | \ 3 | -------------------------------------------------------------------------------- /tests/conf/c1.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button middle 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/c2.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner bottomright 8 | layout.orientation vertical 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text false 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad false 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon false 19 | 20 | appearance.viewports false 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar false 27 | 28 | window.skip_pager false 29 | 30 | window.sticky false 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button right 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/c3.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 3 7 | layout.corner bottomright 8 | layout.orientation horizontal 9 | 10 | appearance.skin default 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon false 19 | 20 | appearance.viewports false 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar false 27 | 28 | window.skip_pager false 29 | 30 | window.sticky false 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button right 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/default.conf: -------------------------------------------------------------------------------- 1 | # ------------------------ 2 | # rieman 1.2 configuration 3 | # ------------------------ 4 | 5 | # size of a single desktop in pixels; if one dimension (width or height) 6 | # is zero then the other dimension is proportional to real resolution 7 | geometry.width 50 8 | geometry.height 0 9 | 10 | layout.wrap 2 11 | layout.corner topleft 12 | layout.orientation horizontal 13 | 14 | appearance.skin default 15 | 16 | # displays desktop number in the desktop background 17 | appearance.desktop_text true 18 | appearance.desktop_text.content number 19 | 20 | # displays name of a desktop below it 21 | appearance.desktop_pad true 22 | appearance.desktop_pad.position above # | below 23 | appearance.desktop_pad.margin 2 24 | 25 | # displays window icons 26 | appearance.window_icon true 27 | 28 | # display viewports grid inside desktop 29 | appearance.viewports true 30 | 31 | # display list of desktop's minimized windows under desktop 32 | appearance.minitray true 33 | 34 | # put into dock/slit 35 | window.withdrawn false 36 | 37 | # sets type of window to dock 38 | window.dock true 39 | 40 | # do not show pager in taskbar 41 | window.skip_taskbar true 42 | 43 | # do not show in pagers 44 | window.skip_pager true 45 | 46 | # appear on all desktops 47 | window.sticky true 48 | 49 | # startup position on the screen and corner offset 50 | window.position topright 51 | window.position.dx 0 52 | window.position.dy 0 53 | 54 | # window layer - below/above/normal 55 | window.layer above 56 | 57 | # NET_WM_STRUT/NET_WM_STRUT_PARTIAL to reserve space for the window 58 | # on desktop borders 59 | window.strut true 60 | window.strut.left 0 61 | window.strut.left_start_y 0 62 | window.strut.left_end_y 0 63 | window.strut.right 0 64 | window.strut.right_start_y 0 65 | window.strut.right_end_y 0 66 | window.strut.top 0 67 | window.strut.top_start_x 0 68 | window.strut.top_end_x 0 69 | window.strut.bottom 0 70 | window.strut.bottom_start_x 0 71 | window.strut.bottom_end_x 0 72 | 73 | # switches the desktop when clicked by it 74 | actions.change_desktop.mouse_button left 75 | actions.tile_windows.mouse_button right 76 | 77 | # rieman will listen for commands from this DGRAM unix socket 78 | #control.socket /path/to/.rieman.sock 79 | -------------------------------------------------------------------------------- /tests/conf/noroot.conf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/conf/noroot.conf -------------------------------------------------------------------------------- /tests/conf/skin-1.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 0 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-1 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray true 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/skin-2.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 1 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-2 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray true 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/skin-3.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 2 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-3 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content name 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports false 21 | 22 | appearance.minitray false 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/skin-bad-key-2.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 0 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-bad-key-2 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray true 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/skin-bad-key-3.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 0 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-bad-key-3 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content name 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position below 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray true 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/conf/skin-bad-key.conf: -------------------------------------------------------------------------------- 1 | # rieman config 2 | 3 | geometry.width 50 4 | geometry.height 35 5 | 6 | layout.wrap 0 7 | layout.corner topright 8 | layout.orientation vertical 9 | 10 | appearance.skin ../tests/skins/skin-bad-key 11 | 12 | appearance.desktop_text true 13 | appearance.desktop_text.content number 14 | 15 | appearance.desktop_pad true 16 | appearance.desktop_pad.position above 17 | 18 | appearance.window_icon true 19 | 20 | appearance.viewports true 21 | 22 | appearance.minitray true 23 | 24 | window.withdrawn false 25 | 26 | window.skip_taskbar true 27 | 28 | window.skip_pager true 29 | 30 | window.sticky true 31 | 32 | window.position topleft 33 | 34 | window.layer normal 35 | 36 | actions.change_desktop true 37 | actions.change_desktop.mouse_button left 38 | 39 | -------------------------------------------------------------------------------- /tests/rieman_tests.py: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Copyright (C) 2017-2025 Vladimir Homutov 4 | # 5 | 6 | # This file is part of Rieman. 7 | # 8 | # Rieman is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # Rieman is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | 20 | import sys, subprocess, re, tempfile, signal, time, os, traceback 21 | 22 | rie_tests = [ 23 | 24 | # usage display with correct and wrong option 25 | { 'name': 'commandline', 26 | 'show-help': { 27 | 'cmd': 'build/rieman -h', 28 | 'stdout': r'Usage: build/rieman \[-h\] \[-v\[v\]\] \[-c \\]', 29 | 'stderr': r'^$', 30 | 'rc': 0, 31 | 'timeout': 1 }, 32 | 33 | 'show-version': { 34 | 'cmd': 'build/rieman -v', 35 | 'stdout': r'rieman \d\.\d', 36 | 'stderr': r'^$', 37 | 'rc': 0, 38 | 'timeout': 1 }, 39 | 40 | 'show-verbose-version': { 41 | 'cmd': 'build/rieman -vv', 42 | 'stdout': r'Rieman \d\.\d\.\d Copyright \(c\) \d\d\d\d-\d\d\d\d .*prefix:.*debug:.*', 43 | 'stderr': r'^$', 44 | 'rc': 0, 45 | 'timeout': 1 }, 46 | 47 | 'wrong-arg': { 48 | 'cmd': 'build/rieman -foo', 49 | 'stdout': r'Usage: build/rieman \[-h\] \[-v\[v\]\] \[-c \\]', 50 | 'stderr': r'Unknown argument "-foo"', 51 | 'rc': 1, 52 | 'timeout': 1 }, 53 | 54 | 'no-log-arg': { 55 | 'cmd': 'build/rieman -l', 56 | 'stdout': r'Usage: build/rieman \[-h\] \[-v\[v\]\] \[-c \\]', 57 | 'stderr': r'option "-l" requires argument', 58 | 'rc': 1, 59 | 'timeout': 1 }, 60 | 61 | 'wrong-arg-and-log': { 62 | 'cmd': 'build/rieman -l foo -foo', 63 | 'stdout': r'Usage: build/rieman \[-h\] \[-v\[v\]\] \[-c \\]', 64 | 'stderr': r'Unknown argument "-foo"', 65 | 'rc': 1, 66 | 'timeout': 1 }, 67 | 68 | 'bad-log': { 69 | 'cmd': 'build/rieman -l /non-existing', 70 | 'stdout': r'.*', 71 | 'stderr': r'failed to create log', 72 | 'rc': 1, 73 | 'timeout': 1 }, 74 | 75 | 'exec': [ 'show-help', 'show-version', 'show-verbose-version', 76 | 'wrong-arg', 'no-log-arg', 'wrong-arg-and-log', 'bad-log' ] 77 | }, 78 | { 'name': 'invalid configuration', 79 | 'noexist': { 80 | 'cmd': 'build/rieman -c foo', 81 | 'stdout': r'^\[.*\] -log- using configuration file \'foo\'.*\[.*\] -err- open\(\"foo\"\) failed.*', 82 | 'stderr': r'', 83 | 'rc': 1, 84 | 'timeout': 1 }, 85 | 86 | 'missing-name': { 87 | 'cmd': 'build/rieman -c', 88 | 'stdout': r'Usage: build/rieman \[-h\] \[-v\[v\]\] \[-c \\]', 89 | 'stderr': r'option "-c" requires argument', 90 | 'rc': 1, 91 | 'timeout': 1 }, 92 | 93 | 'bad-conf': { 94 | 'cmd': 'build/rieman -c tests/conf/bad.conf', 95 | 'stdout': r'syntax error.*configuration load failed', 96 | 'stderr': r'.*', 97 | 'rc': 1, 98 | 'timeout': 1 }, 99 | 100 | 'bad-key': { 101 | 'cmd': 'build/rieman -c tests/conf/bad-key.conf', 102 | 'stdout': r'conversion to bool failed: unknown value \'not-a-boolean\'.*configuration load failed', 103 | 'stderr': r'.*', 104 | 'rc': 1, 105 | 'timeout': 1 }, 106 | 107 | 'bad-key-validate': { 108 | 'cmd': 'build/rieman -c tests/conf/bad-key-validate.conf', 109 | 'stdout': r'conversion to bool failed.*configuration load failed', 110 | 'stderr': r'.*', 111 | 'rc': 1, 112 | 'timeout': 1 }, 113 | 114 | 'bad-key-2': { 115 | 'cmd': 'build/rieman -c tests/conf/bad-key-2.conf', 116 | 'stdout': r'conversion to uint32 failed: no digits.*configuration load failed', 117 | 'stderr': r'.*', 118 | 'rc': 1, 119 | 'timeout': 1 }, 120 | 121 | 'bad-key-3': { 122 | 'cmd': 'build/rieman -c tests/conf/bad-key-3.conf', 123 | 'stdout': r'conversion to uint32 failed: strtol.*configuration load failed', 124 | 'stderr': r'.*', 125 | 'rc': 1, 126 | 'timeout': 1 }, 127 | 128 | 'bad-key-4': { 129 | 'cmd': 'build/rieman -c tests/conf/bad-key-4.conf', 130 | 'stdout': r'invalid variant \'unknown\' of key \'layout\.corner\'.*configuration load failed', 131 | 'stderr': r'.*', 132 | 'rc': 1, 133 | 'timeout': 1 }, 134 | 135 | 'bad-key-5': { 136 | 'cmd': 'build/rieman -c tests/conf/bad-key-5.conf', 137 | 'stdout': r'invalid variant \'unknown-mask\' of key \'window\.layer\'.*configuration load failed', 138 | 'stderr': r'.*', 139 | 'rc': 1, 140 | 'timeout': 1 }, 141 | 142 | 'skin-bad-key': { 143 | 'cmd': 'build/rieman -c tests/conf/skin-bad-key.conf', 144 | 'stdout': r'conversion to hex failed.*Failed to load skin configuration', 145 | 'stderr': r'rieman failed to start due to fatal error', 146 | 'rc': 1, 147 | 'timeout': 1 }, 148 | 149 | 'skin-bad-key-2': { 150 | 'cmd': 'build/rieman -c tests/conf/skin-bad-key-2.conf', 151 | 'stdout': r'conversion of \'NODBL\' to double failed: no digits.*Failed to load skin configuration', 152 | 'stderr': r'rieman failed to start due to fatal error', 153 | 'rc': 1, 154 | 'timeout': 1 }, 155 | 156 | 'skin-bad-key-3': { 157 | 'cmd': 'build/rieman -c tests/conf/skin-bad-key-3.conf', 158 | 'stdout': r'conversion of .* to double failed: strtod.*Failed to load skin configuration', 159 | 'stderr': r'rieman failed to start due to fatal error', 160 | 'rc': 1, 161 | 'timeout': 1 }, 162 | 163 | 'skin-bad-key-log': { 164 | 'cmd': 'build/rieman -c tests/conf/skin-bad-key.conf -l build/test-skin-bad-key.log', 165 | 'stdout': r'.*', 166 | 'stderr': r'rieman failed to start due to fatal error.*error log below:', 167 | 'rc': 1, 168 | 'timeout': 1 }, 169 | 170 | 'exec': [ 'noexist', 'missing-name', 'bad-conf', 171 | 'bad-key', 'bad-key-2', 'bad-key-3', 'bad-key-4', 'bad-key-5', 172 | 'bad-key-validate', 'skin-bad-key', 'skin-bad-key-2', 173 | 'skin-bad-key-3', 'skin-bad-key-log' 174 | ] 175 | }, 176 | 177 | # normal execution with valid config 178 | { 'name': 'running', 179 | 180 | 'default-exec': { 181 | 'cmd': 'build/rieman', 182 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'./conf/rieman.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 183 | 'stderr': r'.*', 184 | 'rc': 0, 185 | 'timeout': 1 }, 186 | 187 | 'xdg': { 188 | 'cmd': 'build/rieman', 189 | 'env': { 'XDG_CONFIG_HOME': 'foo', 'XDG_DATA_HOME': 'foo' }, 190 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'./conf/rieman.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 191 | 'stderr': r'.*', 192 | 'rc': 0, 193 | 'timeout': 1 }, 194 | 195 | 'c1': { 196 | 'cmd': 'build/rieman -c tests/conf/c1.conf', 197 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'tests/conf/c1.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 198 | 'stderr': r'.*', 199 | 'rc': 0, 200 | 'timeout': 1 }, 201 | 202 | 'c2': { 203 | 'cmd': 'build/rieman -c tests/conf/c2.conf', 204 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'tests/conf/c2.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 205 | 'stderr': r'.*', 206 | 'rc': 0, 207 | 'timeout': 1 }, 208 | 209 | 'c3': { 210 | 'cmd': 'build/rieman -c tests/conf/c3.conf', 211 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'tests/conf/c3.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 212 | 'stderr': r'.*', 213 | 'rc': 0, 214 | 'timeout': 1 }, 215 | 216 | 'exec': [ 'default-exec', 'xdg', 'c1', 'c2', 'c3' ] 217 | }, 218 | 219 | # skin-related 220 | { 'name': 'skins', 221 | 222 | 'skin-1': { 223 | 'cmd': 'build/rieman -c tests/conf/skin-1.conf', 224 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'tests/conf/skin-1.conf\'\n\[.*\] -log- desktop geometry is \d+ x \d+.*', 225 | 'stderr': r'.*', 226 | 'rc': 0, 227 | 'timeout': 1 }, 228 | 229 | 'skin-3': { 230 | 'cmd': 'build/rieman -c tests/conf/skin-3.conf', 231 | 'stdout': r'^\[.*\] -log- rieman ver\.\d\.\d\.\d .*started\.\.\.\n\[.*\] -log- using configuration file \'tests/conf/skin-3.conf\'.*\[.*\] -err- cairo_image_surface_create_from_png\(\'.*missing.png\'\).*', 232 | 'stderr': r'rieman failed to start', 233 | 'rc': 0, 234 | 'timeout': 1 }, 235 | 236 | 'exec': [ 'skin-1', 'skin-3' ] 237 | }, 238 | # signals 239 | { 'name': 'signals', 240 | 241 | 'reload': { 242 | 'cmd': 'build/rieman -c tests/conf/skin-1.conf', 243 | 'stdout': r'\*\*\* starting event loop.*\*\*\* reload signal received', 244 | 'stderr': r'.*', 245 | 'rc': 0, 246 | 'reload' : True, 247 | 'timeout': 1 }, 248 | 249 | 'term-and-withdrawn': { 250 | 'cmd': 'build/rieman -w -c tests/conf/skin-1.conf', 251 | 'stdout': r'\*\*\* starting event loop.*\*\*\* terminate signal received', 252 | 'stderr': r'.*', 253 | 'rc': 0, 254 | 'terminate' : True, 255 | 'timeout': 1 }, 256 | 257 | 'term-and-log': { 258 | 'cmd': 'build/rieman -c tests/conf/skin-1.conf-l build/test-term-and-log.log', 259 | 'stdout': r'.*', 260 | 'stderr': r'.*', 261 | 'rc': 0, 262 | 'terminate' : True, 263 | 'timeout': 1 }, 264 | 265 | 'exec': [ 'reload', 'term-and-withdrawn', 'term-and-log' ] 266 | }, 267 | ]; 268 | 269 | class Alarm(Exception): 270 | pass 271 | 272 | def alarm_handler(signum, frame): 273 | raise Alarm 274 | 275 | def tc_match(pattern, actual, name): 276 | 277 | rx = re.compile(pattern, re.DOTALL) 278 | 279 | if rx.search(actual.decode('utf-8')) == None: 280 | print("\n !!! actual %s does not match expected pattern" % name) 281 | print("\n actual %s:\n >>>\n%s" % (name, actual.decode('utf-8'))); 282 | print(" <<<\n Expected regex: '%s'" % (pattern)); 283 | return None 284 | 285 | return True 286 | 287 | def tc_exec(tc): 288 | 289 | procs = {} 290 | tfiles = {} 291 | result = {} 292 | 293 | # omg, python requires this to be done, can't just assign x[a][b] 294 | for proc in tc['exec']: 295 | tfiles[proc] = {} 296 | result[proc] = {} 297 | 298 | # open temporary files for client/server stdout/stderr 299 | for proc in tc['exec']: 300 | for out in ['stdout', 'stderr']: 301 | tfiles[proc][out] = tempfile.TemporaryFile() 302 | 303 | # execute server and then client and redirect output to temp files 304 | max_tmout = 0 305 | for proc in tc['exec']: 306 | # Let the 1st (typically server) some time to initialize (open socket) 307 | time.sleep(0.2) 308 | tc_env = os.environ.copy() 309 | 310 | if tc[proc].get('env'): 311 | tc_env.update(tc[proc]['env']) 312 | 313 | procs[proc] = subprocess.Popen(tc[proc]['cmd'].split(' '), 314 | stdout=tfiles[proc]['stdout'], 315 | stderr=tfiles[proc]['stderr'], 316 | universal_newlines=True, 317 | env = tc_env) 318 | 319 | if tc[proc]['timeout'] > max_tmout: 320 | max_tmout = tc[proc]['timeout'] 321 | 322 | try: 323 | 324 | signal.signal(signal.SIGALRM, alarm_handler) 325 | signal.alarm(max_tmout) 326 | 327 | for proc in tc['exec']: 328 | if tc[proc].get('reload') == True: 329 | time.sleep(0.2) 330 | os.kill(procs[proc].pid, signal.SIGUSR1) 331 | 332 | if tc[proc].get('terminate') == True: 333 | time.sleep(0.2) 334 | os.kill(procs[proc].pid, signal.SIGTERM) 335 | 336 | procs[proc].wait() 337 | result[proc]['rc'] = procs[proc].returncode 338 | 339 | signal.alarm(0) 340 | 341 | except Alarm: 342 | for proc in tc['exec']: 343 | if os.path.exists("/proc/%d" % procs[proc].pid): 344 | # TODO: kill does not allow coverage to be written 345 | # only kill if terminate failed; 346 | #procs[proc].kill() 347 | procs[proc].terminate() 348 | procs[proc].wait() 349 | result[proc]['rc'] = procs[proc].returncode 350 | 351 | for proc in tc['exec']: 352 | for out in ['stdout', 'stderr']: 353 | tfiles[proc][out].flush() 354 | tfiles[proc][out].seek(0, 0) 355 | result[proc][out] = tfiles[proc][out].read() 356 | 357 | return result 358 | 359 | def tc_announce(tc): 360 | 361 | tcname = tc['name'] 362 | 363 | width = INDENT - len(tcname) 364 | spaces = " " * width 365 | 366 | sys.stdout.write(">> %s%s" % (tcname, spaces)) 367 | 368 | if verbose == None: 369 | return 370 | 371 | sys.stdout.write("=== '%s' ===\n" % tc['name']) 372 | for proc in tc['exec']: 373 | sys.stdout.write(" %s: %s\n" % (proc, tc[proc]['cmd'])) 374 | 375 | 376 | def tc_result(tc, status, info = ""): 377 | 378 | if status == True: 379 | sys.stdout.write("pass\n") 380 | elif status == False: 381 | sys.stdout.write("fail\n") 382 | else: 383 | sys.stdout.write("exception while running test: %s\n" % info) 384 | 385 | def run_test(testcase): 386 | 387 | result = tc_exec(testcase) 388 | 389 | passed = True 390 | 391 | for proc in testcase['exec']: 392 | for out in ['stdout', 'stderr']: 393 | if verbose: 394 | print("|| %s: >>> %s <<<\n" % (proc + ' ' + out, result[proc][out])) 395 | 396 | if tc_match(testcase[proc][out], result[proc][out], proc + ' ' + out) == None: 397 | passed = False 398 | 399 | if testcase[proc].get('rc') and (testcase[proc]['rc'] != result[proc]['rc']): 400 | print("\n !!! actual rc %d does not match expected %d for %s" % 401 | (result[proc]['rc'], testcase[proc]['rc'], proc)) 402 | passed = False 403 | 404 | return passed 405 | 406 | verbose = os.getenv('RIE_TEST_VERBOSE') 407 | 408 | if len(sys.argv) > 1: 409 | test_filter = sys.argv[1] 410 | else: 411 | test_filter = "" 412 | 413 | failed = 0 414 | max_w = 0 415 | 416 | for tc in rie_tests: 417 | if len(tc['name']) > max_w: 418 | max_w = len(tc['name']) 419 | 420 | INDENT = max_w + 4 421 | 422 | for tc in rie_tests: 423 | 424 | tc_announce(tc) 425 | 426 | tm = re.compile(test_filter, re.IGNORECASE) 427 | if tm.search(tc['name']) == None: 428 | sys.stdout.write('skipped\n') 429 | continue 430 | 431 | try: 432 | if run_test(tc) != True: 433 | failed = failed + 1 434 | tc_result(tc, False) 435 | else: 436 | tc_result(tc, True) 437 | except: 438 | ex, value, trace = sys.exc_info() 439 | tc_result(tc, None, str(value)) 440 | traceback.print_tb(trace) 441 | 442 | if failed > 0: 443 | sys.exit(1) 444 | -------------------------------------------------------------------------------- /tests/skins/skin-1/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-1/img.png -------------------------------------------------------------------------------- /tests/skins/skin-1/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-1/missing_icon.png -------------------------------------------------------------------------------- /tests/skins/skin-1/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: skin-1 2 | 3 | fonts.desktop-name.face Times 4 | fonts.desktop-name.size 10 5 | fonts.desktop-name.color 0 6 | 7 | fonts.window-name.face Times 8 | fonts.window-name.size 10 9 | fonts.window-name.color 0 10 | 11 | fonts.desktop-number.face Times 12 | fonts.desktop-number.size 10 13 | fonts.desktop-number.color 0 14 | 15 | backgrounds.pager.type image 16 | backgrounds.pager.src img.png 17 | 18 | backgrounds.desktop.type image 19 | backgrounds.desktop.src img.png 20 | 21 | backgrounds.desktop-active.type image 22 | backgrounds.desktop-active.src img.png 23 | 24 | backgrounds.desktop-pad.type image 25 | backgrounds.desktop-pad.src img.png 26 | 27 | backgrounds.window.type image 28 | backgrounds.window.src img.png 29 | 30 | backgrounds.window-active.type image 31 | backgrounds.window-active.src img.png 32 | 33 | borders.viewport.width 1 34 | borders.viewport.type color 35 | borders.viewport.color 0 36 | 37 | -------------------------------------------------------------------------------- /tests/skins/skin-3/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-3/img.png -------------------------------------------------------------------------------- /tests/skins/skin-3/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-3/missing_icon.png -------------------------------------------------------------------------------- /tests/skins/skin-3/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: skin-1 2 | 3 | fonts.desktop-name.face Times 4 | fonts.desktop-name.size 10 5 | fonts.desktop-name.color 0 6 | 7 | fonts.window-name.face Times 8 | fonts.window-name.size 10 9 | fonts.window-name.color 0 10 | 11 | fonts.desktop-number.face Times 12 | fonts.desktop-number.size 10 13 | fonts.desktop-number.color 0 14 | 15 | backgrounds.pager.type image 16 | backgrounds.pager.src missing.png 17 | 18 | backgrounds.desktop.type image 19 | backgrounds.desktop.src missing.png 20 | 21 | backgrounds.desktop-active.type image 22 | backgrounds.desktop-active.src missing.png 23 | 24 | backgrounds.desktop-pad.type image 25 | backgrounds.desktop-pad.src missing.png 26 | 27 | backgrounds.window.type image 28 | backgrounds.window.src missing.png 29 | 30 | backgrounds.window-active.type image 31 | backgrounds.window-active.src missing.png 32 | 33 | borders.viewport.width 1 34 | borders.viewport.type image 35 | borders.viewport.src missing.png 36 | 37 | -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-2/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key-2/img.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-2/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key-2/missing_icon.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-2/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: skin-bad-key-2 2 | 3 | fonts.desktop-name.face Times 4 | fonts.desktop-name.size 10 5 | fonts.desktop-name.color 0 6 | 7 | fonts.window-name.face Times 8 | fonts.window-name.size 10 9 | fonts.window-name.color 0 10 | 11 | fonts.desktop-number.face Times 12 | fonts.desktop-number.size 10 13 | fonts.desktop-number.color 0 14 | 15 | backgrounds.pager.type image 16 | backgrounds.pager.src img.png 17 | backgrounds.pager.alpha NODBL 18 | 19 | backgrounds.desktop.type image 20 | backgrounds.desktop.src img.png 21 | 22 | backgrounds.desktop-active.type image 23 | backgrounds.desktop-active.src img.png 24 | 25 | backgrounds.desktop-pad.type image 26 | backgrounds.desktop-pad.src img.png 27 | 28 | backgrounds.window.type image 29 | backgrounds.window.src img.png 30 | 31 | backgrounds.window-active.type image 32 | backgrounds.window-active.src img.png 33 | 34 | borders.viewport.width 1 35 | borders.viewport.type image 36 | borders.viewport.color img.png 37 | 38 | -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-3/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key-3/img.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-3/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key-3/missing_icon.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key-3/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: skin-bad-key-2 2 | 3 | fonts.desktop-name.face Times 4 | fonts.desktop-name.size 10 5 | fonts.desktop-name.color 0 6 | 7 | fonts.window-name.face Times 8 | fonts.window-name.size 10 9 | fonts.window-name.color 0 10 | 11 | fonts.desktop-number.face Times 12 | fonts.desktop-number.size 10 13 | fonts.desktop-number.color 0 14 | 15 | backgrounds.pager.type image 16 | backgrounds.pager.src img.png 17 | backgrounds.pager.alpha 1E999999999999999999999999999999999999999999999999999999999999999999999999999999999 18 | 19 | backgrounds.desktop.type image 20 | backgrounds.desktop.src img.png 21 | 22 | backgrounds.desktop-active.type image 23 | backgrounds.desktop-active.src img.png 24 | 25 | backgrounds.desktop-pad.type image 26 | backgrounds.desktop-pad.src img.png 27 | 28 | backgrounds.window.type image 29 | backgrounds.window.src img.png 30 | 31 | backgrounds.window-active.type image 32 | backgrounds.window-active.src img.png 33 | 34 | borders.viewport.width 1 35 | borders.viewport.type image 36 | borders.viewport.color img.png 37 | 38 | -------------------------------------------------------------------------------- /tests/skins/skin-bad-key/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key/img.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key/missing_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vl409/rieman/d6e2fd2f43847e84fe0b0b0df01e0f1228657252/tests/skins/skin-bad-key/missing_icon.png -------------------------------------------------------------------------------- /tests/skins/skin-bad-key/rieman_skin.conf: -------------------------------------------------------------------------------- 1 | # rieman skin: skin-bad-key 2 | 3 | fonts.desktop-name.face Times 4 | fonts.desktop-name.size 10 5 | fonts.desktop-name.color 4332323278333 6 | 7 | fonts.window-name.face Times 8 | fonts.window-name.size 10 9 | fonts.window-name.color 4332323278333 10 | 11 | fonts.desktop-number.face Times 12 | fonts.desktop-number.size 10 13 | fonts.desktop-number.color 4332323278333 14 | 15 | backgrounds.pager.type image 16 | backgrounds.pager.src img.png 17 | 18 | backgrounds.desktop.type image 19 | backgrounds.desktop.src img.png 20 | 21 | backgrounds.desktop-active.type image 22 | backgrounds.desktop-active.src img.png 23 | 24 | backgrounds.desktop-pad.type image 25 | backgrounds.desktop-pad.src img.png 26 | 27 | backgrounds.window.type image 28 | backgrounds.window.src img.png 29 | 30 | backgrounds.window-active.type image 31 | backgrounds.window-active.src img.png 32 | 33 | borders.viewport.width 1 34 | borders.viewport.type image 35 | borders.viewport.color img.png 36 | 37 | --------------------------------------------------------------------------------