Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial commit of FFcast 1.0

  • Loading branch information...
commit 314e513396674f860028e68971daa710691f99ff 0 parents
@lolilolicon authored
Showing with 748 additions and 0 deletions.
  1. +1 −0  .gitignore
  2. +43 −0 Makefile
  3. +105 −0 README
  4. +2 −0  TODO
  5. +365 −0 ffcast.bash
  6. +232 −0 xrectsel.c
1  .gitignore
@@ -0,0 +1 @@
+/release/
43 Makefile
@@ -0,0 +1,43 @@
+OUT = xrectsel
+VERSION = $(shell git describe)
+
+SRC = ${wildcard *.c}
+OBJ = ${SRC:.c=.o}
+DISTFILES = Makefile README ffcast.bash xrectsel.c
+
+PREFIX ?= /usr
+MANPREFIX ?= ${PREFIX}/share/man
+
+CPPFLAGS := -DVERSION=\"${VERSION}\" ${CPPFLAGS}
+CFLAGS := --std=c99 -g -pedantic -Wall -Wextra -Werror ${CFLAGS}
+LDFLAGS := -lX11 ${LDFLAGS}
+
+all: ${OUT}
+
+${OUT}: ${OBJ}
+ ${CC} -o $@ ${OBJ} ${LDFLAGS}
+
+strip: ${OUT}
+ strip --strip-all ${OUT}
+
+install: xrectsel ffcast.bash
+ install -D -m755 xrectsel ${DESTDIR}${PREFIX}/bin/xrectsel
+ install -D -m755 ffcast.bash ${DESTDIR}${PREFIX}/bin/ffcast
+
+uninstall:
+ @echo removing executable file from ${DESTDIR}${PREFIX}/bin
+ rm -f ${DESTDIR}${PREFIX}/bin/xrectsel
+ rm -f ${DESTDIR}${PREFIX}/bin/ffcast
+
+dist: clean
+ mkdir ffcast-${VERSION}
+ cp ${DISTFILES} ffcast-${VERSION}
+ sed "s/\(^VERSION *\)= .*/\1= ${VERSION}/" < Makefile > ffcast-${VERSION}/Makefile
+ sed "s/@VERSION[@]/${VERSION}/g" < ffcast.bash > ffcast-${VERSION}/ffcast.bash
+ tar czf ffcast-${VERSION}.tar.gz ffcast-${VERSION}
+ rm -rf ffcast-${VERSION}
+
+clean:
+ ${RM} ${OUT} ${OBJ}
+
+.PHONY: clean dist install uninstall
105 README
@@ -0,0 +1,105 @@
+FFcast
+======
+
+FFcast helps the user interactively select a screen region and hands over the
+geometry to FFmpeg for screen recording.
+
+FFcast version 1.0 rewritten from scratch, and incompatible with version 0.x.
+
+Usage
+-----
+
+Examples are always most helpful to get you started.
+
+ ffcast -s
+
+You will be asked to select a region using mouse, then ffmpeg starts recording.
+Press 'q' to end recording. The output file by default is ffcast-*.mkv.
+
+ ffcast -svv ffmpeg -r 25 -- -f alsa -i hw:0 -vcodec libx264 cast.mkv
+
+Debug is turned on by -vv. You will see in the debug output the ffmpeg command
+line that's called. Notice that '--' from above is replaced with the x11grab
+input options, and other options are unchanged- That's indeed what ffcast does.
+
+ ffcast -ws
+
+You will be asked to first select a window by mouse click, and then a screen
+region by mouse. The recorded region is the region selections combined by
+union. Indeed, you can pass any number of -w and -s.
+
+ DISPLAY=:1 ffcast
+
+FFcast, as with xwininfo, xrectsel, respects the DISPLAY environment variable.
+The above records the whole screen of display :1. This is useful when you run
+a nested X server with Xephyr and want to record stuff inside its window.
+
+FFcast 1.0 changes from 0.x
+---------------------------
+
+Many of the options are removed, and the code is considerably simplified and
+more readable. But the most significant change is that now the user can pass
+any valid argument to FFmpeg- FFcast will never touch FFmpeg's options other
+than the x11grab input ones (which is the point of FFcast). The implication
+of this is that, for example, you can easily pass audio input options to get
+audio recording- a feature widely expected yet hard to implement/maintain.
+
+The reasoning is that FFcast should not know anything about FFmpeg except for
+what it must know. In other words, I will not maintain the FFmpeg's crap,
+it's the user's responsibility to keep up with whatever FFmpeg changes. In
+other words, FFcast doesn't hold the user's hands but gives him/her all the
+control. Whatever works for you ;). The part FFcast does touch, however, is
+the x11grab input options- currently something like this:
+
+ -f x11grab -s 600x400 -i :0.0+100+100
+
+That's it.
+
+Also changed is the xrectsel program. It now supports several format strings.
+They are %x, %y, %X, %Y, %w, %h, %b and %d. Read the source code for details.
+
+History
+-------
+
+Originally, Michal Witkowski (Neuro) posted[1] "x264 Lossless Screencast Script"
+at ArchLinux forums. I then went on and heavily modified and extended the
+script, and finally released FFcast 0.x. The idea behind Neuro's script was to
+parse xwininfo's output and pass it to ffmpeg, so you can easily record a window
+by simply clicking it. I liked it, and naturally linked the behavior with the
+screenshot application 'scrot'. I wanted to find a way to select an arbitrary
+screen region for capture. I went on to look at the scrot source code, as well
+as post a topic[2] asking for help. HashBox was very kind to post his code and
+even clean it up for me- I finally combined what I got from scrot and HashBox's
+code and put togethor xrectsel.c. All was looking good to me.
+
+But obviously I was midguided to think it's a good idea to take control of all
+the irrelevant FFmpeg options and added even more (like -t). And then people
+came to me and complained that FFcast doesn't do sound recording. I at first
+still thought I should implement it, but then found that we simply couldn't-
+with all the sound systems out there, there's no easy way to determine the sound
+input device in the first place. I could have added some options in the config
+file and whatnot, but I *knew* I was on the wrong track, so I did nothing.
+
+After a long time, a thread[3] at ArchLinux forums reminded me of FFcast and
+the painful fact that it sucked. I then sat down, opened the script, and didn't
+read much before I started to write prototype code for FFcast 1.0.
+
+[1] https://bbs.archlinux.org/viewtopic.php?id=85237
+[2] https://bbs.archlinux.org/viewtopic.php?id=85378
+[3] https://bbs.archlinux.org/viewtopic.php?id=127335
+
+FAQ
+---
+
+Q: Why not support console recording?
+A: Since you asked, you probably know that ffmpeg can record framebuffer,
+ `ffmpeg -f fbdev -i /dev/fb0` for example. The thing is ffmpeg's framebuffer
+ recording is very primitive- it does not support region selection of any
+ kind, just the whole screen. Even if it did, we wouldn't be able to do the
+ same kind of interative selection as in X. Due to these limitations and
+ the goal of ffcast, the user is better off using ffmpeg directly.
+ I also recommend 'ttyrec' for your tty recording needs.
+
+Q: Can I record audio using ffcast?
+A: Yes, you can do everything you like with ffmpeg since ffcast 1.0.
+
2  TODO
@@ -0,0 +1,2 @@
+* Choose a decent set of default ffmpeg options- ask experts ;)
+* Add verbose and debug messages- never fail silently.
365 ffcast.bash
@@ -0,0 +1,365 @@
+#!/bin/bash
+
+set -e
+shopt -s extglob
+
+readonly progname=ffcast progver='@VERSION@'
+readonly cast_cmd=ffmpeg
+declare -a opt_args
+declare -a cast_args cast_cmdline x11grab_opts
+declare -a cast_args_default=(-v 1 -r 25 -- -vcodec libx264 "$progname-$(date +%Y%m%d-%H%M%S).mkv")
+declare -i borderless=1 mod16=0 verbosity=0
+PS4='debug: command: ' # for set -x
+x='+x'; [[ $- == *x* ]] && x='-x' # save set -x
+readonly x
+
+#---
+# Functions
+
+_msg() {
+ local prefix=$1
+ shift || return
+ local fmt=$1
+ shift || return
+ printf "$prefix$fmt\n" "$@"
+}
+
+debug() {
+ (( verbosity >= 2 )) || return
+ _msg 'debug: ' "$@"
+} >&2
+
+verbose() {
+ (( verbosity >= 1 )) || return
+ _msg 'verbose: ' "$@"
+} >&2
+
+msg() {
+ (( verbosity >= 0 )) || return
+ _msg ':: ' "$@"
+} >&2
+
+warn() {
+ (( verbosity >= -1 )) || return
+ _msg 'warning: ' "$@"
+} >&2
+
+error() {
+ (( verbosity >= -1 )) || return
+ _msg 'error: ' "$@"
+} >&2
+
+parse_geospec_get_corners() {
+ local geospec=$1
+ local _x _y x_ y_ w h k
+ local n='?([-+])+([0-9])'
+ local m='?(-)+([0-9])'
+ local N='+([0-9])'
+ # strip whitespaces
+ IFS=$' \t' read -r geospec <<< "$geospec"
+ case $geospec in
+ $n,$n+([' \t'])$n,$n) # x1,y1 x2,y2
+ IFS=$', \t' read _x _y x_ y_ <<< "$geospec"
+ ;;
+ ${N}x${N}\+${m}\+${m}) # wxh+x+y
+ IFS='x+' read w h _x _y <<< "$geospec"
+ (( x_ = rootw - _x - w ))
+ (( y_ = rooth - _y - h ))
+ ;;
+ *)
+ error "invalid geometry specification: \`%s'" "$geospec"
+ return 1
+ ;;
+ esac
+ printf '%d,%d %d,%d' $_x $_y $x_ $y_
+}
+
+region_intersect_corners() {
+ local corners
+ local _x _y x_ y_
+ local _X _Y X_ Y_
+ # Initialize variable- otherwise bash will fallback to 0
+ IFS=' ,' read _X _Y X_ Y_ <<< "$1"
+ shift || return 1
+ for corners in "$@"; do
+ IFS=' ,' read _x _y x_ y_ <<< "$corners"
+ (( _X = _x > _X ? _x : _X ))
+ (( _Y = _y > _Y ? _y : _Y ))
+ (( X_ = x_ > X_ ? x_ : X_ ))
+ (( Y_ = y_ > Y_ ? y_ : Y_ ))
+ done
+ printf '%d,%d %d,%d' $_X $_Y $X_ $Y_
+}
+
+region_union_corners() {
+ local corners
+ local _x _y x_ y_
+ local _X _Y X_ Y_
+ # Initialize variable- otherwise bash will fallback to 0
+ IFS=' ,' read _X _Y X_ Y_ <<< "$1"
+ shift || return 1
+ for corners in "$@"; do
+ IFS=' ,' read _x _y x_ y_ <<< "$corners"
+ (( _X = _x < _X ? _x : _X ))
+ (( _Y = _y < _Y ? _y : _Y ))
+ (( X_ = x_ < X_ ? x_ : X_ ))
+ (( Y_ = y_ < Y_ ? y_ : Y_ ))
+ done
+ printf '%d,%d %d,%d' $_X $_Y $X_ $Y_
+}
+
+select_region_get_corners() {
+ msg "%s" "please select a region using mouse"
+ xrectsel_get_corners
+}
+
+select_window_get_corners() {
+ msg "%s" "please click once in target window"
+ LC_ALL=C xwininfo | xwininfo_get_corners
+}
+
+# stdin: xrectsel output
+# stdout: x1,y1 x2,y2
+xrectsel_get_corners() {
+ # Note: requires xrectsel 0.3
+ xrectsel "%x,%y %X,%Y"
+}
+
+# stdin: xwininfo output (locale: C)
+# stdout: ${width}x${height}
+xwininfo_get_dimensions() {
+ local line
+ local -i w h
+ while IFS=$' \t' read -r line; do
+ if [[ $line == 'Width: '+([0-9]) ]]; then
+ w=${line#'Width: '}
+ elif [[ $line == 'Height: '+([0-9]) ]]; then
+ h=${line#'Height: '}
+ else
+ continue
+ fi
+ if (( w && h )); then
+ printf '%dx%d' $w $h
+ return
+ fi
+ done
+ return 1
+}
+
+# stdin: xwininfo output (locale: C)
+# stdout: x1,y1 x2,y2
+xwininfo_get_corners() {
+ local line
+ local _x _y x_ y_ b
+ local n='-?[0-9]+'
+ local corners="^Corners: *\\+($n)\\+($n) *-$n\\+$n *-($n)-($n) *\\+$n-$n\$"
+ # Note: explicitly set IFS to ensure stripping of whitespaces
+ while IFS=$' \t' read -r line; do
+ if [[ $line == 'Border width: '+([0-9]) ]]; then
+ b=${line#'Border width: '}
+ elif [[ $line =~ $corners ]]; then
+ _x=${BASH_REMATCH[1]}
+ _y=${BASH_REMATCH[2]}
+ x_=${BASH_REMATCH[3]}
+ y_=${BASH_REMATCH[4]}
+ else
+ continue
+ fi
+ [[ -n $_x ]] || continue
+ if (( ! borderless )); then
+ :
+ elif [[ -n $b ]]; then
+ (( _x += b )) || :
+ (( _y += b )) || :
+ (( x_ += b )) || :
+ (( y_ += b )) || :
+ else
+ continue
+ fi
+ printf '%d,%d, %d,%d' $_x $_y $x_ $y_
+ return
+ done
+ return 1
+}
+
+#---
+# Split command line arguments into two parts:
+#
+# ffcast ... ffmpeg ... [--] ...
+
+while (( $# )); do
+ [[ $1 == "$cast_cmd" ]] && break
+ opt_args+=("$1")
+ shift
+done
+
+if shift; then
+ cast_cmdline=("$cast_cmd" "$@")
+else
+ cast_cmdline=("$cast_cmd" "${cast_args_default[@]}")
+fi
+
+#---
+# Process arguments passed to ffcast, get a region geometry.
+
+declare -i rootw=0 rooth=0 _x=0 _y=0 x_=0 y_=0 w=0 h=0
+IFS='x' read rootw rooth <<< "$(LC_ALL=C xwininfo -root | xwininfo_get_dimensions)"
+
+set -- "${opt_args[@]}"
+
+usage() {
+ cat <<EOF
+$progname $progver
+Usage: ${0##*/} [arguments] [ffmpeg command]
+
+ Arguments:
+ -s select a rectangular region by mouse
+ -w select a window by mouse click
+ -b include borders of selected window
+ -m trim selected region to be mod 16
+ -q less verbose
+ -v more verbose
+ -h print this help and exit
+ -H print detailed help and exit
+ <geospec> geometry specification
+
+ All the arguments can be repeated, and are processed in order.
+ For example, -vv is more verbose than -v.
+EOF
+if (( verbosity < 1 )); then
+cat <<EOF
+
+ Use \`${0##*/} -vh' for detailed help.
+EOF
+else
+cat <<EOF
+ Any number of selections are supported, e.g., -sw is valid.
+ All selected regions are combined by union.
+
+ FFmpeg command is run as given, with x11grab input options inserted,
+ either replacing the first instance of '--' or, if no '--' is given,
+ after 'ffmpeg'. You can give any valid ffmpeg command line, e.g.,
+
+ ${0##*/} -s ffmpeg -r 25 -- -f alsa -i hw:0 -vcodec libx264 cast.mkv
+
+ <geospec> is usually not used. It is intended as a way to use external
+ region selection commands. For example,
+
+ ${0##*/} "\$(xrectsel '%x,%y %X,%Y')"
+
+ <geospec> must conform to one of the following syntax:
+
+ - x1,y1 x2,y2 (x1,y1) = (left,top) offset of the region
+ (x2,y2) = (right,bottom) offset of the region
+ - wxh+x+y (wxh) = dimension of the region
+ (+x+y) = (+left+top) offset of the region
+EOF
+fi
+ exit $1
+}
+
+OPTIND=1
+i=0
+while getopts 'bhmqsvw' opt; do
+ case $opt in
+ h)
+ usage 0
+ ;;
+ m)
+ mod16=1
+ ;;
+ s)
+ corners_list[i++]=$(select_region_get_corners)
+ debug "corners: %s" "${corners_list[-1]}"
+ ;;
+ w)
+ corners_list[i++]=$(select_window_get_corners)
+ debug "corners: %s" "${corners_list[-1]}"
+ ;;
+ b)
+ borderless=0
+ ;;
+ q)
+ (( verbosity-- )) || :
+ ;;
+ v)
+ (( verbosity++ )) || :
+ ;;
+ esac
+done
+shift $(( OPTIND -1 ))
+
+# The rest are geometry specifications
+while (( $# )); do
+ corners_list[i++]=$(parse_geospec_get_corners "$1")
+ debug "corners: %s" "${corners_list[-1]}"
+ shift
+done
+
+if (( i )); then
+ corners=$(region_union_corners "${corners_list[@]}")
+ debug "corners union all selections: %s" "${corners}"
+ corners=$(region_intersect_corners "$corners" "0,0 0,0")
+ debug "corners rootwin intersection: %s" "${corners}"
+ IFS=' ,' read _x _y x_ y_ <<< "$corners"
+fi
+
+(( w = rootw - _x - x_ ))
+(( h = rooth - _y - y_ ))
+# x264 requires frame size divisible by 2, mod 16 is said to be optimal.
+# Adapt by reducing- expanding could exceed screen edges, and would not
+# be much beneficial anyway.
+if (( mod16 )); then
+ w_old=w
+ h_old=h
+ (( w = 16 * (w / 16) ))
+ (( h = 16 * (h / 16) ))
+else
+ w_old=w
+ h_old=h
+ (( w = 2 * (w / 2) ))
+ (( h = 2 * (h / 2) ))
+fi
+
+if (( verbosity >= 1 )); then
+ if (( w < w_old )); then
+ verbose 'trim frame width from %d to %d' $w_old $w
+ fi
+ if (( h < h_old )); then
+ verbose 'trim frame height from %d to %d' $h_old $h
+ fi
+fi
+
+x11grab_opts=(-f x11grab -s "${w}x${h}" -i "${DISPLAY}+${_x},${_y}")
+
+#---
+# Insert x11grab options into ffmpeg command line.
+
+set -- "${cast_cmdline[@]}"
+
+if ! shift; then
+ error 'no ffmpeg command line specified'
+ exit 1
+fi
+
+while (( $# )); do
+ if [[ $1 == '--' ]]; then
+ cast_args+=("${x11grab_opts[@]}")
+ break
+ else
+ cast_args+=("$1")
+ shift
+ fi
+done
+
+if shift; then # got '--'
+ cast_args+=("$@")
+else # no '--', then put x11grab options at first
+ cast_args=("${x11grab_opts[@]}" "${cast_args[@]}")
+fi
+
+(( verbosity >= 2 )) && set -x
+"${cast_cmd}" "${cast_args[@]}"
+set $x
+
+# vim:ts=4:sw=4:et:
232 xrectsel.c
@@ -0,0 +1,232 @@
+/* xrectsel.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+
+typedef struct Region Region;
+struct Region {
+ Window root;
+ int x; /* offset from left of screen */
+ int y; /* offset from top of screen */
+ int X; /* offset from right of screen */
+ int Y; /* offset from bottom of screen */
+ unsigned int w; /* width */
+ unsigned int h; /* height */
+ unsigned int b; /* border_width */
+ unsigned int d; /* depth */
+};
+
+static void die(const char *errstr, ...);
+static void error(const char *errstr, ...);
+static int print_region_attr(const char *fmt, Region region);
+static int select_region(Display *dpy, Window root, Region *region);
+
+int main(int argc, const char *argv[])
+{
+ Display *dpy;
+ Window root;
+ Region sr; /* selected region */
+ const char *fmt; /* format string */
+
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) {
+ die("failed to open display %s\n", getenv("DISPLAY"));
+ }
+
+ root = DefaultRootWindow(dpy);
+
+ fmt = argc > 1 ? argv[1] : "%wx%h+%x+%y\n";
+
+ /* interactively select a rectangular region */
+ if (select_region(dpy, root, &sr) != EXIT_SUCCESS) {
+ XCloseDisplay(dpy);
+ die("failed to select a rectangular region\n");
+ }
+
+ print_region_attr(fmt, sr);
+
+ XCloseDisplay(dpy);
+ return EXIT_SUCCESS;
+}
+
+static void die(const char *errstr, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "xrectsel: ");
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static void error(const char *errstr, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "xrectsel: ");
+ va_start(ap, errstr);
+ vfprintf(stderr, errstr, ap);
+ va_end(ap);
+}
+
+static int print_region_attr(const char *fmt, Region region)
+{
+ const char *s;
+
+ for (s = fmt; *s; ++s) {
+ if (*s == '%') {
+ switch (*++s) {
+ case '%':
+ putchar('%');
+ break;
+ case 'x':
+ printf("%i", region.x);
+ break;
+ case 'y':
+ printf("%i", region.y);
+ break;
+ case 'X':
+ printf("%i", region.X);
+ break;
+ case 'Y':
+ printf("%i", region.Y);
+ break;
+ case 'w':
+ printf("%u", region.w);
+ break;
+ case 'h':
+ printf("%u", region.h);
+ break;
+ case 'b':
+ printf("%u", region.b);
+ break;
+ case 'd':
+ printf("%u", region.d);
+ break;
+ }
+ } else {
+ putchar(*s);
+ }
+ }
+
+ return 0;
+}
+
+static int select_region(Display *dpy, Window root, Region *region)
+{
+ XEvent ev;
+
+ GC sel_gc;
+ XGCValues sel_gv;
+
+ int done = 0, btn_pressed = 0;
+ int x, y, width, height;
+ int start_x, start_y;
+
+ Cursor cursor;
+ cursor = XCreateFontCursor(dpy, XC_crosshair);
+
+ /* Grab pointer for these events */
+ XGrabPointer(dpy, root, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
+ GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
+ /* Grab keyboard */
+ XGrabKeyboard(dpy, root, False, GrabModeAsync, GrabModeAsync, CurrentTime);
+
+ sel_gv.function = GXinvert;
+ sel_gv.subwindow_mode = IncludeInferiors;
+ sel_gv.line_width = 1;
+ sel_gc = XCreateGC(dpy, root, GCFunction | GCSubwindowMode | GCLineWidth, &sel_gv);
+
+ for (;;) {
+ XNextEvent(dpy, &ev);
+ switch (ev.type) {
+ case ButtonPress:
+ btn_pressed = 1;
+ x = start_x = ev.xbutton.x_root;
+ y = start_y = ev.xbutton.y_root;
+ width = height = 0;
+ break;
+ case MotionNotify:
+ /* Draw only if button is pressed */
+ if (btn_pressed) {
+ /* Re-draw last Rectangle to clear it */
+ XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
+
+ width = ev.xbutton.x_root - start_x;
+ height = ev.xbutton.y_root - start_y;
+
+ /* Cursor moves backwards, (x,y) always is the northwest pole */
+ if (width < 0) {
+ width = 0 - width;
+ x = start_x - width;
+ } else {
+ x = start_x;
+ }
+ if (height < 0) {
+ height = 0 - height;
+ y = start_y - height;
+ } else {
+ y = start_y;
+ }
+ /* Draw Rectangle */
+ XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
+ XFlush(dpy);
+ }
+ break;
+ case ButtonRelease:
+ done = 1;
+ break;
+ case KeyPress:
+ error("Keyboard pressed, aborting\n");
+ done = 2;
+ break;
+ case KeyRelease:
+ break;
+ default:
+ break;
+ }
+ if (done)
+ break;
+ }
+
+ /* Re-draw last Rectangle to clear it */
+ XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
+ XFlush(dpy);
+
+ XUngrabPointer(dpy, CurrentTime);
+ XUngrabKeyboard(dpy, CurrentTime);
+ XFreeCursor(dpy, cursor);
+ XFreeGC(dpy, sel_gc);
+ XSync(dpy, 1);
+
+ if (done < 2) {
+ Region rr; /* root region */
+ Region sr; /* selected region */
+
+ if (False == XGetGeometry(dpy, root, &rr.root, &rr.x, &rr.y, &rr.w, &rr.h, &rr.b, &rr.d)) {
+ error("failed to get root window geometry\n");
+ return EXIT_FAILURE;
+ }
+ sr.x = x;
+ sr.y = y;
+ /* note: signed to unsigned int conversion
+ * since width is guaranteed to be non-negative, converting it to unsigned
+ * int works here */
+ sr.w = width;
+ sr.h = height;
+ /* calculate right and bottom offset */
+ sr.X = rr.w - sr.x - sr.w;
+ sr.Y = rr.h - sr.y - sr.h;
+ /* those doesn't really make sense but should be set */
+ sr.b = rr.b;
+ sr.d = rr.d;
+ *region = sr;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.