-
Notifications
You must be signed in to change notification settings - Fork 2
/
rerandr3
620 lines (550 loc) · 20.2 KB
/
rerandr3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
#!/bin/bash
# rerandr3 - Bash multimonitor handler daemon
# by Vladimir Kudrya
# https://github.com/Vladimir-csp/
#
# This script is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version. See <http://www.gnu.org/licenses/>.
# for help see show_help function or run rerandr3 help
# hook trigger boundary in pixel quantity (width*height)
# 1366 * 768 = 1049088
PIXEL_TRIGGER=1100000
# which strategy to use when autostacking
# (xrandr relative position options: --right-of, --left-of, --above, --below)
STACK_STRATEGY="--right-of"
SOCKET="/tmp/rerandr3.fifo"
DAEMON_LOG="$HOME/.rerandr3.log"
UDEV_RULE_FILE="/etc/udev/rules.d/95-rerandr3-monitor-trigger.rules"
UDEV_RULE="KERNEL==\"card0\", SUBSYSTEM==\"drm\", RUN+=\"$(readlink -f "$0") trigger-restore\""
# Name of X process, needed for some checks
XORG_PROCESS=Xorg
# check for needed tools
BINFAIL=0
for BINARY in xrandr xxd sed tr xdpyinfo
do
which $BINARY >/dev/null || { printf "%s\n" "$BINARY binary not found!" >&2 ; BINFAIL=1 ;}
done
[ "$BINFAIL" = "1" ] && exit 1 || true
unset BINFAIL
show_help(){
cat << "EOF"
syntax:
rerandr command
where command is one of:
save - save current uniqe monitor setup based on output names and
monitor models from EDID data.
restore - if current monitor setup is known, apply stored config.
If setup is unknown, try to disable everything disconnected and
enable and stack everything connected.
show - dump setup ID and xrandr arguments of current state.
daemon - launch user session daemon for automatic triggering.
trigger-save - tell daemon to do save.
trigger-restore - tell daemon to restore configuration.
hooks - just apply screen hooks
insert-udev - insert udev rule for auto triggering.
to use rerandr3 in 'full auto', create udev rule:
KERNEL=="card0", SUBSYSTEM=="drm", RUN+="/full/path/to/rerandr3 trigger-restore"
EOF
}
##############################################
#### window hooks
##############################################
hooks() {
## do things on every switch
printf "%s\n" "Applying general hooks."
## get DPI
#DPI="$(xdpyinfo | grep 'resolution:' | grep -o '[0-9]\+x[0-9]\+' | head -n 1 | cut -d 'x' -f 1)"
## sync dpi to xrdb
#printf "%s\n" "Xft.dpi: $DPI" | xrdb -merge
## sync dpi to fontconfig
#mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/fontconfig/conf.d"
#cat > "${XDG_CONFIG_HOME:-$HOME/.config}/fontconfig/conf.d/90-rerandr3-dpi.conf" << EOF
#<?xml version="1.0"?>
#<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
#<!-- Generated by rerandr3, do not edit. -->
#<fontconfig>
# <match target="pattern">
# <edit name="dpi" mode="assign"><double>$DPI</double></edit>
# </match>
#</fontconfig>
#EOF
#which nitrogen > /dev/null && nitrogen --restore
#{ pgrep -u "$USER" conky > /dev/null 2> /dev/null && sleep 1 && killall -USR1 conky ; } &
}
smallhooks() {
# do things if screen area is less than $PIXEL_TRIGGER
printf "%s\n" "Applying small screen hooks."
#wmctrl -xr Firefox -b "add,maximized_horz,maximized_vert"
#wmctrl -xr Thunderbird -b "add,maximized_horz,maximized_vert"
#wmctrl -xr Spacefm -b "add,maximized_horz,maximized_vert"
}
largehooks() {
# do things if screen area is greater than $PIXEL_TRIGGER
printf "%s\n" "Applying large screen hooks."
#wmctrl -xr Firefox -b "remove,maximized_vert,maximized_horz"
#wmctrl -xr Thunderbird -b "remove,maximized_vert,maximized_horz"
#wmctrl -xr Spacefm -b "remove,maximized_vert,maximized_horz"
#wmctrl -xr Firefox -e "0,-1,-1,1280,$HEIGHT"
#wmctrl -xr Thunderbird -e "0,-1,-1,1280,$HEIGHT"
#wmctrl -xr Spacefm -e "0,-1,-1,1280,$HEIGHT"
}
applyhooks() {
# skip if run by root (DM, etc)
[ "$(id -ru)" = "0" ] && return 0
# calculate dimensions and apply hooks
DIMENSIONS="$(xdpyinfo | grep dimensions | grep -o '[0-9]\+x[0-9]\+' | head -n 1)"
WIDTH="$(printf "%s\n" "$DIMENSIONS" | cut -d x -f 1)"
HEIGHT="$(printf "%s\n" "$DIMENSIONS" | cut -d x -f 2)"
PIXELS="$(( $WIDTH * $HEIGHT ))"
printf "%s\n" "Pixel quantity: $PIXELS"
if [ "$PIXELS" -gt "$PIXEL_TRIGGER" ] ; then
hooks
largehooks
else
hooks
smallhooks
fi
# hooks must not fail
true
}
prepare_confdir(){
if [ -n "$XDG_CONFIG_HOME" ]
then
CONF_DIR="$XDG_CONFIG_HOME/rerandr3"
else
CONF_DIR="$HOME/.config/rerandr3"
fi
mkdir -p "$CONF_DIR"
[ -d "$CONF_DIR" -a -w "$CONF_DIR" ] || { printf "%s\n" "Could not access config directory \"$CONF_DIR\"!" >&2 ; exit 1 ;}
}
prepare_pipe(){
[ ! -p "$SOCKET" ] && mkfifo -m 666 "$SOCKET"
[ "$(stat -c %a "$SOCKET")" != "666" ] && chmod 666 "$SOCKET"
[ ! -p "$SOCKET" -a "$(stat -c %a "$SOCKET")" != "666" ] && { printf "%s\n" "Could not prepare named pipe \"$SOCKET\"!" >&2 ; exit 1 ;}
}
get_data(){
# parsing verbose xrandr output
OUTPUT_NUM=-1
unset OUTPUT STATUS ROLE POSITION POSITION_X POSITION_Y CRTC MODEL SERIAL MODE_CURRENT MODE_PREFERRED EDID EDID_CATCH RANDR ROTATION REFLECTION GAMMA BRIGHTNESS TRANSFORM SCREEN_NUM DIMENSIONS
OIFS=$IFS
printf "%s\n" "Gathering data..."
IFS=
while read LINE ; do
# get output name and status
# triggered by and connection status keyword and lack of indent.
if printf "%s" "$LINE" | grep -q '^Screen[[:space:]]\+[0-9]\+:'
then
# found new screen
SCREEN_NUM="$(printf "%s" "$LINE" | grep -o '^Screen[[:space:]]\+[0-9]\+:' | grep -o '[0-9]\+')"
# we really support only one screen
[ "$SCREEN_NUM" -gt "0" ] && break
DIMENSIONS="$(printf "%s" "$LINE" | grep -o 'current[[:space:]]\+[0-9]\+[[:space:]]*x[[:space:]]*[0-9]\+' | grep -o '[0-9]\+[[:space:]]*x[[:space:]]*[0-9]\+' | tr -d '[[:space:]]')"
# done with this line
continue
fi
if printf "%s" "$LINE" | grep -q '^[[:alnum:]_-]\+[[:space:]]\+\(connected\|disconnected\)'
then
# found new output
OUTPUT_NUM=$(( $OUTPUT_NUM + 1 ))
# get name, first non-whitespace block
OUTPUT[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -o '^[^[:space:]]\+')"
# get status by keyword
STATUS[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -ow 'connected\|disconnected')"
# check if primary
ROLE[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -ow 'primary')"
# get mode (XxY string)
MODE_CURRENT[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -o '[0-9]\+x[0-9]\+[+-][0-9]\+[+-][0-9]\+' | grep -o '[0-9]\+x[0-9]\+')"
# everything else is irrelevant if output is disconnected
if [ "${STATUS[$OUTPUT_NUM]}" = "connected" ]
then
# get position (+X+Y string and each component)
POSITION[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -o '[0-9]\+x[0-9]\+[+-][0-9]\+[+-][0-9]\+' | grep -o '[+-][0-9]\+[+-][0-9]\+')"
POSITION_X[$OUTPUT_NUM]="$(printf "%s" "${POSITION[$OUTPUT_NUM]}" | grep -o '[+-][0-9]\+' | head -n 1)"
POSITION_Y[$OUTPUT_NUM]="$(printf "%s" "${POSITION[$OUTPUT_NUM]}" | grep -o '[+-][0-9]\+' | tail -n 1)"
# active reflect and rotation settings are between ") ... ("
RANDR[$OUTPUT_NUM]="${LINE#*)}"
RANDR[$OUTPUT_NUM]="${RANDR[$OUTPUT_NUM]%%(*}"
# get rotation by keyword
ROTATION[$OUTPUT_NUM]="$(printf "%s" "${RANDR[$OUTPUT_NUM]}" | grep -o 'normal\|right\|inverted|\left')"
# words 'x' and 'y' represent reflection, may vary in case
# conditionally compose proper value for --reflect
printf "%s" "${RANDR[$OUTPUT_NUM]}" | grep -qiw 'x' && REFLECTION[$OUTPUT_NUM]="x"
printf "%s" "${RANDR[$OUTPUT_NUM]}" | grep -qiw 'y' && REFLECTION[$OUTPUT_NUM]="${REFLECTION[$OUTPUT_NUM]}y"
[ -z "${REFLECTION[$OUTPUT_NUM]}" ] && REFLECTION[$OUTPUT_NUM]="normal"
fi
# done with this line
continue
fi
# get CRTC
# using crtc in saved configs works around xrandr position restore bug... theoretically
# update: it does not, but messes things up when trying to shuffle CRTCs around enabled outputs
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+CRTC:'
then
CRTC[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -o '[0-9]\+')"
# done with this line
continue
fi
# trigger EDID catching
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+EDID:'
then
EDID_CATCH=1
# done with this line, data comes next
continue
fi
# catch and combine EDID string
if [ "$EDID_CATCH" = "1" ]
then
# capture while line contains only hexadecimal string
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+[[:xdigit:]]\+$'
then
EDID[$OUTPUT_NUM]="${EDID[$OUTPUT_NUM]}$(printf "%s" "$LINE" | grep -o '[[:xdigit:]]\+')"
# get next line
continue
else
# edid string is over
unset EDID_CATCH
# extract data. Mad skillz in voodoo magic are used here.
EDID[$OUTPUT_NUM]=$(printf "%s" "${EDID[$OUTPUT_NUM]}" | fold -w2 | paste -sd\ )
MODEL[$OUTPUT_NUM]="${EDID[$OUTPUT_NUM]##*fc}"
MODEL[$OUTPUT_NUM]="${MODEL[$OUTPUT_NUM]%%0a*}"
MODEL[$OUTPUT_NUM]="$(printf "%s" "${MODEL[$OUTPUT_NUM]}" | xxd -r -p | tr -cd '\11\12\15\40-\176' | sed 's/[^a-zA-Z0-9_[:space:]-]//g;s/^[[:space:]]\+\|[[:space:]]\+$//g;s/[[:space:]]\+/_/g')"
SERIAL[$OUTPUT_NUM]="${EDID[$OUTPUT_NUM]##*ff}"
SERIAL[$OUTPUT_NUM]="${SERIAL[$OUTPUT_NUM]%%0a*}"
SERIAL[$OUTPUT_NUM]="$(printf "%s" "${SERIAL[$OUTPUT_NUM]}" | xxd -r -p | tr -cd '\11\12\15\40-\176' | sed 's/[^a-zA-Z0-9_[:space:]-]//g;s/^[[:space:]]\+\|[[:space:]]\+$//g;s/[[:space:]]\+/_/g')"
# do not skip line here, it may contain other data
fi
fi
# trigger transformation catching
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+Transform:'
then
TRANSFORM_CATCH=1
# remove header
LINE="${LINE#*:}"
# do not skip, data already begins on the same line
fi
# catch and combine transformation data
if [ "$TRANSFORM_CATCH" = "1" ]
then
# capture while line contains only digits and spaces
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+[0-9\.[:space:]-]\+$'
then
# we need all the figures separated by commas in one line
[ -n "${TRANSFORM[$OUTPUT_NUM]}" ] && FORMATTER=',' || unset FORMATTER
TRANSFORM[$OUTPUT_NUM]="${TRANSFORM[$OUTPUT_NUM]}${FORMATTER}$(printf "%s" "$LINE" | sed 's/[^0-9\.[:space:]-]//g;s/^[[:space:]]\+\|[[:space:]]\+$//g;s/[[:space:]]\+/,/g')"
# get next line
continue
else
# transform data is over
unset TRANSFORM_CATCH
# do not skip cycle here, we are not on transform line any more
fi
fi
# get gamma and brightness
printf "%s" "$LINE" | grep -q '^[[:space:]]\+Gamma:' && GAMMA[$OUTPUT_NUM]="$(printf "%s" "$LINE" | sed 's/^[[:space:]]\+Gamma:[[:space:]]*//')"
printf "%s" "$LINE" | grep -q '^[[:space:]]\+Brightness:' && BRIGHTNESS[$OUTPUT_NUM]="$(printf "%s" "$LINE" | sed 's/^[[:space:]]\+Brightness:[[:space:]]*//')"
# get preferred mode
if printf "%s" "$LINE" | grep -q '^[[:space:]]\+[0-9]\+x[0-9]\+'
then
# get by +preferred keyword
if printf "%s" "$LINE" | grep -q '\+preferred'
then
MODE_PREFERRED[$OUTPUT_NUM]="$(printf "%s" "$LINE" | grep -o '[0-9]\+x[0-9]\+')"
fi
continue
fi
done < <(xrandr --verbose)
IFS=$OIFS
# get DPI value
DPI="$(xdpyinfo | grep 'resolution:' | grep -o '[0-9]\+x[0-9]\+' | head -n 1 | cut -d 'x' -f 1)"
}
dump_args(){
# return current screen config formatted in xrandr cli arguments
for OUTPUT_NUM in ${!OUTPUT[@]}
do
printf "%s" "--output ${OUTPUT[$OUTPUT_NUM]} "
if [ -z "${MODE_CURRENT[$OUTPUT_NUM]}" ]
then
printf "%s" "--off "
else
printf "%s" "--mode ${MODE_CURRENT[$OUTPUT_NUM]} --pos ${POSITION_X[$OUTPUT_NUM]}x${POSITION_Y[$OUTPUT_NUM]} "
[ -n "${ROTATION[$OUTPUT_NUM]}" ] && printf "%s" "--rotate ${ROTATION[$OUTPUT_NUM]} "
[ -n "${REFLECTION[$OUTPUT_NUM]}" ] && printf "%s" "--reflect ${REFLECTION[$OUTPUT_NUM]} "
# disable CRTC saving, serves no real purpose, but breaks things sometimes.
# [ -n "${CRTC[$OUTPUT_NUM]}" ] && printf "%s" "--crtc ${CRTC[$OUTPUT_NUM]} "
[ -n "${GAMMA[$OUTPUT_NUM]}" ] && printf "%s" "--gamma ${GAMMA[$OUTPUT_NUM]} "
[ -n "${BRIGHTNESS[$OUTPUT_NUM]}" ] && printf "%s" "--brightness ${BRIGHTNESS[$OUTPUT_NUM]} "
[ -n "${TRANSFORM[$OUTPUT_NUM]}" ] && printf "%s" "--transform ${TRANSFORM[$OUTPUT_NUM]} "
[ "${ROLE[$OUTPUT_NUM]}" = "primary" ] && printf "%s" "--primary "
fi
done
[ -n "$DPI" ] && printf "%s" "--dpi $DPI "
}
dump_id(){
# generate and return setup ID based on output names, display models and serial numbers
FORMATTER=
for OUTPUT_NUM in ${!OUTPUT[@]}
do
if [ "${STATUS[$OUTPUT_NUM]}" = "connected" ]
then
[ -n "$FORMATTER" ] && printf "%s" "-"
FORMATTER=1
printf "%s" "${OUTPUT[$OUTPUT_NUM]}"
[ -n "${MODEL[$OUTPUT_NUM]}" ] && printf "%s" "-${MODEL[$OUTPUT_NUM]}"
# do not output serial number, makes setups more portable
#[ -n "${SERIAL[$OUTPUT_NUM]}" ] && printf "%s" "-${SERIAL[$OUTPUT_NUM]}"
fi
done
}
save_setup(){
SETUP_ID=$(dump_id)
SETUP_ARGS=$(dump_args)
printf "%s\n" "Saving configuration for setup \"${SETUP_ID}\"..."
printf "%s\n" "$SETUP_ARGS" > "$CONF_DIR/${SETUP_ID}.rerandr"
}
show_setup(){
SETUP_ID=$(dump_id)
SETUP_ARGS=$(dump_args)
printf "%s\n" "${SETUP_ID}"
printf "%s\n" "$SETUP_ARGS"
}
apply_saved(){
# read from config file, strip everything but injection-safe characters
SAVED_SETUP_ARGS="$(cat "$CONF_DIR/${SETUP_ID}.rerandr" | sed 's/[^[:alnum:][:space:]\.,:_+-]//g')"
if [ "$SETUP_ARGS" = "$SAVED_SETUP_ARGS" ]
then
printf "%s\n" "Nothing to do."
else
# first disable everything unneeded, this works around xrandr CRTC allocation bug
# preserve framebuffer size to preserve window positions
unset DISABLE_ARGS
for OUTPUT_NUM in ${!OUTPUT[@]}
do
if [ -n "${MODE_CURRENT[$OUTPUT_NUM]}" ] && printf "%s" "$SAVED_SETUP_ARGS" | grep -q "\--output ${OUTPUT[$OUTPUT_NUM]} --off"
then
printf "%s\n" "Disabling unneeded output ${OUTPUT[$OUTPUT_NUM]}"
DISABLE_ARGS="$DISABLE_ARGS --output ${OUTPUT[$OUTPUT_NUM]} --off"
fi
done
[ -n "$DISABLE_ARGS" ] && xrandr --fb "$DIMENSIONS" $DISABLE_ARGS
# apply saved config
xrandr $SAVED_SETUP_ARGS
if [ "$?" = "0" ]
then
# if applied successfully, go to hooks.
applyhooks
else
# if something wend wrong, go through auto routine
printf "%s\n" "Failed to apply saved configuration, trying auto..."
apply_auto
fi
fi
}
apply_auto(){
# autostacking
# first disable disconnected outputs preserving framebuffer size
# mark active and primary outputs in the process
unset PRIMARY_NUM DISABLE_ARGS ACTIVE_NUMS ENABLE_NUMS LAST_ENABLED
for OUTPUT_NUM in ${!OUTPUT[@]}
do
if [ "${STATUS[$OUTPUT_NUM]}" = "disconnected" -a -n "${MODE_CURRENT[$OUTPUT_NUM]}" ]
then
printf "%s\n" "Disabling disconnected output \"${OUTPUT[$OUTPUT_NUM]}\"..."
DISABLE_ARGS="$DISABLE_ARGS --output ${OUTPUT[$OUTPUT_NUM]} --off"
# clear mode for output to be disabled
MODE_CURRENT[$OUTPUT_NUM]=
elif [ "${STATUS[$OUTPUT_NUM]}" = "connected" -a -n "${MODE_CURRENT[$OUTPUT_NUM]}" -a "${ROLE[$OUTPUT_NUM]}" = "primary" ]
then
printf "%s\n" "found connected and active primary output ${OUTPUT[$OUTPUT_NUM]}"
PRIMARY_NUM=$OUTPUT_NUM
# collect active outputs
ACTIVE_NUMS=( "${ACTIVE_NUMS[@]}" "$OUTPUT_NUM" )
elif [ "${STATUS[$OUTPUT_NUM]}" = "connected" -a -n "${MODE_CURRENT[$OUTPUT_NUM]}" ]
then
# collect active outputs
ACTIVE_NUMS=( "${ACTIVE_NUMS[@]}" "$OUTPUT_NUM" )
elif [ "${STATUS[$OUTPUT_NUM]}" = "connected" -a -z "${MODE_CURRENT[$OUTPUT_NUM]}" ]
then
# collect inactive outputs to be enabled
ENABLE_NUMS=( "${ENABLE_NUMS[@]}" "$OUTPUT_NUM" )
fi
done
# apply disabing outputs
[ -n "$DISABLE_ARGS" ] && xrandr --fb "$DIMENSIONS" $DISABLE_ARGS
# if there is no active primary, make one from first active or whatever
if [ -z "$PRIMARY_NUM" -a -n "${ACTIVE_NUMS[*]}" ]
then
printf "%s\n" "Making output ${OUTPUT[${ACTIVE_NUMS[0]}]} primary"
xrandr --fb "$DIMENSIONS" --output "${OUTPUT[${ACTIVE_NUMS[0]}]}" --auto --primary
PRIMARY_NUM="${ACTIVE_NUMS[0]}"
elif [ -z "$PRIMARY_NUM" -a -z "${ACTIVE_NUMS[*]}" ]
then
printf "%s\n" "Enabling output ${OUTPUT[${ENABLE_NUMS[0]}]} as primary"
xrandr --fb "$DIMENSIONS" --output ${OUTPUT[${ENABLE_NUMS[0]}]} --auto --primary
ACTIVE_NUMS=( "${ENABLE_NUMS[0]}" "${ACTIVE_NUMS[@]}" )
ENABLE_NUMS[0]=
fi
# start enabling and stacking outputs
# enable sequential, single command will fail when we run out of CRTCs.
LAST_NUM="$PRIMARY_NUM"
for OUTPUT_NUM in ${ACTIVE_NUMS[@]} ${ENABLE_NUMS[@]}
do
if [ "$OUTPUT_NUM" != "$PRIMARY_NUM" ]
then
printf "%s\n" "Putting ouptut ${OUTPUT[$OUTPUT_NUM]} next to ${OUTPUT[$LAST_NUM]}"
xrandr --output "${OUTPUT[$OUTPUT_NUM]}" --auto "$STACK_STRATEGY" "${OUTPUT[$LAST_NUM]}"
LAST_NUM="$OUTPUT_NUM"
fi
done
# in case framebuffer is still not updated to current config
xrandr --auto
applyhooks
}
apply_setup(){
# get data
SETUP_ID=$(dump_id)
SETUP_ARGS=$(dump_args)
# decide if restore saved or go with auto
if [ -r "$CONF_DIR/${SETUP_ID}.rerandr" ]
then
printf "%s\n" "Applying configuration for setup \"${SETUP_ID}\"..."
apply_saved
else
printf "%s\n" "No configuration found for setup \"${SETUP_ID}\"."
apply_auto
fi
}
getsessionpid(){
# got this from fbpanel xlogout script, refined.
# find PID of session manager process to exit when it no longer exists
# get display number
DPY=${DISPLAY#*\:}
DPY=${DPY%%\.*}
# get X pid
XPID=$(cat "/tmp/.X${DPY}-lock" | grep -o '[0-9]\+')
# get pid of xdm (or gdm, kdm, simple xinitrc, etc). usually it's parent of X
XDMPID=$(ps -o ppid --no-headers --pid=$XPID)
# recursivly find child of xdm that was started in home dir -
# it's user's session start up script
pid_scan(){
unset XDMCHILDREN
while [ $# != 0 ]; do
XDMCHILDREN="$XDMCHILDREN $(ps --no-headers -o pid --ppid=$1)"
shift
done
for pid in $XDMCHILDREN; do
if cwd=$(ls -al /proc/$pid/cwd 2>/dev/null); then
cwd=`sed 's/.*-> //' <<< $cwd`
[ "$cwd" = "$HOME" ] && printf "%s\n" "$pid" && return
fi
done
pids=$XDMCHILDREN
[ -n "$pids" ] && pid_scan $XDMCHILDREN;
}
SESSIONPID=`pid_scan $XDMPID`
[ -z "$SESSIONPID" ] && printf "%s\n" "No session parent found" >&2 && false
[ -n "$SESSIONPID" ] && printf "%s" "$SESSIONPID"
}
check_udev(){
[ ! "$(cat "$UDEV_RULE_FILE" 2>/dev/null)" = "$UDEV_RULE" ] && printf "%b" "Info:\trerandr3 udev rule \"$UDEV_RULE_FILE\" is missing or invalid.\n\tAutomatic triggering is not possible.\n\tRun \"$(basename "$0") insert-udev\" to insert rule.\n"
}
insert_udev(){
if [ "$(id -ru)" = "0" ]
then
printf "%s\n" "$UDEV_RULE" > "$UDEV_RULE_FILE"
udevadm control --reload
else
printf "%s\n" "You are not root. Can not insert udev rule" >&2
exit 1
fi
}
if [ "$1" = "help" -o "$1" = "--help" -o "$1" = "-h" ]
then
show_help
elif [ "$1" = "save" ]
then
prepare_confdir
get_data
save_setup
elif [ "$1" = "restore" ]
then
prepare_confdir
get_data
apply_setup
elif [ "$1" = "show" ]
then
prepare_confdir
get_data
show_setup
elif [ "$1" = "trigger-save" ]
then
[ -p "$SOCKET" -a "$(stat -c %a "$SOCKET")" = "666" ] && printf "%s\n" "save" > "$SOCKET"
true
elif [ "$1" = "trigger-restore" ]
then
# if there is no Xorg running, exit (workaround for messing with boot via udev rule)
ps -C "$XORG_PROCESS" >/dev/null 2>/dev/null || exit 0
[ -p "$SOCKET" -a "$(stat -c %a "$SOCKET")" = "666" ] && printf "%s\n" "restore" > "$SOCKET"
true
elif [ "$1" = "daemon" ]
then
prepare_pipe 2>&1 | tee -a "$DAEMON_LOG"
check_udev | tee -a "$DAEMON_LOG"
# clean pipes
cat /dev/null > "$SOCKET" &
cat "$SOCKET" | head -c 128 > /dev/null
"$0" restore 2>&1 | tee -a "$DAEMON_LOG"
# session tracking
getsessionpid
MAINPID="$$"
printf "%b" "Started daemon. PID: $MAINPID\nTracking session leader: PID $SESSIONPID\n" | tee -a "$DAEMON_LOG"
while true
do
kill -0 $SESSIONPID 2> /dev/null || { printf "%s\n" "exit" > "$SOCKET" ; exit 0 ; }
kill -0 $MAINPID 2> /dev/null || exit 0
sleep 3s
done &
applyhooks
while true
do
printf "%s\n" "Waiting for socket trigger..."
ACTION="$(cat "$SOCKET" | head -c 8 | sed 's/[^[:alpha:]_-]//g')"
if [ "$ACTION" = "save" ]
then
"$0" save
ACTION=
elif [ "$ACTION" = "restore" ]
then
"$0" restore
ACTION=
elif [ "$ACTION" = "exit" ]
then
printf "%s\n" "Exiting..."
exit 0
else
printf "%s\n" "Unknown action \"$ACTION\"."
ACTION=
sleep 5s
fi
done 2>&1 | tee -a "$DAEMON_LOG"
elif [ "$1" = "hooks" ]
then
applyhooks
elif [ "$1" = "insert-udev" ]
then
printf "%s\n" "Will now insert udev rule:
$UDEV_RULE
in file:
$UDEV_RULE_FILE
Root access required.
Press any key to continue, or Ctrl+c to cancel"
read -sn1
sudo "$0" root-insert-udev
elif [ "$1" = "root-insert-udev" ]
then
insert_udev
else
show_help
exit 1
fi