Skocz do zawartości
Gość <account_deleted>

LIRC - trochę inaczej... :)

Rekomendowane odpowiedzi

Gość <account_deleted>

Dnia pewnego stwierdziłem, że mam niezły bajzel jeśli chodzi o sterowanie z pilota: dziesiątki drobnych skryptów do wszystkiego, niektóre odpalające kolejne skrypty i programy, wszystko w drobnych kawałkach i do tego irexec, który jest imo raczej daleki od ideału. Aplikacje z natywnym plugiem do lircd to inna bajka, ale i tak wolę je sterować po swojemu ;) Tak więc po paru godzinach rozproszonych w tygodnie ukleiłem skrypcik zastępujący w pełni irexec, posiadający w zasadzie nieograniczone możliwości konfiguracji i rozbudowy. Nazwałem to sobie lircexec.sh, a brzmi jak następuje:

 

#!/bin/bash#%TAB=4#dependancies:#lirc, xautomation, wmctrl, notify-osd# lircexec.sh v0.20 <testing possibilities>  by tomazzi <tomazzi@wp.pl>ver="0.20"# This script tends to replace irexec (part of LIRC project, see [url=http://www.lirc.org),][url=http://www.lirc.org),][url=http://www.lirc.org),]http://www.lirc.org),[/url][/url][/url]# giving more flexibility and capabilities to control typical Linux Desktop. For now,# it is not applicable for dedicated HTPC solutions, but maybe someday, someone will# convert it to fast c-code with sockets support, etc? Who knows... :)####################################################################################### As usual: this code is free and that means free from any kind of warranty too. #### It can format your hard disk or even damage your hardware - I don't care.      #######################################################################################-----------------------------| config:shopt -s expand_aliases #(it is on by default only for interactive shells)shopt -u checkwinsize #do not check terminal size#default config:lircpath=$(dirname "$0")"/"	#following slash required here! (but not in --path option)rampath=""osd_level=1 #0= osd off, 1= report mode change only, 2= all modes & events (n/a for now)dbg_mode=0 #1= debug mode onkeypress_flt=250000000 #default time limit for repeating keypresses (nanosecs here, secs in --keyfilter)#command line parameters:for parm in $*do	parmtype=$(sed -n 's_=_=\n_;P' <(echo $parm))	parmval=$(sed -n 's/'"$parmtype"'//p' <(echo $parm))	case $parmtype in		--path=)			#path to mode-files and helper scripts, add following slash			if [ -d $parmval ];then				lircpath=$(echo $parmval | sed -n 's_[^/]$_&/_;p')			else				echo "Path not found: "$parmval ;exit 1			fi ;;		--fastpath=)			#copy lircpath/files to mounted ramdisk for faster execution, lower CPU load (thanks to tempfs/ramfs)			#and for lower HDD I/O load - it has more important things to do ;)			#...assuming one have rights to do so...			if [ -d $parmval ];then				rampath=$(echo $parmval | sed -n 's_[^/]$_&/_;p')			else				echo "FastPath not found: "$parmval ;exit 1			fi ;;		--debug)			dbg_mode=1 ;;		--osdlevel=)			osd_level=$parmval ;;		--keyfilter=)			#seconds to nanoseconds conversion			#desired formats: ii ii.ff .ff but even most stupid errors will be corrected, f.e. "0an0ther.some0thing?.23" -> 0.023s -> 23000000ns :)			keypress_flt=$(echo "$parmval" | sed -n 's_[.0-9]$_&\._ #one dot at least, even for whole numbers													#extract digits, allow first dot only:													s_[^\.0-9]__g;s_\.__2g													#integer part:													h													s_\.[.0-9]*$__													#fraction part, formatting:													x													s_^[^\.]*\.__													s_^[0-9]*$_&000000000_;s_[0-9]_&\n_9													#merge, remove leading zeros:													H;x													s_\n__													s_^[0]*__;s_^\n_0\n_;P' ) ;;		*)			echo -e "\nUnknown parameter: "$parm"\nUsage:\n $0 [ --path= | --fastpath= | --debug | --osdlevel= | --keyfilter= ]"			exit 1 ;;	esacdone#-----------------------------| aliases: fast and tricky way to obtain "self-modifying" code... :)if [ $dbg_mode = "1" ]; then	alias dbg_echo=echo	alias dbg_cmd=""	alias dbg_else=elseelse	alias dbg_echo=#	alias dbg_cmd=#	alias dbg_else=#fiif [ $keypress_flt != "0" ]; then	alias keyfilter=""else	alias keyfilter=#fi#TODO: osd notifications, simple workaround for now:case $osd_level in	1)		alias osd_mode_info1='notify-send --urgency=low --icon=$lircpath"LIRC_logo.png"'		alias osd_mode_info2=# ;;	2)		alias osd_mode_info1='notify-send --urgency=low --icon=$lircpath"LIRC_logo.png"'		alias osd_mode_info2='notify-send --urgency=low --icon=$lircpath"LIRC_logo.png"' ;;	*)		alias osd_mode_info1=#		alias osd_mode_info2=# ;;esac#-----------------------------| functions:keycmd()	# $1=keyline field (key command)	{	echo -n ${keydef[$1]}	}keylinedef() # $1=mode-file	{	#key definition in current mode	IFS="%"	keydef=( $(sed -n '/^'${keycat[2]}'/ p' <"$lircpath$1") ) #this converts continous line to dynamic array of words, delimiter="%"	unset IFS	ktype=${keydef[1]}	dbg_echo -en ", ktype:"$ktype": "	}switchmode() # $1=noexit (option)	{	if [ -f "$lircpath$newmode" ]; then		caption=$(sed -n '2 {s_^caption:[ \t]*%__;s_%_\n_;P;q}' <$lircpath$newmode)		winid=$(sed -n '/'"$caption"'/!d;s_ _\n_;s_^0x[0]*__;P;q' <(wmctrl -l)) #check if requested target window already exist		dbg_echo -en "\n...search for window: "$caption" ..."		# TRICK: (evaluate "exec <cmd>" &) - this effectively detaches <cmd> from current process, keeping		# all advantages of evaluateuate  -> lower memory usage, higher speed, lower overall system load !!!		# "Disown" does not do this (!!!) - it creates "ghosts" (waiting) parent instances as a way to separate child process.		# exec will terminate forked evaluateuate - both are built-in, so there's no extra IO traffic.		if [ "$1" != "noexit" ]; then			#if app from previous mode appears to be dead already, do not execute cmd_exit			dbg_echo -en "\nexecuting cmd_exit: "; sed -n '3 {s|^cmd_exit:[ \t]*%||;s|%|\n|;P;q}' <$lircpath$lircmode			(evaluate "exec $(sed -n '3 {s|^cmd_exit:[ \t]*%||;s|%|\n|;P;q}' <$lircpath$lircmode)" &)	#cmd_exit		fi		if [ "$winid" = "" ]; then			#execute app_cmd only if target app is not running already			dbg_echo -en "window not found.\nexecuting app_cmd: "; sed -n '1 {s|^app_cmd:[ \t]*%||;s|%|\n|;P;q}' <$lircpath$newmode 			(evaluate "exec $(sed -n '1 {s|^app_cmd:[ \t]*%||;s|%|\n|;P;q}' <$lircpath$newmode)" &)	#app_cmd			#disown			wait_modesw="1" #next focus check will update winid - this should give enough time for window initialization			#the trick is in that user normally will wait for window to apperar before applying comands. Mode will be dropped otherwise.		else			dbg_echo -e "found: requested app already running!\nbring to front: 0x"$winid			wmctrl -i -a "0x$winid"		fi		lircmode=$newmode		chkfocus=$(sed -n '4 {s|^chk_focus:[ \t]*%||;s|%|\n|;P;q}' <$lircpath$newmode) #should I check focus in this mode?		osd_mode_info1 $lircmode		dbg_echo "      new mode: "$lircmode			dbg_else		dbg_echo "mode-file not exist:"$lircpath$newmode;	fi	}ktype_mapping()	{	case $ktype in		kt_normal|kt_mode|kt_cycle|kt_seqset)			key_exec "$lircmode";;		kt_disabled)			dbg_echo ", key disabled" 			;; #key disabled (global override) - do nothing		*)  # key not defined in current mode or kt_global			# temporarily force mode-global to find key-definition			keylinedef "mode-global"			#if_debug "badkey"			dbg_echo -en "\nmode override: global"	#debug			key_exec "mode-global" ;;	esac	}key_exec() # $1=mode (current or global) for override mode	{	case $ktype in		kt_normal)			dbg_echo -ne "\n cmd: "; keycmd 5			(evaluate "exec $(keycmd 5)" &) ;; #execute cmd0		kt_mode)			newmode=$(keycmd 5)			if [ $lircmode != $newmode ]; then				dbg_echo -n ", new mode request:" $newmode				#new mode requested				switchmode			else				dbg_echo ": mode already set: (" $newmode ")"				#requested mode already active ($caption and $winid initialized)				if [ $1 = "mode-global" ];then					#mode=global or global mode override, key not defined in current mode					if [ -z "$(sed -n '/'$winid'/!d;p;q' <(wmctrl -l))" ]; then						#if target window does not exist, switch to mode-global (f.e. app was closed manually)						dbg_echo "target window (" $winid ") not exist, switchmode=global"						newmode="mode-global"						switchmode noexit #(do not execute cmd_exit from prev. mode when program appears to be dead already)					else						#otherwise: bring target window to front - mode key works as shortcut ;)						#besides, this prevents unintentional launching of another instatnce or closing invisible window 						dbg_echo "target window:0x"$winid "has no focus, bring to front"						wmctrl -i -a "0x$winid"					fi				fi			fi ;;		kt_cycle)			kstate=${keydef[3]}			dbg_echo ", kt_cycle state:"$kstate			 #indirect addressing of command field in current key def. line			(evaluate "exec  ${keydef[5 + $kstate]}" &)			(( kstate += 1 ))			if (( kstate > ${keydef[4]} )); then				kstate=0				dbg_echo ", kt_cycle reset:"$kstate			dbg_else				dbg_echo ", kt_cycle next:"$kstate			fi			#save kstate to file (remember current state in key def. line)			#$1 - current or global mode (override)			sed -i '/^'${keycat[2]}'/ s_%[0-9]%_%'$kstate'%_' "$lircpath$1" ;;		kt_seqset)			#set (or reset) kt_cycle sequence for current/override mode kt_cycle keys			#cmd0 (field 5) = key name, cmd1 (field 6) = new kt_cycle state / seq.step number			seqset_key=$(echo ${keydef[5]}) #echo is the simplest string converter			dbg_echo -en "\nseqset target key: $seqset_key sequence step set to: ${keydef[6]}\n" 			sed -i '/^'$seqset_key'/ s_%[0-9]%_%'"${keydef[6]}"'%_' "$lircpath$1" ;;	esac	}scan()	{	while read -a keycat	do	keyfilter keyflt_now=$(date +%s%N); keyflt_delta=$(( keyflt_now - keyflt_last ))	keyfilter dbg_echo -ne "\nkeytime: "$keyflt_now"ns ,delta: "$keyflt_delta"ns"	keyfilter keyflt_last=$keyflt_now	dbg_echo -ne "\n----> scan: "$lircmode", key: "${keycat[2]}	keyfilter if (( keypress_flt > keyflt_delta )) ;then	keyfilter dbg_echo -ne "\n      keyfilter: keypress ignored!\n"	keyfilter continue	keyfilter fi	if [ "$chkfocus" = "1" ];then		if [ $wait_modesw = 1 ]; then		#just after mode switching: get target window id		winid=$(sed -n '/'"$caption"'/!d;s_ _\n_;s_^0x[0]*__;P;q' <(wmctrl -l))		wait_modesw=0 #checked		fi		dbg_echo -n ", chkfocus="$chkfocus		#check if current-mode app window has focus		#get active window ID		activewin=$(sed -n '/_NET_ACTIVE_WINDOW/!d;s/^.*# 0x//;p;q' <(xprop -root -notype))		dbg_echo -n ", active:0x"$activewin" vs target:0x"$winid		if [ "$activewin" = "$winid" ];then			#key definition in current mode			dbg_echo -n " = focus hit"			keylinedef $lircmode			ktype_mapping		else			#current-mode window has no focus - override keymap with mode-global			#TODO: check if window still exist when focus is lost !!!			dbg_echo -n " = !focus lost! mode override: global"					keylinedef "mode-global"			key_exec "mode-global"		fi	else		dbg_echo -n ", chkfocus0="$chkfocus		keylinedef $lircmode		ktype_mapping	fi	keyfilter dbg_echo -en " :exec time: "$(( $(date +%s%N) - keyflt_now ))"ns\n"	done	}#-----------------------------| begin#move to ram?if [ "$rampath" != "" ]; then	dbg_echo -en "--fastpath: moving to ram\n"	mkdir $rampath"lircexec"	cp -RP --remove-destination "$lircpath"* -t $rampath"lircexec"	rm $rampath"lircexec/"$(basename $0) #script already in memory - no need to waste additional ram space	lircpath=$rampath"lircexec/"fiPATH=$PATH:$lircpath#wait for lircd to startwhile [ -z "$(pidof lircd)" ] ; do	dbg_echo -en "\nwaiting for lirc daemon...\n"	sleep 1	done#startup, init runtime varslircmode="mode-global"winid=""wait_modesw="0"activewin=""caption=""chkfocus="0"keyfilter keyflt_last=$(date +%s%N); keyflt_now="0"; keyflt_delta="0"#print config if debugging is enabled:dbg_echo -en "\n lircexec.sh v$ver, startup configuration:\n path="$lircpath"\n osd_level="$osd_leveldbg_echo -en "\n keyfilter="$keypress_flt"ns\n dbg_mode="$dbg_modekeyfilter dbg_echo -e "\ntimestamp: "$keyflt_last"ns"#lircd running, start key scanning: (irw is part of LIRC project, see [url=http://www.lirc.org)][url=http://www.lirc.org)][url=http://www.lirc.org)]http://www.lirc.org)[/url][/url][/url]#without parameters irw connects to default lircd socket: "/var/run/lirc/lircd"(irw | scan &)#fork in subshell, then exit - without this, current instance stays in wait state (wchannel),#while the second will be invoked to satisfy pipe feeder (irw)exit 0

 

