mirror of
https://github.com/ashaduri/gsmartcontrol.git
synced 2026-09-27 06:15:45 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
460ed687ee | ||
|
|
b8de0d933b |
@@ -805,94 +805,6 @@ bool GscInfoWindow::on_delete_event([[maybe_unused]] GdkEventAny* e)
|
||||
|
||||
|
||||
|
||||
bool GscInfoWindow::on_key_press_event(GdkEventKey* event)
|
||||
{
|
||||
// Handle Ctrl+Tab and Ctrl+Shift+Tab for tab navigation
|
||||
if ( (event->state & GDK_CONTROL_MASK)
|
||||
&& (event->keyval == GDK_KEY_Tab || event->keyval == GDK_KEY_ISO_Left_Tab)) {
|
||||
|
||||
// Helper to cycle only across visible notebook pages
|
||||
auto cycle_visible_pages = [&](Gtk::Notebook* notebook) -> bool {
|
||||
if (!notebook) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int n_pages = notebook->get_n_pages();
|
||||
std::vector<int> visible_pages;
|
||||
visible_pages.reserve(n_pages);
|
||||
|
||||
for (int i = 0; i < n_pages; ++i) {
|
||||
if (auto* page = notebook->get_nth_page(i)) {
|
||||
if (page->get_visible()) {
|
||||
visible_pages.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need at least two visible pages to make cycling meaningful
|
||||
if (visible_pages.size() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int current_page = notebook->get_current_page();
|
||||
int visible_index = 0;
|
||||
|
||||
auto it = std::find(visible_pages.begin(), visible_pages.end(), current_page);
|
||||
if (it != visible_pages.end()) {
|
||||
visible_index = static_cast<int>(std::distance(visible_pages.begin(), it));
|
||||
}
|
||||
|
||||
int next_visible_index = 0;
|
||||
|
||||
// Check if Shift is also pressed for backward navigation
|
||||
if (event->state & GDK_SHIFT_MASK) {
|
||||
// Ctrl+Shift+Tab: go to previous visible tab
|
||||
next_visible_index =
|
||||
(visible_index - 1 + static_cast<int>(visible_pages.size())) %
|
||||
static_cast<int>(visible_pages.size());
|
||||
} else {
|
||||
// Ctrl+Tab: go to next visible tab
|
||||
next_visible_index =
|
||||
(visible_index + 1) %
|
||||
static_cast<int>(visible_pages.size());
|
||||
}
|
||||
|
||||
notebook->set_current_page(visible_pages[next_visible_index]);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto* main_notebook = lookup_widget<Gtk::Notebook*>("main_notebook");
|
||||
if (main_notebook) {
|
||||
// Check if we're on the Advanced tab with sub-tabs
|
||||
auto* advanced_tab_vbox = lookup_widget<Gtk::Box*>("advanced_tab_vbox");
|
||||
auto* advanced_notebook = lookup_widget<Gtk::Notebook*>("advanced_notebook");
|
||||
|
||||
// Only handle sub-tab cycling if the Advanced tab is currently active
|
||||
if (advanced_tab_vbox && advanced_notebook && advanced_notebook->get_visible()) {
|
||||
const int advanced_page_num = main_notebook->page_num(*advanced_tab_vbox);
|
||||
const int current_main_page = main_notebook->get_current_page();
|
||||
|
||||
// Advanced tab is active, so cycle through its sub-tabs
|
||||
if (advanced_page_num >= 0 && advanced_page_num == current_main_page) {
|
||||
if (cycle_visible_pages(advanced_notebook)) {
|
||||
return true; // event handled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, cycle through main notebook tabs
|
||||
if (cycle_visible_pages(main_notebook)) {
|
||||
return true; // event handled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call base class handler for other keys
|
||||
return Gtk::Window::on_key_press_event(event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GscInfoWindow::on_refresh_info_button_clicked()
|
||||
{
|
||||
this->refresh_info();
|
||||
|
||||
@@ -217,10 +217,6 @@ class GscInfoWindow : public AppBuilderWidget<GscInfoWindow, true> {
|
||||
/// Reimplemented from Gtk::Window.
|
||||
bool on_delete_event(GdkEventAny* e) override;
|
||||
|
||||
/// Handle key press events for keyboard navigation.
|
||||
/// Reimplemented from Gtk::Window.
|
||||
bool on_key_press_event(GdkEventKey* event) override;
|
||||
|
||||
|
||||
// ---------- Other callbacks
|
||||
|
||||
|
||||
+116
-3
@@ -21,6 +21,11 @@ Copyright:
|
||||
#include "win32_tools.h" // hz::win32_utf8_to_utf16
|
||||
#else
|
||||
#include <memory>
|
||||
#include <unistd.h> // geteuid, fork, execvp, setuid, setgid
|
||||
#include <sys/types.h> // uid_t, gid_t
|
||||
#include <sys/wait.h> // waitpid
|
||||
#include <pwd.h> // getpwuid
|
||||
#include "env_tools.h" // hz::env_get_value
|
||||
#endif
|
||||
|
||||
|
||||
@@ -29,6 +34,89 @@ Copyright:
|
||||
namespace hz {
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/// Launch URL as the original user when running as root.
|
||||
/// This is needed because gtk_show_uri_on_window() doesn't work when running as root
|
||||
/// (D-Bus session is not accessible).
|
||||
/// \return error message on error, empty string on success.
|
||||
inline std::string launch_url_as_original_user(const std::string& link)
|
||||
{
|
||||
// Get the original user's UID from environment variables
|
||||
// SUDO_UID is set by sudo, PKEXEC_UID is set by pkexec
|
||||
std::string uid_str;
|
||||
uid_t original_uid = 0;
|
||||
gid_t original_gid = 0;
|
||||
|
||||
if (hz::env_get_value("SUDO_UID", uid_str) || hz::env_get_value("PKEXEC_UID", uid_str)) {
|
||||
try {
|
||||
original_uid = static_cast<uid_t>(std::stoul(uid_str));
|
||||
} catch (...) {
|
||||
return "Cannot parse original user UID";
|
||||
}
|
||||
|
||||
// Get the original user's GID
|
||||
struct passwd* pw = getpwuid(original_uid);
|
||||
if (pw) {
|
||||
original_gid = pw->pw_gid;
|
||||
} else {
|
||||
return "Cannot get original user information";
|
||||
}
|
||||
} else {
|
||||
return "Cannot determine original user UID";
|
||||
}
|
||||
|
||||
// Fork and execute xdg-open as the original user
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
return "Cannot fork process";
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// Child process
|
||||
|
||||
// Restore HOME environment variable if available
|
||||
// This helps xdg-open find the correct configuration
|
||||
std::string sudo_user;
|
||||
if (hz::env_get_value("SUDO_USER", sudo_user)) {
|
||||
struct passwd* pw = getpwnam(sudo_user.c_str());
|
||||
if (pw && pw->pw_dir) {
|
||||
setenv("HOME", pw->pw_dir, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Drop privileges to original user
|
||||
// Set GID first, then UID (order matters for security)
|
||||
if (setgid(original_gid) != 0) {
|
||||
_exit(1);
|
||||
}
|
||||
if (setuid(original_uid) != 0) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Execute xdg-open with the URL
|
||||
const char* argv[] = {"xdg-open", link.c_str(), nullptr};
|
||||
execvp("xdg-open", const_cast<char* const*>(argv));
|
||||
|
||||
// If execvp returns, it failed
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Parent process - wait for child
|
||||
int status = 0;
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
return "Cannot wait for child process";
|
||||
}
|
||||
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||
return {}; // Success
|
||||
}
|
||||
|
||||
return "xdg-open failed to launch URL";
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
/// Open URL in browser or mailto: link in mail client.
|
||||
/// Return error message on error, empty string otherwise.
|
||||
@@ -50,16 +138,41 @@ inline std::string launch_url([[maybe_unused]] GtkWindow* window, const std::str
|
||||
#else
|
||||
|
||||
GError* error = nullptr;
|
||||
bool status = false;
|
||||
|
||||
// Check if running as root
|
||||
bool is_root = (geteuid() == 0);
|
||||
|
||||
// If running as root, try to launch as the original user first
|
||||
if (is_root) {
|
||||
std::string result = launch_url_as_original_user(link);
|
||||
if (result.empty()) {
|
||||
return {}; // Success
|
||||
}
|
||||
// If launching as original user failed, fall through to try GTK method
|
||||
}
|
||||
|
||||
// Try the standard GTK method
|
||||
#if GTK_CHECK_VERSION(3, 22, 0)
|
||||
bool status = static_cast<bool>(gtk_show_uri_on_window(window, link.c_str(), GDK_CURRENT_TIME, &error));
|
||||
status = static_cast<bool>(gtk_show_uri_on_window(window, link.c_str(), GDK_CURRENT_TIME, &error));
|
||||
#else
|
||||
GdkScreen* screen = (window ? gtk_window_get_screen(window) : nullptr);
|
||||
bool status = static_cast<bool>(gtk_show_uri(screen, link.c_str(), GDK_CURRENT_TIME, &error));
|
||||
status = static_cast<bool>(gtk_show_uri(screen, link.c_str(), GDK_CURRENT_TIME, &error));
|
||||
#endif
|
||||
std::unique_ptr<GError, decltype(&g_error_free)> uerror(error, &g_error_free);
|
||||
|
||||
if (!status) {
|
||||
return std::string("Cannot open URL: ")
|
||||
// GTK method failed. If running as root, we already tried the fallback.
|
||||
// Otherwise, try the fallback now.
|
||||
if (!is_root) {
|
||||
std::string result = launch_url_as_original_user(link);
|
||||
if (result.empty()) {
|
||||
return {}; // Success
|
||||
}
|
||||
}
|
||||
|
||||
// Both methods failed, return error
|
||||
return std::string("Cannot open URL")
|
||||
+ ((error && error->message) ? (std::string(": ") + error->message) : ".");
|
||||
}
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user