.gear/rules | 2 + .gear/tags/list | 1 + meson.build | 9 +++ proto/wayfire-shell.xml | 130 ++++++++++++++++++++-------------------- src/background/background.cpp | 5 +- src/dock/dock-app.cpp | 2 + src/dock/dock.cpp | 2 +- src/dock/toplevel-icon.cpp | 58 ++++++++++++++++-- src/dock/toplevel-icon.hpp | 8 +++ src/panel/panel.cpp | 2 +- src/panel/widgets/launchers.cpp | 9 +-- src/util/gtk-utils.cpp | 12 ++++ src/util/gtk-utils.hpp | 3 + src/util/wf-autohide-window.cpp | 5 +- src/util/wf-autohide-window.hpp | 2 +- src/util/wf-shell-app.cpp | 13 +++- wf-shell.ini.example | 4 ++ wf-shell.spec | 44 ++++++++++++++ 18 files changed, 225 insertions(+), 86 deletions(-) diff --git a/.gear/rules b/.gear/rules new file mode 100644 index 0000000..4fcfd99 --- /dev/null +++ b/.gear/rules @@ -0,0 +1,2 @@ +tar: @version@:. +diff: @version@:. . diff --git a/.gear/tags/list b/.gear/tags/list new file mode 100644 index 0000000..91a3d88 --- /dev/null +++ b/.gear/tags/list @@ -0,0 +1 @@ +4cb058da549e425edf71c534298682bb5b30ce24 0.1 diff --git a/meson.build b/meson.build index 344aba0..35a41df 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,7 @@ project( 'c_std=c11', 'warning_level=2', 'werror=false', + 'sysconfdir=/etc', ], ) @@ -18,10 +19,18 @@ wayland_protos = dependency('wayland-protocols') gtkmm = dependency('gtkmm-3.0') wfconfig = dependency('wf-config') #TODO fallback submodule +prefixdir=get_option('prefix') +sysconfdir=join_paths(prefixdir, get_option('sysconfdir')) +conf_data = configuration_data() +conf_data.set('SYSCONFDIR', get_option('sysconfdir')) +install_data('wf-shell.ini.example', rename: 'wf-shell.ini', + install_dir: join_paths(sysconfdir, 'wf-shell')) + add_project_arguments(['-Wno-pedantic', '-Wno-unused-parameter', '-Wno-parentheses', '-Wno-cast-function-type'], language: 'cpp') icon_dir = join_paths(get_option('prefix'), 'share', 'wayfire', 'icons') add_global_arguments('-DICONDIR="' + icon_dir + '"', language : 'cpp') +add_global_arguments('-DSYSCONFDIR="' + get_option('sysconfdir') + '"', language : 'cpp') subdir('proto') subdir('data') diff --git a/proto/wayfire-shell.xml b/proto/wayfire-shell.xml index 6709e28..d1d4f82 100644 --- a/proto/wayfire-shell.xml +++ b/proto/wayfire-shell.xml @@ -18,6 +18,65 @@ + + + + Assign the given role to the given surface and add it to the + given output. A client can specify a null output, in which case + the compositor will assign the surface to the focused output, + if any such output. + + The role cannot be changed later, and neither can the surface be + moved to a different output, except by the compositor. + + + + + + + + + + + + Represents a single output. + Each output is managed independently from the others. + + + + Panels are always rendered on top, even above fullscreen windows. + If autohide is 1, the event indicates that the panels should hide + itself, by for example unmapping or sliding outside of the output. + If autohide is 0, this means that the reason for the last request + with autohide == 1 is no longer valid, i.e the panels can show + themselves. + + The output_hide_panels can be called multiple times with + autohide = 1, and the panel should show itself only when + it has received a matching number of events with autohide = 0 + + + + + + + + Request the compositor to not render the output, so + the output usually is cleared to black color. + To enable output rendering again, call inhibit_output_done + + + + + + Stop inhibiting the output. This must be called as many times + as inhibit_output was called to actually uninhibit rendering. + + The inhibit/inhibit_done requests can be called multiple times, + even from different apps, so don't assume that a call to + inhibit_done would always mean actually starting the rendering process. + + @@ -26,6 +85,13 @@ It belongs to the output which it was created for. + + + + + + + @@ -99,68 +165,4 @@ - - - - Represents a single output. - Each output is managed independently from the others. - - - - - - - - - - - - Assign the given role to the given surface - and add it to the current output. - - The role cannot be changed later, and neither - can it be moved to a different output. - - - - - - - - - - Panels are always rendered on top, even above fullscreen windows. - If autohide is 1, the event indicates that the panels should hide - itself, by for example unmapping or sliding outside of the output. - If autohide is 0, this means that the reason for the last request - with autohide == 1 is no longer valid, i.e the panels can show - themselves. - - The output_hide_panels can be called multiple times with - autohide = 1, and the panel should show itself only when - it has received a matching number of events with autohide = 0 - - - - - - - - Request the compositor to not render the output, so - the output usually is cleared to black color. - To enable output rendering again, call inhibit_output_done - - - - - - Stop inhibiting the output. This must be called as many times - as inhibit_output was called to actually uninhibit rendering. - - The inhibit/inhibit_done requests can be called multiple times, - even from different apps, so don't assume that a call to - inhibit_done would always mean actually starting the rendering process. - - - diff --git a/src/background/background.cpp b/src/background/background.cpp index 782f5bf..ea60183 100644 --- a/src/background/background.cpp +++ b/src/background/background.cpp @@ -32,8 +32,9 @@ class WayfireBackground std::exit(-1); } - wm_surface = zwf_output_v1_get_wm_surface(output->zwf, surface, - ZWF_OUTPUT_V1_WM_ROLE_BACKGROUND); + wm_surface = zwf_shell_manager_v1_get_wm_surface( + output->display->zwf_shell_manager, surface, + ZWF_WM_SURFACE_V1_ROLE_BACKGROUND, output->handle); zwf_wm_surface_v1_configure(wm_surface, 0, 0); } diff --git a/src/dock/dock-app.cpp b/src/dock/dock-app.cpp index fef6c5b..17f9429 100644 --- a/src/dock/dock-app.cpp +++ b/src/dock/dock-app.cpp @@ -1,5 +1,6 @@ #include "dock.hpp" #include "toplevel.hpp" +#include "toplevel-icon.hpp" #include #include @@ -46,6 +47,7 @@ class WfDockApp::impl : public WayfireShellApp void on_activate() override { WayfireShellApp::on_activate(); + IconProvider::load_custom_icons(this->config->get_section("dock")); /* At this point, wayland connection has been initialized, * and hopefully outputs have been created */ diff --git a/src/dock/dock.cpp b/src/dock/dock.cpp index 4cdd6fa..40a2796 100644 --- a/src/dock/dock.cpp +++ b/src/dock/dock.cpp @@ -27,7 +27,7 @@ class WfDock::impl this->output = output; window = std::unique_ptr ( new WayfireAutohidingWindow(100, 100, output, - ZWF_OUTPUT_V1_WM_ROLE_PANEL)); + ZWF_WM_SURFACE_V1_ROLE_PANEL)); window->set_keyboard_mode(ZWF_WM_SURFACE_V1_KEYBOARD_FOCUS_MODE_NO_FOCUS); window->increase_autohide(); diff --git a/src/dock/toplevel-icon.cpp b/src/dock/toplevel-icon.cpp index 28cb1a4..63c6b79 100644 --- a/src/dock/toplevel-icon.cpp +++ b/src/dock/toplevel-icon.cpp @@ -119,7 +119,15 @@ class WfToplevelIcon::impl void set_state(uint32_t state) { + bool was_activated = this->state & WF_TOPLEVEL_STATE_ACTIVATED; this->state = state; + bool is_activated = this->state & WF_TOPLEVEL_STATE_ACTIVATED; + + if (!was_activated && is_activated) { + this->button.get_style_context()->remove_class("flat"); + } else if (was_activated && !is_activated) { + this->button.get_style_context()->add_class("flat"); + } } ~impl() @@ -151,6 +159,34 @@ namespace IconProvider c = std::tolower(c); return str; } + + std::map custom_icons; + } + + void load_custom_icons(wayfire_config_section *section) + { + static const std::string prefix = "icon_mapping_"; + for (auto option : section->options) + { + if (option->name.compare(0, prefix.length(), prefix) != 0) + continue; + + auto app_id = option->name.substr(prefix.length()); + custom_icons[app_id] = option->as_string(); + } + } + + bool set_custom_icon(Gtk::Image& image, std::string app_id, int size, int scale) + { + if (!custom_icons.count(app_id)) + return false; + + auto pb = load_icon_pixbuf_safe(custom_icons[app_id], size * scale); + if (!pb.get()) + return false; + + set_image_pixbuf(image, pb, scale); + return true; } /* Gio::DesktopAppInfo @@ -201,26 +237,32 @@ namespace IconProvider return Icon{}; } - /* Second method: Just look up the built-in icon theme, - * perhaps some icon can be found there */ - void set_image_from_icon(Gtk::Image& image, std::string app_id_list, int size, int scale) { std::string app_id; std::istringstream stream(app_id_list); + bool found_icon = false; + /* Wayfire sends a list of app-id's in space separated format, other compositors * send a single app-id, but in any case this works fine */ while (stream >> app_id) { + /* Try first method: custom icon file provided by the user */ + if (set_custom_icon(image, app_id, size, scale)) + { + found_icon = true; + break; + } + + /* Then try to load the DesktopAppInfo */ auto icon = get_from_desktop_app_info(app_id); std::string icon_name = "unknown"; if (!icon) { - /* Perhaps no desktop app info, but we might still be able to - * get an icon directly from the icon theme */ + /* Finally try directly looking up the icon, if it exists */ if (Gtk::IconTheme::get_default()->lookup_icon(app_id, 24)) icon_name = app_id; } else @@ -234,7 +276,13 @@ namespace IconProvider /* finally found some icon */ if (icon_name != "unknown") + { + found_icon = true; break; + } } + + if (!found_icon) + std::cout << "Failed to load icon for any of " << app_id_list << std::endl; } }; diff --git a/src/dock/toplevel-icon.hpp b/src/dock/toplevel-icon.hpp index 25b5f03..186aa01 100644 --- a/src/dock/toplevel-icon.hpp +++ b/src/dock/toplevel-icon.hpp @@ -2,6 +2,7 @@ #define WF_DOCK_TOPLEVEL_ICON_HPP #include "display.hpp" +#include "config.hpp" #include #include @@ -19,4 +20,11 @@ class WfToplevelIcon std::unique_ptr pimpl; }; +namespace IconProvider +{ + /* Loads custom app_id -> icon file mappings from the section + * They have the format icon_mapping_ = */ + void load_custom_icons(wayfire_config_section *section); +} + #endif /* end of include guard: WF_DOCK_TOPLEVEL_ICON_HPP */ diff --git a/src/panel/panel.cpp b/src/panel/panel.cpp index 5ffd4d3..9d92f57 100644 --- a/src/panel/panel.cpp +++ b/src/panel/panel.cpp @@ -132,7 +132,7 @@ class WayfirePanel this->current_output_width, minimal_panel_height->as_int(), this->output, - ZWF_OUTPUT_V1_WM_ROLE_OVERLAY)); + ZWF_WM_SURFACE_V1_ROLE_OVERLAY)); bg_color = config_section->get_option("background_color", "gtk_headerbar"); bg_color->add_updated_handler(&on_window_color_updated); diff --git a/src/panel/widgets/launchers.cpp b/src/panel/widgets/launchers.cpp index 641da1b..9d7d985 100644 --- a/src/panel/widgets/launchers.cpp +++ b/src/panel/widgets/launchers.cpp @@ -68,14 +68,7 @@ struct FileLauncherInfo : public LauncherInfo command = name; this->icon = icon; - try { - // check if file is loadable - Gdk::Pixbuf::create_from_file(icon, 24, 24); - } catch(...) { - return false; - } - - return true; + return load_icon_pixbuf_safe(icon, 24).get() != nullptr; } Glib::RefPtr get_pixbuf(int32_t size) diff --git a/src/util/gtk-utils.cpp b/src/util/gtk-utils.cpp index 3d947c6..248a4a6 100644 --- a/src/util/gtk-utils.cpp +++ b/src/util/gtk-utils.cpp @@ -3,6 +3,18 @@ #include #include +Glib::RefPtr load_icon_pixbuf_safe(std::string icon_path, int size) +{ + try + { + auto pb = Gdk::Pixbuf::create_from_file(icon_path, size, size); + return pb; + } catch(...) + { + return {}; + } +} + void invert_pixbuf(Glib::RefPtr& pbuff) { int channels = pbuff->get_n_channels(); diff --git a/src/util/gtk-utils.hpp b/src/util/gtk-utils.hpp index ecfb448..0878957 100644 --- a/src/util/gtk-utils.hpp +++ b/src/util/gtk-utils.hpp @@ -4,6 +4,9 @@ #include #include +/* Loads a pixbuf with the given size from the given file, returns null if unsuccessful */ +Glib::RefPtr load_icon_pixbuf_safe(std::string icon_path, int size); + struct WfIconLoadOptions { int user_scale = -1; diff --git a/src/util/wf-autohide-window.cpp b/src/util/wf-autohide-window.cpp index 2c61874..417da30 100644 --- a/src/util/wf-autohide-window.cpp +++ b/src/util/wf-autohide-window.cpp @@ -5,7 +5,7 @@ #include WayfireAutohidingWindow::WayfireAutohidingWindow(int width, int height, - WayfireOutput *output, zwf_output_v1_wm_role role) + WayfireOutput *output, zwf_wm_surface_v1_role role) { this->set_size_request(width, height); this->set_decorated(false); @@ -22,7 +22,8 @@ WayfireAutohidingWindow::WayfireAutohidingWindow(int width, int height, std::exit(-1); } - wm_surface = zwf_output_v1_get_wm_surface(output->zwf, surface, role); + wm_surface = zwf_shell_manager_v1_get_wm_surface( + output->display->zwf_shell_manager, surface, role, output->handle); this->m_position_changed = [=] () {this->update_position();}; this->signal_draw().connect_notify( diff --git a/src/util/wf-autohide-window.hpp b/src/util/wf-autohide-window.hpp index d683dc8..218bb04 100644 --- a/src/util/wf-autohide-window.hpp +++ b/src/util/wf-autohide-window.hpp @@ -42,7 +42,7 @@ class WayfireAutohidingWindow : public Gtk::Window public: WayfireAutohidingWindow(int width, int height, - WayfireOutput *output, zwf_output_v1_wm_role role); + WayfireOutput *output, zwf_wm_surface_v1_role role); zwf_wm_surface_v1* get_wm_surface() const; /* Sets the edge of the screen where the window is */ diff --git a/src/util/wf-shell-app.cpp b/src/util/wf-shell-app.cpp index 948cc23..85e31aa 100644 --- a/src/util/wf-shell-app.cpp +++ b/src/util/wf-shell-app.cpp @@ -6,8 +6,17 @@ std::string WayfireShellApp::get_config_file() { - std::string home_dir = getenv("HOME"); - std::string config_file = home_dir + "/.config/wf-shell.ini"; + const char *config_basename = "wf-shell.ini"; + + std::string config_dir = getenv("XDG_CONFIG_DIR") ? : + (std::string(getenv("HOME") ? : "") + "./config"); + std::string config_file = config_dir + "/" + config_basename; + + /* use system-wide config file if local is not accessible */ + /* FIXME: there can be a race */ + if (access(config_file.c_str(), F_OK) == -1) + std::string(SYSCONFDIR "/wf-shell/") + config_basename; + return config_file; } diff --git a/wf-shell.ini.example b/wf-shell.ini.example index 0d2b778..5a1c3ad 100644 --- a/wf-shell.ini.example +++ b/wf-shell.ini.example @@ -103,3 +103,7 @@ menu_fuzzy_search = 1 [dock] position = bottom # or top + +# For applications that aren't installed/configured properly, you can manually +# set icons for given app_id's. Below is an example for IntelliJ IDEA +icon_mapping_jetbrains-idea-ce = //idea.png diff --git a/wf-shell.spec b/wf-shell.spec new file mode 100644 index 0000000..f4f18bd --- /dev/null +++ b/wf-shell.spec @@ -0,0 +1,44 @@ +Name: wf-shell +Version: 0.1 +Release: alt1 + +Summary: A panel for the Wayfire compositor + +License: MIT +Group: Graphical desktop/Other +Url: https://github.com/WayfireWM/wf-shell + +Source: %name-%version.tar +Patch1: %name-%version-%release.patch + +# Automatically added by buildreq on Sun Mar 17 2019 +# optimized out: at-spi2-atk fontconfig fontconfig-devel glib2-devel glibc-kernheaders-generic glibc-kernheaders-x86 libat-spi2-core libatk-devel libatkmm-devel libcairo-devel libcairo-gobject libcairo-gobject-devel libcairomm-devel libfreetype-devel libgdk-pixbuf libgdk-pixbuf-devel libgio-devel libglibmm-devel libgpg-error libgtk+3-devel libpango-devel libpangomm-devel libsigc++2-devel libstdc++-devel libwayland-client libwayland-client-devel libwayland-cursor libwayland-egl libwf-config ninja-build pkg-config python-base python-modules python3 python3-base python3-module-pkg_resources sh4 wayland-devel xz +BuildRequires: gcc-c++ libgtkmm3-devel libwf-config-devel meson wayland-protocols + +%description +%summary. + +%prep +%setup +%patch1 -p1 + +%build +%meson +%meson_build + +%install +%meson_install + +%check +%meson_test + +%files +%config(noreplace) %_sysconfdir/%name/%name.ini +%_bindir/* +%_datadir/wayfire + +%changelog +* Fri Mar 15 2019 Vladimir D. Seleznev 0.1-alt1 +- Initial build for Sisyphus. + +