W skrócie:

Skrypt mapuje klawisze pilota w zależności od aktualnego trybu (lircmode), sprawdzjąc jednak czy okno docelowe jest aktywne i czy wogóle istnieje - np. włączenie trybu gdy dana aplikacja jest już odpalona powoduje wyciągnięcie jej okna do przodu. Obsługiwane są klawisze globalne, oddzielne komendy dla wejścia w dany tryb (odpalenie aplikacji) i dla wyjścia, filtrowanie klawiszy typu autorepeat, pełny dostęp do klawiszy XFree86 - poprzez xte i odpowiednie bindingi -> np wyświetlanie notyfikacji volume/mute, notyfikacje o aktualnym mapowaniu pilota (lircmode), w miarę wszystkomówiący debug... w skrócie ;)

 

Wszystkie akcje definiowanie są w pliku tekstowym, przykład dla "mode-power" czyli okienka zamykania systemu:

 

app_cmd:	%power-selector.sh%caption:	%Shut Down%cmd_exit:	%xte "key Return"%chk_focus:	%0%POWER	 	%kt_disabled% 	%0%0		%					%CH_UP	 	%kt_normal%		%0%0		%xte "key Up"		%CH_DOWN	 	%kt_normal%		%0%0		%xte "key Down"		%ENTER	 	%kt_mode%		%0%0		%mode-global		%

