Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add focus wait to Zenity/KDialog (#2198)
  • Loading branch information
time-killer-games committed Mar 29, 2021
1 parent 4a9b18f commit 3eb85d4
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 60 deletions.
2 changes: 1 addition & 1 deletion CI/solve_engine_deps.sh
Expand Up @@ -33,7 +33,7 @@ if [ "$WIDGETS" == "GTK+" ] || [ "$TEST_HARNESS" == true ]; then
LINUX_DEPS="$LINUX_DEPS libgtk2.0-dev"
fi
if [ "$WIDGETS" == "xlib" ] || [ "$TEST_HARNESS" == true ]; then
LINUX_DEPS="$LINUX_DEPS zenity kdialog"
LINUX_DEPS="$LINUX_DEPS zenity kdialog libprocps-dev"
fi

###### Extensions #######
Expand Down
2 changes: 1 addition & 1 deletion ENIGMAsystem/SHELL/Widget_Systems/xlib/Info/About.ey
Expand Up @@ -4,7 +4,7 @@
Name: X11 Widgets
Identifier: xlib
Build-Platforms: MacOSX, Linux, FreeBSD
Represents: Linux, FreeBSD
Represents: None
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
Author: Samuel Venable

Expand Down
5 changes: 5 additions & 0 deletions ENIGMAsystem/SHELL/Widget_Systems/xlib/Makefile
Expand Up @@ -2,3 +2,8 @@ SOURCES += $(wildcard Widget_Systems/xlib/*.cpp) Widget_Systems/General/WSdialog
override CXXFLAGS += $(shell pkg-config x11 --cflags)
override CFLAGS += $(shell pkg-config x11 --cflags)
override LDLIBS += $(shell pkg-config x11 --libs) -lz -lpthread
ifeq ($(OS), Linux)
override LDLIBS += -lprocps
else ifeq ($(OS), FreeBSD)
override LDLIBS += -lutil -lc
endif
134 changes: 101 additions & 33 deletions ENIGMAsystem/SHELL/Widget_Systems/xlib/dialogs.cpp
Expand Up @@ -23,28 +23,43 @@
#include <string>
#include <thread>
#include <chrono>
#include <vector>

#include "dialogs.h"

#include "../../../../CompilerSource/OS_Switchboard.h"

#include "Widget_Systems/widgets_mandatory.h"
#include "Widget_Systems/General/WSdialogs.h"

#include "Universal_System/estring.h"
#include "Platforms/General/PFwindow.h"

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include <pthread.h>
#include <libgen.h>
#include <unistd.h>
#include <fcntl.h>

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>

#if CURRENT_PLATFORM_ID == OS_MACOSX
#include <sys/proc_info.h>
#include <libproc.h>
#elif CURRENT_PLATFORM_ID == OS_LINUX
#include <proc/readproc.h>
#elif CURRENT_PLATFORM_ID == OS_FREEBSD
#include <sys/user.h>
#include <libutil.h>
#endif

using std::string;
using std::vector;

namespace enigma {

Expand Down Expand Up @@ -72,10 +87,10 @@ static bool kwin_running() {
return bKWinRunning;
}

static unsigned long get_wid_or_pid(Display *display, Window window, bool wid) {
static unsigned long GetActiveWidOrWindowPid(Display *display, Window window, bool wid) {
SetErrorHandlers();
unsigned char *prop;
unsigned long property;
unsigned long property = 0;
Atom actual_type, filter_atom;
int actual_format, status;
unsigned long nitems, bytes_after;
Expand All @@ -89,19 +104,19 @@ static unsigned long get_wid_or_pid(Display *display, Window window, bool wid) {
return property;
}

static Window wid_from_top(Display *display) {
static Window WidFromTop(Display *display) {
SetErrorHandlers();
int screen = XDefaultScreen(display);
Window window = RootWindow(display, screen);
return (Window)get_wid_or_pid(display, window, true);
return (Window)GetActiveWidOrWindowPid(display, window, true);
}

static pid_t pid_from_wid(Display *display, Window window) {
return (pid_t)get_wid_or_pid(display, window, false);
static pid_t PidFromWid(Display *display, Window window) {
return (pid_t)GetActiveWidOrWindowPid(display, window, false);
}

// create dialog process
static pid_t process_execute(const char *command, int *infp, int *outfp) {
static pid_t ProcessCreate(const char *command, int *infp, int *outfp) {
int p_stdin[2];
int p_stdout[2];
pid_t pid;
Expand Down Expand Up @@ -146,14 +161,79 @@ static pid_t process_execute(const char *command, int *infp, int *outfp) {
return pid;
}

// set dialog transient.
static void force_window_of_pid_to_be_transient(Display *display, pid_t pid) {
#if CURRENT_PLATFORM_ID == OS_MACOSX
static void PpidFromPid(pid_t procId, pid_t *parentProcId) {
proc_bsdinfo proc_info;
if (proc_pidinfo(procId, PROC_PIDTBSDINFO, 0, &proc_info, sizeof(proc_info)) > 0) {
*parentProcId = proc_info.pbi_ppid;
}
}
#endif

static std::vector<pid_t> PidFromPpid(pid_t parentProcId) {
std::vector<pid_t> vec;
#if CURRENT_PLATFORM_ID == OS_MACOSX
int cntp = proc_listpids(PROC_ALL_PIDS, 0, nullptr, 0);
std::vector<pid_t> proc_info(cntp);
std::fill(proc_info.begin(), proc_info.end(), 0);
proc_listpids(PROC_ALL_PIDS, 0, &proc_info[0], sizeof(pid_t) * cntp);
for (int j = cntp; j > 0; j--) {
if (proc_info[j] == 0) { continue; }
pid_t ppid; PpidFromPid(proc_info[j], &ppid);
if (ppid == parentProcId) {
vec.push_back(proc_info[j]);
}
}
#elif CURRENT_PLATFORM_ID == OS_LINUX
PROCTAB *proc = openproc(PROC_FILLSTAT);
while (proc_t *proc_info = readproc(proc, nullptr)) {
if (proc_info->ppid == parentProcId) {
vec.push_back(proc_info->tgid);
}
freeproc(proc_info);
}
closeproc(proc);
#elif CURRENT_PLATFORM_ID == OS_FREEBSD
int cntp; if (kinfo_proc *proc_info = kinfo_getallproc(&cntp)) {
for (int j = 0; j < cntp; j++) {
if (proc_info[j].ki_ppid == parentProcId) {
vec.push_back(proc_info[j].ki_pid);
}
}
free(proc_info);
}
#endif
return vec;
}

static pid_t PidFromPpidRecursive(pid_t parentProcId) {
std::vector<pid_t> pidVec = PidFromPpid(parentProcId);
if (pidVec.size()) {
parentProcId = PidFromPpidRecursive(pidVec[0]);
}
return parentProcId;
}

// set dialog transient; set title caption.
static void *modify_shell_dialog(void *pid) {
SetErrorHandlers();
Window wid = wid_from_top(display);
while (pid_from_wid(display, wid) != pid) {
wid = wid_from_top(display);
Display *display = XOpenDisplay(nullptr); Window wid;
pid_t child = PidFromPpidRecursive((pid_t)(std::intptr_t)pid);
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
wid = WidFromTop(display);
if (PidFromWid(display, wid) == child) {
break;
}
}
XSetTransientForHint(display, wid, (Window)enigma_user::window_handle());
XSetTransientForHint(display, wid, (Window)(std::intptr_t)enigma_user::window_handle());
int len = enigma_user::message_get_caption().length() + 1; char *buffer = new char[len]();
strcpy(buffer, enigma_user::message_get_caption().c_str()); XChangeProperty(display, wid,
XInternAtom(display, "_NET_WM_NAME", false),
XInternAtom(display, "UTF8_STRING", false),
8, PropModeReplace, (unsigned char *)buffer, len);
delete[] buffer; XCloseDisplay(display);
return nullptr;
}

bool widget_system_initialize() {
Expand All @@ -166,26 +246,14 @@ bool widget_system_initialize() {
string create_shell_dialog(string command) {
string output; char buffer[BUFSIZ];
int outfp = 0, infp = 0; ssize_t nRead = 0;
pid_t pid = process_execute(command.c_str(), &infp, &outfp);
pid_t fpid = 0; if ((fpid = fork()) == 0) {
SetErrorHandlers();
Display *display = XOpenDisplay(nullptr);
force_window_of_pid_to_be_transient(display, pid);
XCloseDisplay(display);
exit(0);
}
pid_t pid = ProcessCreate(command.c_str(), &infp, &outfp);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); pthread_t thread;
pthread_create(&thread, nullptr, modify_shell_dialog, (void *)(std::intptr_t)pid);
while ((nRead = read(outfp, buffer, BUFSIZ)) > 0) {
buffer[nRead] = '\0';
output.append(buffer, nRead);
}
kill(fpid, SIGTERM);
bool died = false;
for (unsigned i = 0; !died && i < 4; i++) {
int status;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
if (waitpid(fpid, &status, WNOHANG) == fpid) died = true;
}
if (!died) kill(fpid, SIGKILL);
pthread_cancel(thread);
while (output.back() == '\r' || output.back() == '\n')
output.pop_back();
return output;
Expand Down
49 changes: 25 additions & 24 deletions ENIGMAsystem/SHELL/Widget_Systems/xlib/dialogs.h
Expand Up @@ -15,10 +15,11 @@
*** with this code. If not, see <http://www.gnu.org/licenses/>
**/

#include <string>

#include "Widget_Systems/widgets_mandatory.h"

#include "Platforms/xlib/XLIBwindow.h"
#include <string>
using std::string;

namespace enigma {

Expand All @@ -27,29 +28,29 @@ std::string create_shell_dialog(std::string command);
class CommandLineWidgetEngine {
public:
virtual ~CommandLineWidgetEngine() = default;
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;
virtual int show_message(const string &message) = 0;
virtual int show_message_cancelable(string message) = 0;
virtual bool show_question(string message) = 0;
virtual int show_question_cancelable(string message) = 0;
virtual int show_attempt(string errortext) = 0;
virtual void show_debug_message(string errortext, MESSAGE_TYPE type) = 0;
virtual string get_string(string message, string def) = 0;
virtual string get_password(string message, string def) = 0;
virtual double get_integer(string message, double def) = 0;
virtual double get_passcode(string message, double def) = 0;
virtual string get_open_filename(string filter, string fname) = 0;
virtual string get_open_filenames(string filter, string fname) = 0;
virtual string get_save_filename(string filter, string fname) = 0;
virtual string get_open_filename_ext(string filter, string fname, string dir, string title) = 0;
virtual string get_open_filenames_ext(string filter, string fname, string dir, string title) = 0;
virtual string get_save_filename_ext(string filter, string fname, string dir, string title) = 0;
virtual string get_directory(string dname) = 0;
virtual string get_directory_alt(string capt, string root) = 0;
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;
virtual int show_message(const std::string &message) = 0;
virtual int show_message_cancelable(std::string message) = 0;
virtual bool show_question(std::string message) = 0;
virtual int show_question_cancelable(std::string message) = 0;
virtual int show_attempt(std::string errortext) = 0;
virtual void show_debug_message(std::string errortext, MESSAGE_TYPE type) = 0;
virtual std::string get_string(std::string message, std::string def) = 0;
virtual std::string get_password(std::string message, std::string def) = 0;
virtual double get_integer(std::string message, double def) = 0;
virtual double get_passcode(std::string message, double def) = 0;
virtual std::string get_open_filename(std::string filter, std::string fname) = 0;
virtual std::string get_open_filenames(std::string filter, std::string fname) = 0;
virtual std::string get_save_filename(std::string filter, std::string fname) = 0;
virtual std::string get_open_filename_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
virtual std::string get_open_filenames_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
virtual std::string get_save_filename_ext(std::string filter, std::string fname, std::string dir, std::string title) = 0;
virtual std::string get_directory(std::string dname) = 0;
virtual std::string get_directory_alt(std::string capt, std::string root) = 0;
virtual int get_color(int defcol) = 0;
virtual int get_color_ext(int defcol, string title) = 0;
virtual string message_get_caption() = 0;
virtual void message_set_caption(string title) = 0;
virtual int get_color_ext(int defcol, std::string title) = 0;
virtual std::string message_get_caption() = 0;
virtual void message_set_caption(std::string title) = 0;

}; // class CommandLineWidgetEngine

Expand Down
1 change: 0 additions & 1 deletion ENIGMAsystem/SHELL/Widget_Systems/xlib/zenity.cpp
Expand Up @@ -66,7 +66,6 @@ using enigma::create_shell_dialog;
static string add_escaping(string str, bool is_caption, string new_caption) {
string result = str; if (is_caption && str.empty()) result = new_caption;
result = string_replace_all(result, "\"", "\\\""); // zenity needs this for quotes to show
result = string_replace_all(result, "_", "__"); // zenity needs this for underscores to show
return result;
}

Expand Down

0 comments on commit 3eb85d4

Please sign in to comment.