From 2a2d4756413cddd8143c9f2470f2d1a934dc0bb3 Mon Sep 17 00:00:00 2001
From: Salt <rehashedsalt@cock.li>
Date: Fri, 21 Jun 2019 22:19:38 -0500
Subject: [PATCH] Cleanup, wait for WM to init before firing XDG autostarts

---
 README.md   |  6 +++++-
 firestarter | 56 ++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index 20d3394..5aa2170 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ command -v lemonbar
 ~/.bin/lemonbar.sh | lemonbar
 ```
 
-After all these programs have been started, firestarter executes `~/.firestarterrc` if it exists.
+After all these programs have been started, firestarter starts XDG autostart applications and executes `~/.firestarterrc` if it exists.
 
 ## Logging
 
@@ -68,6 +68,10 @@ Firestarter, in addition to spawning the programs in the default configs, also i
 | 53 | Failed to create logging directory (and logging is enabled) |
 | 54 | `HOME` does not exist or is unreadable |
 
+## Idiosyncracies
+
+* The `wm` config file is special; if it exists and a default can be found for it, firestarter will watch the `_NET_WM_NAME` atom on the root window, waiting for it to initialize before starting XDG autostarts. This prevents applications from being started before the WM is ready to manage them. You can disable this by setting `FS_NOWAITWM`.
+
 ## Contribution
 
 Firestarter by no means contains an exhaustive list of all possible programs. If you know of or have created a program that should be added, *please* open an issue about it. The script should be light but its choices massive.
diff --git a/firestarter b/firestarter
index a614fb2..f33c1d0 100755
--- a/firestarter
+++ b/firestarter
@@ -13,6 +13,8 @@ _configdir="${XDG_CONFIG_HOME:-$HOME/.config}/$_name"
 _logdir="${XDG_DATA_HOME:-$HOME/.local/share}/$_name/logs"
 _firestarterrc="$HOME/.firestarterrc"
 _sessionid="$(< /proc/self/sessionid)"
+# Dummy fd for read sleeping
+exec 1023<> <(:)
 
 # Basic functions
 print() {
@@ -41,6 +43,10 @@ err() {
 		exit "$2"
 	fi
 }
+has() {
+	[ -z "$1" ] && return 1
+	command -v "$1" > /dev/null 2>&1
+}
 gettarget() {
 	# Parse a defaults file to get the target program
 	[ -z "$1" ] && return 1
@@ -278,6 +284,7 @@ Start or generate a desktop environment configuration
 Additionally, firestarter responds to the following environment variables:
 
   FS_NOLOG	If nonempty, create no log files
+  FS_NOWAITWM	If nonempty, skip waiting on the WM to initialize
 
 Copyright (c) 2019 rehashedsalt@cock.li
 Licensed under the MIT License
@@ -303,7 +310,7 @@ step_check() {
 }
 step_preexecute() {
 	# Special things that can't use simple configuration files
-	[ "$_dryrun" = "1" ] && return 0
+	[ -n "$_dryrun" ] && return 0
 
 	# Exports
 	export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-firestarter}"
@@ -319,7 +326,7 @@ step_preexecute() {
 		hasdbus=1
 	elif \
 		[ -z "$DBUS_SESSION_BUS_ADDRESS" ] && \
-		command -v dbus-launch > /dev/null 2>&1; then
+		has dbus-launch; then
 		# We have dbus but haven't started up a bus yet
 		eval "$(dbus-launch --exit-with-session --sh-syntax)"
 		hasdbus=1
@@ -327,18 +334,18 @@ step_preexecute() {
 		log "Did not start dbus; some applications may misbehave"
 	fi
 	if [ -n "$hasdbus" ]; then
-		if command -v dbus-update-activation-environment > /dev/null 2>&1; then
+		if has dbus-update-activation-environment; then
 			dbus-update-activation-environment --verbose --systemd --all > /dev/null 2>&1
 		fi
 	fi
 	unset hasdbus
 
 	# kcminit/Qt settings
-	if command -v kcminit > /dev/null 2>&1; then
+	if has kcminit; then
 		log "Initializing KDE Control Module settings"
 		kcminit > /dev/null 2>&1
 		export XDG_CURRENT_DESKTOP="KDE"
-	elif command -v qt5ct > /dev/null 2>&1; then
+	elif has qt5ct; then
 		log "Integrating qt5ct"
 		if [ -z "$QT_QPA_PLATFORMTHEME" ]; then
 			export QT_QPA_PLATFORMTHEME="qt5ct"
@@ -355,7 +362,7 @@ step_preexecute() {
 	fi
 
 	# xhost
-	if command -v xhost > /dev/null 2>&1; then
+	if has xhost; then
 		if xhost +si:localuser:"$(id -un)" > /dev/null 2>&1; then
 			log "Session can be accessed in other sessions by this user"
 		else
@@ -364,7 +371,7 @@ step_preexecute() {
 	fi
 
 	# xresources
-	if [ -n "$DISPLAY" ] && command -v xrdb && [ -r "$HOME/.Xresources" ]; then
+	if [ -n "$DISPLAY" ] && has xrdb && [ -r "$HOME/.Xresources" ]; then
 		if xrdb "$HOME/.Xresources" > /dev/null 2>&1; then
 			log "Loaded in .Xresources"
 		else
@@ -373,7 +380,7 @@ step_preexecute() {
 	fi
 
 	# xset
-	if command -v xset > /dev/null 2>&1; then
+	if has xset; then
 		log "Disabling bell"
 		xset -b
 	fi
@@ -407,7 +414,7 @@ step_execute() {
 		if gettarget "$file"; then
 			target="$_return"
 			log "Found target for \"$filename\": \"$target\""
-			if ! [ "$_dryrun" = "1" ]; then
+			if [ -z "$_dryrun" ]; then
 				if [ -f "$logfile" ]; then
 					[ -f "$logfile.old" ] && rm "$logfile.old"
 					mv "$logfile" "$logfile.old"
@@ -418,7 +425,30 @@ step_execute() {
 	done
 }
 step_postexecute() {
-	# Wait for the WM to initialize, if one was found
+	# Wait for the WM to initialize, if one was found and we have the tools
+	if [ -z "$FS_NOWAITWM" ] && gettarget "$_configdir/wm" && has xprop && has grep; then
+		log "Waiting for WM to initialize: \"$_return\""
+		for (( i=0; i<10; i++ )); do
+			line="$(xprop -root | grep ^_NET_WM_NAME | grep -o '"\S*"$')"
+			if [ -n "$line" ]; then
+				log "WM has intiailized, _NET_WM_NAME atom reads: $line"
+				break
+			fi
+			read -t 1 -u 1023
+		done
+	fi
+	# Start XDG autostarters, if they exist
+	if [ -z "$_dryrun" ]; then
+		if has dex; then
+			dex -a > /dev/null 2>&1
+		elif has fbautostart; then
+			fbautostart > /dev/null 2>&1
+		elif has xdg-autostart; then
+			xdg-autostart ${XDG_CURRENT_DESKTOP:-firestarter}
+		else
+			log "Could not find an XDG autostarter"
+		fi
+	fi
 	# Source in a user script if it exists
 	if [ -r "$_firestarterrc" ] && [ -z "$_dryrun" ]; then
 		log "Sourcing .firestarterrc"
@@ -426,7 +456,7 @@ step_postexecute() {
 	fi
 }
 step_wait() {
-	[ "$_dryrun" = "1" ] && exit 0
+	[ -n "$_dryrun" ] && exit 0
 	log "Waiting for programs to exit"
 	log "Send any termination signal to firestarter to log out"
 	trap step_logout EXIT
@@ -435,7 +465,7 @@ step_wait() {
 }
 step_logout() {
 	log "Logging out"
-	if command -v loginctl > /dev/null 2>&1; then
+	if has loginctl; then
 		# Use loginctl if possible
 		if [ -n "$_sessionid" ]; then
 			loginctl terminate-session "$_sessionid"
@@ -452,7 +482,7 @@ main() {
 	while getopts ":dgh" opt; do
 		case $opt in
 			d)
-				if ! [ "$_dryrun" = "1" ]; then
+				if [ -z "$_dryrun" ]; then
 					log "Performing a dry run"
 					_dryrun=1
 				fi