Skip to content

Commit 3eb85d4

Browse files
author
time-killer-games
authored
Add focus wait to Zenity/KDialog (#2198)
1 parent 4a9b18f commit 3eb85d4

File tree

6 files changed

+133
-60
lines changed

6 files changed

+133
-60
lines changed

CI/solve_engine_deps.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ if [ "$WIDGETS" == "GTK+" ] || [ "$TEST_HARNESS" == true ]; then
3333
LINUX_DEPS="$LINUX_DEPS libgtk2.0-dev"
3434
fi
3535
if [ "$WIDGETS" == "xlib" ] || [ "$TEST_HARNESS" == true ]; then
36-
LINUX_DEPS="$LINUX_DEPS zenity kdialog"
36+
LINUX_DEPS="$LINUX_DEPS zenity kdialog libprocps-dev"
3737
fi
3838

3939
###### Extensions #######

ENIGMAsystem/SHELL/Widget_Systems/xlib/Info/About.ey

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Name: X11 Widgets
55
Identifier: xlib
66
Build-Platforms: MacOSX, Linux, FreeBSD
7-
Represents: Linux, FreeBSD
7+
Represents: None
88
Description: Use Qt with KDialog Widgets by calling "widget_set_system(ws_x11_kdialog)". Use GTK+ with Zenity Widgets by calling "widget_set_system(ws_x11_zenity)". The default will match your current Desktop Environment. License, documentation, and install instructions: https://enigma-dev.org/docs/Wiki/Dialogs#Zenity_Widgets
99
Author: Samuel Venable
1010

ENIGMAsystem/SHELL/Widget_Systems/xlib/Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ SOURCES += $(wildcard Widget_Systems/xlib/*.cpp) Widget_Systems/General/WSdialog
22
override CXXFLAGS += $(shell pkg-config x11 --cflags)
33
override CFLAGS += $(shell pkg-config x11 --cflags)
44
override LDLIBS += $(shell pkg-config x11 --libs) -lz -lpthread
5+
ifeq ($(OS), Linux)
6+
override LDLIBS += -lprocps
7+
else ifeq ($(OS), FreeBSD)
8+
override LDLIBS += -lutil -lc
9+
endif

ENIGMAsystem/SHELL/Widget_Systems/xlib/dialogs.cpp

+101-33
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,43 @@
2323
#include <string>
2424
#include <thread>
2525
#include <chrono>
26+
#include <vector>
2627

2728
#include "dialogs.h"
2829

30+
#include "../../../../CompilerSource/OS_Switchboard.h"
31+
2932
#include "Widget_Systems/widgets_mandatory.h"
3033
#include "Widget_Systems/General/WSdialogs.h"
3134

3235
#include "Universal_System/estring.h"
3336
#include "Platforms/General/PFwindow.h"
3437

35-
#include <X11/Xlib.h>
36-
#include <X11/Xatom.h>
37-
#include <X11/Xutil.h>
38-
3938
#include <sys/types.h>
4039
#include <sys/wait.h>
4140
#include <sys/stat.h>
4241

42+
#include <pthread.h>
4343
#include <libgen.h>
4444
#include <unistd.h>
4545
#include <fcntl.h>
4646

47+
#include <X11/Xlib.h>
48+
#include <X11/Xatom.h>
49+
#include <X11/Xutil.h>
50+
51+
#if CURRENT_PLATFORM_ID == OS_MACOSX
52+
#include <sys/proc_info.h>
53+
#include <libproc.h>
54+
#elif CURRENT_PLATFORM_ID == OS_LINUX
55+
#include <proc/readproc.h>
56+
#elif CURRENT_PLATFORM_ID == OS_FREEBSD
57+
#include <sys/user.h>
58+
#include <libutil.h>
59+
#endif
60+
4761
using std::string;
62+
using std::vector;
4863

4964
namespace enigma {
5065

@@ -72,10 +87,10 @@ static bool kwin_running() {
7287
return bKWinRunning;
7388
}
7489

75-
static unsigned long get_wid_or_pid(Display *display, Window window, bool wid) {
90+
static unsigned long GetActiveWidOrWindowPid(Display *display, Window window, bool wid) {
7691
SetErrorHandlers();
7792
unsigned char *prop;
78-
unsigned long property;
93+
unsigned long property = 0;
7994
Atom actual_type, filter_atom;
8095
int actual_format, status;
8196
unsigned long nitems, bytes_after;
@@ -89,19 +104,19 @@ static unsigned long get_wid_or_pid(Display *display, Window window, bool wid) {
89104
return property;
90105
}
91106

92-
static Window wid_from_top(Display *display) {
107+
static Window WidFromTop(Display *display) {
93108
SetErrorHandlers();
94109
int screen = XDefaultScreen(display);
95110
Window window = RootWindow(display, screen);
96-
return (Window)get_wid_or_pid(display, window, true);
111+
return (Window)GetActiveWidOrWindowPid(display, window, true);
97112
}
98113

99-
static pid_t pid_from_wid(Display *display, Window window) {
100-
return (pid_t)get_wid_or_pid(display, window, false);
114+
static pid_t PidFromWid(Display *display, Window window) {
115+
return (pid_t)GetActiveWidOrWindowPid(display, window, false);
101116
}
102117

103118
// create dialog process
104-
static pid_t process_execute(const char *command, int *infp, int *outfp) {
119+
static pid_t ProcessCreate(const char *command, int *infp, int *outfp) {
105120
int p_stdin[2];
106121
int p_stdout[2];
107122
pid_t pid;
@@ -146,14 +161,79 @@ static pid_t process_execute(const char *command, int *infp, int *outfp) {
146161
return pid;
147162
}
148163

149-
// set dialog transient.
150-
static void force_window_of_pid_to_be_transient(Display *display, pid_t pid) {
164+
#if CURRENT_PLATFORM_ID == OS_MACOSX
165+
static void PpidFromPid(pid_t procId, pid_t *parentProcId) {
166+
proc_bsdinfo proc_info;
167+
if (proc_pidinfo(procId, PROC_PIDTBSDINFO, 0, &proc_info, sizeof(proc_info)) > 0) {
168+
*parentProcId = proc_info.pbi_ppid;
169+
}
170+
}
171+
#endif
172+
173+
static std::vector<pid_t> PidFromPpid(pid_t parentProcId) {
174+
std::vector<pid_t> vec;
175+
#if CURRENT_PLATFORM_ID == OS_MACOSX
176+
int cntp = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
177+
std::vector<pid_t> proc_info(cntp);
178+
std::fill(proc_info.begin(), proc_info.end(), 0);
179+
proc_listpids(PROC_ALL_PIDS, 0, &proc_info[0], sizeof(pid_t) * cntp);
180+
for (int j = cntp; j > 0; j--) {
181+
if (proc_info[j] == 0) { continue; }
182+
pid_t ppid; PpidFromPid(proc_info[j], &ppid);
183+
if (ppid == parentProcId) {
184+
vec.push_back(proc_info[j]);
185+
}
186+
}
187+
#elif CURRENT_PLATFORM_ID == OS_LINUX
188+
PROCTAB *proc = openproc(PROC_FILLSTAT);
189+
while (proc_t *proc_info = readproc(proc, nullptr)) {
190+
if (proc_info->ppid == parentProcId) {
191+
vec.push_back(proc_info->tgid);
192+
}
193+
freeproc(proc_info);
194+
}
195+
closeproc(proc);
196+
#elif CURRENT_PLATFORM_ID == OS_FREEBSD
197+
int cntp; if (kinfo_proc *proc_info = kinfo_getallproc(&cntp)) {
198+
for (int j = 0; j < cntp; j++) {
199+
if (proc_info[j].ki_ppid == parentProcId) {
200+
vec.push_back(proc_info[j].ki_pid);
201+
}
202+
}
203+
free(proc_info);
204+
}
205+
#endif
206+
return vec;
207+
}
208+
209+
static pid_t PidFromPpidRecursive(pid_t parentProcId) {
210+
std::vector<pid_t> pidVec = PidFromPpid(parentProcId);
211+
if (pidVec.size()) {
212+
parentProcId = PidFromPpidRecursive(pidVec[0]);
213+
}
214+
return parentProcId;
215+
}
216+
217+
// set dialog transient; set title caption.
218+
static void *modify_shell_dialog(void *pid) {
151219
SetErrorHandlers();
152-
Window wid = wid_from_top(display);
153-
while (pid_from_wid(display, wid) != pid) {
154-
wid = wid_from_top(display);
220+
Display *display = XOpenDisplay(nullptr); Window wid;
221+
pid_t child = PidFromPpidRecursive((pid_t)(std::intptr_t)pid);
222+
while (true) {
223+
std::this_thread::sleep_for(std::chrono::milliseconds(5));
224+
wid = WidFromTop(display);
225+
if (PidFromWid(display, wid) == child) {
226+
break;
227+
}
155228
}
156-
XSetTransientForHint(display, wid, (Window)enigma_user::window_handle());
229+
XSetTransientForHint(display, wid, (Window)(std::intptr_t)enigma_user::window_handle());
230+
int len = enigma_user::message_get_caption().length() + 1; char *buffer = new char[len]();
231+
strcpy(buffer, enigma_user::message_get_caption().c_str()); XChangeProperty(display, wid,
232+
XInternAtom(display, "_NET_WM_NAME", false),
233+
XInternAtom(display, "UTF8_STRING", false),
234+
8, PropModeReplace, (unsigned char *)buffer, len);
235+
delete[] buffer; XCloseDisplay(display);
236+
return nullptr;
157237
}
158238

159239
bool widget_system_initialize() {
@@ -166,26 +246,14 @@ bool widget_system_initialize() {
166246
string create_shell_dialog(string command) {
167247
string output; char buffer[BUFSIZ];
168248
int outfp = 0, infp = 0; ssize_t nRead = 0;
169-
pid_t pid = process_execute(command.c_str(), &infp, &outfp);
170-
pid_t fpid = 0; if ((fpid = fork()) == 0) {
171-
SetErrorHandlers();
172-
Display *display = XOpenDisplay(nullptr);
173-
force_window_of_pid_to_be_transient(display, pid);
174-
XCloseDisplay(display);
175-
exit(0);
176-
}
249+
pid_t pid = ProcessCreate(command.c_str(), &infp, &outfp);
250+
std::this_thread::sleep_for(std::chrono::milliseconds(100)); pthread_t thread;
251+
pthread_create(&thread, nullptr, modify_shell_dialog, (void *)(std::intptr_t)pid);
177252
while ((nRead = read(outfp, buffer, BUFSIZ)) > 0) {
178253
buffer[nRead] = '\0';
179254
output.append(buffer, nRead);
180255
}
181-
kill(fpid, SIGTERM);
182-
bool died = false;
183-
for (unsigned i = 0; !died && i < 4; i++) {
184-
int status;
185-
std::this_thread::sleep_for(std::chrono::milliseconds(250));
186-
if (waitpid(fpid, &status, WNOHANG) == fpid) died = true;
187-
}
188-
if (!died) kill(fpid, SIGKILL);
256+
pthread_cancel(thread);
189257
while (output.back() == '\r' || output.back() == '\n')
190258
output.pop_back();
191259
return output;

ENIGMAsystem/SHELL/Widget_Systems/xlib/dialogs.h

+25-24
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@
1515
*** with this code. If not, see <http://www.gnu.org/licenses/>
1616
**/
1717

18+
#include <string>
19+
1820
#include "Widget_Systems/widgets_mandatory.h"
21+
1922
#include "Platforms/xlib/XLIBwindow.h"
20-
#include <string>
21-
using std::string;
2223

2324
namespace enigma {
2425

@@ -27,29 +28,29 @@ std::string create_shell_dialog(std::string command);
2728
class CommandLineWidgetEngine {
2829
public:
2930
virtual ~CommandLineWidgetEngine() = default;
30-
virtual void show_info(string info, int bgcolor, int left, int top, int width, int height, bool embedGameWindow, bool showBorder, bool allowResize, bool stayOnTop, bool pauseGame, string caption) = 0;
31-
virtual int show_message(const string &message) = 0;
32-
virtual int show_message_cancelable(string message) = 0;
33-
virtual bool show_question(string message) = 0;
34-
virtual int show_question_cancelable(string message) = 0;
35-
virtual int show_attempt(string errortext) = 0;
36-
virtual void show_debug_message(string errortext, MESSAGE_TYPE type) = 0;
37-
virtual string get_string(string message, string def) = 0;
38-
virtual string get_password(string message, string def) = 0;
39-
virtual double get_integer(string message, double def) = 0;
40-
virtual double get_passcode(string message, double def) = 0;
41-
virtual string get_open_filename(string filter, string fname) = 0;
42-
virtual string get_open_filenames(string filter, string fname) = 0;
43-
virtual string get_save_filename(string filter, string fname) = 0;
44-
virtual string get_open_filename_ext(string filter, string fname, string dir, string title) = 0;
45-
virtual string get_open_filenames_ext(string filter, string fname, string dir, string title) = 0;
46-
virtual string get_save_filename_ext(string filter, string fname, string dir, string title) = 0;
47-
virtual string get_directory(string dname) = 0;
48-
virtual string get_directory_alt(string capt, string root) = 0;
31+
virtual void show_info(std::string info, int bgcolor, int left, int top, int width, int height, bool embedGameWindow, bool showBorder, bool allowResize, bool stayOnTop, bool pauseGame, std::string caption) = 0;
32+
virtual int show_message(const std::string &message) = 0;
33+
virtual int show_message_cancelable(std::string message) = 0;
34+
virtual bool show_question(std::string message) = 0;
35+
virtual int show_question_cancelable(std::string message) = 0;
36+
virtual int show_attempt(std::string errortext) = 0;
37+
virtual void show_debug_message(std::string errortext, MESSAGE_TYPE type) = 0;
38+
virtual std::string get_string(std::string message, std::string def) = 0;
39+
virtual std::string get_password(std::string message, std::string def) = 0;
40+
virtual double get_integer(std::string message, double def) = 0;
41+
virtual double get_passcode(std::string message, double def) = 0;
42+
virtual std::string get_open_filename(std::string filter, std::string fname) = 0;
43+
virtual std::string get_open_filenames(std::string filter, std::string fname) = 0;
44+
virtual std::string get_save_filename(std::string filter, std::string fname) = 0;
45+
virtual std::string get_open_filename_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
46+
virtual std::string get_open_filenames_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
47+
virtual std::string get_save_filename_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
48+
virtual std::string get_directory(std::string dname) = 0;
49+
virtual std::string get_directory_alt(std::string capt, std::string root) = 0;
4950
virtual int get_color(int defcol) = 0;
50-
virtual int get_color_ext(int defcol, string title) = 0;
51-
virtual string message_get_caption() = 0;
52-
virtual void message_set_caption(string title) = 0;
51+
virtual int get_color_ext(int defcol, std::string title) = 0;
52+
virtual std::string message_get_caption() = 0;
53+
virtual void message_set_caption(std::string title) = 0;
5354

5455
}; // class CommandLineWidgetEngine
5556

ENIGMAsystem/SHELL/Widget_Systems/xlib/zenity.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ using enigma::create_shell_dialog;
6666
static string add_escaping(string str, bool is_caption, string new_caption) {
6767
string result = str; if (is_caption && str.empty()) result = new_caption;
6868
result = string_replace_all(result, "\"", "\\\""); // zenity needs this for quotes to show
69-
result = string_replace_all(result, "_", "__"); // zenity needs this for underscores to show
7069
return result;
7170
}
7271

0 commit comments

Comments
 (0)