przykład dla SMplayera:

app_cmd:	%smplayer%caption:	%SMPlayer%cmd_exit:	%xte "keydown Control_L" "key x" "keyup Control_L"%chk_focus:	%1%POWER	 	%kt_mode%		%0%0		%mode-global		%MUTE	 	%kt_normal%		%0%0		%xte "key m"		%	# Mute - smplayer internalFULLSCREEN	%kt_normal%		%0%0		%xte "key f"		%	# fullscreen togglePREVIOUS	%kt_normal%		%0%0		%xte "key Down"	%	# -1min.NEXT	 	%kt_normal%		%0%0		%xte "key Up"	%	# +1min.PLAYPAUSE	%kt_normal%		%0%0		%xte "key BackSpace"	%REWIND	 	%kt_normal%		%0%0		%xte "key Left"	%	# -10sec.STOP	 	%kt_normal%		%0%0		%xte "key s"		%FFORWARD	%kt_normal%		%0%0		%xte "key Right"	%	# +10sec.EPG	 		%kt_normal%		%0%0		%xte "key v"		%	#subtitles visiblePIP	 		%kt_normal%		%0%0		%xte "keydown Shift_L" "key w" "keyup Shift_L"	%	#Auto Zoom

 

 

Wszelkie pomysły mile widziane - krytyka: też, pod warunkiem że konstruktywna ;)

 

---------------------

Update to v0.20:

...parka kt_cycle | kt_seqset daje całkiem nieplanowane możliwości ;)

--fastpath=/path/to/mounted/ramdisk - działa.

 

No ale dobra: podstawowy cel, jakim było pozbycie się upośledzonego irexec uważam za osiągnięty - na razie muszę sobie odpuścić z braku czasu, ale przy następnej okazji zabiorę się za wersję w cpp + libcairo ;)

 

Kilka słów o tym rozwiązaniu:

- nie jest zbyt szybkie (czas reakcji typowo kilkanaście ms), ale za to ma zdecydowanie mniejszy footprint niż wersja w pythonie (z którą też eksperymentowałem). Do normalnych zadań wystarcza... zaliczając do normalnych również sterowanie VLC odpalonym na drugim kompie... ;)

- tak na prawdę irw nie jest potrzebne: bash może czytać tcp socket z lirca, ale jest to mniej wygodne - dostajemy kody w hexie zamiast nazw klawiszy.

- tak naprawdę wogóle nie potrzeba lircd: bash może czytać character_devices jakimi są porty COM oraz wszystkie (afain) porty I2C w kartch TV. ...tyle że wtedy dostajemy format binarny i do tego już trzeba ukleić jakiś wrapper co by definicje klawiszy wyglądały po ludzku...

- notify-osd to typowy pain-in-arse - każdy, nawet stary client osd daje większe możliwości do zabawy - niemiej jednak wygląda ładnie przy włączonym composite i nie gryzie się z pozostałymi aplikacjami. Zapowiadają się zmiany w tym temacie ... zobaczymy.

Edytowane przez tomazzi

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Dołącz do dyskusji

Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.

Gość
Dodaj odpowiedź do tematu...

×   Wklejono zawartość z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Odnośnik został automatycznie osadzony.   Przywróć wyświetlanie jako odnośnik

×   Przywrócono poprzednią zawartość.   Wyczyść edytor

×   Nie możesz bezpośrednio wkleić grafiki. Dodaj lub załącz grafiki z adresu URL.

Ładowanie


×
×
  • Dodaj nową pozycję...