diff --git a/src/ArgParser.cc b/src/ArgParser.cc index ded0e9e..062bff0 100644 --- a/src/ArgParser.cc +++ b/src/ArgParser.cc @@ -24,11 +24,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. bool ArgParser::parse (int argc, char ** argv) { - int count = 1; + std::vector argVec; + for (int i = 0; i < argc; i++) { + argVec.push_back(Glib::ustring(argv[i])); + } + + return this->parse(argVec); +} + +bool ArgParser::parse + (std::vector argVec) { bool retval = true; - while (count < argc) { - std::string arg (argv[count++]); + for (std::vector::const_iterator i = argVec.begin() + 1; i != argVec.end(); i++) { + std::string arg(*i); std::string key = arg; std::string value; diff --git a/src/ArgParser.h b/src/ArgParser.h index c6739ef..277f4c3 100644 --- a/src/ArgParser.h +++ b/src/ArgParser.h @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include #include +#include class Arg { public: @@ -120,6 +121,7 @@ class ArgParser { // parses away. bool parse (int argc, char ** argv); + bool parse (std::vector argVec); // returns the help text (--help) std::string help_text (void) const; diff --git a/src/Config.cc b/src/Config.cc index 018ddc5..b93690b 100644 --- a/src/Config.cc +++ b/src/Config.cc @@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "Config.h" #include #include "gcs-i18n.h" +#include "Util.h" /** * Constructor, initializes default settings. @@ -250,7 +251,7 @@ bool Config::set_bg(const Glib::ustring disp, const Glib::ustring file, const Se // set data g_key_file_set_string(kf, realdisp.c_str(), "file", file.c_str()); g_key_file_set_integer(kf, realdisp.c_str(), "mode", (gint)mode); - g_key_file_set_string(kf, realdisp.c_str(), "bgcolor", color_to_string(bgcolor).c_str()); + g_key_file_set_string(kf, realdisp.c_str(), "bgcolor", Util::color_to_string(bgcolor).c_str()); // output it Glib::ustring outp = g_key_file_to_data(kf, NULL, NULL); @@ -316,26 +317,6 @@ bool Config::check_dir() { return true; } -/** - * Converts a Gdk::Color to a string representation with a #. - * - * @param color The color to convert - * @return A hex string - */ -Glib::ustring Config::color_to_string(Gdk::Color color) { - guchar red = guchar(color.get_red_p() * 255); - guchar green = guchar(color.get_green_p() * 255); - guchar blue = guchar(color.get_blue_p() * 255); - - char * c_str = new char[7]; - - snprintf(c_str, 7, "%.2x%.2x%.2x", red, green, blue); - Glib::ustring string = '#' + Glib::ustring(c_str); - - delete[] c_str; - return string; -} - /** * Gets the last saved position. */ diff --git a/src/Config.h b/src/Config.h index a948ed0..b5b6e69 100644 --- a/src/Config.h +++ b/src/Config.h @@ -46,8 +46,6 @@ class Config { VecStrs m_vec_dirs; Thumbview::SortMode m_sort_mode; - Glib::ustring color_to_string(Gdk::Color color); - std::string get_bg_config_file() const { return get_file("bg-saved.cfg"); } std::string get_config_file() const { return get_file("nitrogen.cfg"); } std::string get_file(const Glib::ustring filename) const; diff --git a/src/NWindow.cc b/src/NWindow.cc index b7e69d6..57322c2 100644 --- a/src/NWindow.cc +++ b/src/NWindow.cc @@ -163,10 +163,9 @@ void NWindow::sighandle_dblclick_item (const Gtk::TreeModel::Path& path) { // find out which image was double clicked Gtk::TreeModel::iterator iter = (view.store)->get_iter(path); Gtk::TreeModel::Row row = *iter; - this->set_bg(row[view.record.Filename]); - // preview - set dirty flag - m_dirty = true; + // preview - set dirty flag, if setter says we should + m_dirty = this->set_bg(row[view.record.Filename]); } /** @@ -188,7 +187,7 @@ void NWindow::apply_bg () { Gtk::TreeModel::iterator iter = view.get_selected (); Gtk::TreeModel::Row row = *iter; Glib::ustring file = row[view.record.Filename]; - this->set_bg(file); + bool saveToConfig = this->set_bg(file); // apply - remove dirty flag m_dirty = false; @@ -198,7 +197,8 @@ void NWindow::apply_bg () { Gdk::Color bgcolor = this->button_bgcolor.get_color(); // save - Config::get_instance()->set_bg(thedisp, file, mode, bgcolor); + if (saveToConfig) + Config::get_instance()->set_bg(thedisp, file, mode, bgcolor); // tell the bg setter to forget about the first pixmap bg_setter->clear_first_pixmaps(); @@ -282,8 +282,10 @@ bool NWindow::on_delete_event(GdkEventAny *event) /** * Queries the necessary widgets to get the data needed to set a bg. * * @param file The file to set the bg to + * + * @returns If the dirty flag should be set or not */ -void NWindow::set_bg(const Glib::ustring file) { +bool NWindow::set_bg(const Glib::ustring file) { // get the data from the active items SetBG::SetMode mode = SetBG::string_to_mode(this->select_mode.get_active_data()); @@ -292,6 +294,8 @@ void NWindow::set_bg(const Glib::ustring file) { // set it bg_setter->set_bg(thedisp, file, mode, bgcolor); + + return bg_setter->save_to_config(); } // leethax destructor diff --git a/src/NWindow.h b/src/NWindow.h index 169f681..cfad9fb 100644 --- a/src/NWindow.h +++ b/src/NWindow.h @@ -43,7 +43,7 @@ class NWindow : public Gtk::Window { std::map map_displays; // a map of current displays on the running instance to their display names void set_default_display(int display); - void set_bg(Glib::ustring file); + bool set_bg(Glib::ustring file); protected: Glib::RefPtr m_action_group; diff --git a/src/SetBG.cc b/src/SetBG.cc index b231c27..fa8929d 100644 --- a/src/SetBG.cc +++ b/src/SetBG.cc @@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "Config.h" #include #include +#include using namespace Util; @@ -61,6 +62,9 @@ SetBG* SetBG::get_bg_setter() ((SetBGXinerama*)setter)->set_xinerama_info(xinerama_info, xinerama_num_screens); break; #endif + case SetBG::PCMANFM: + setter = new SetBGPcmanfm(); + break; case SetBG::DEFAULT: default: setter = new SetBGXWindows(); @@ -136,33 +140,30 @@ int SetBG::find_desktop_window(Display *xdisp, Window curwindow) { } /** - * Determines if Nautilus is being used to draw the root desktop. + * Finds any false desktop/root window, either via root window hint or recursively. * - * @returns True if nautilus is drawing the desktop. + * @returns The Window's ID, or 0. */ -SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr display) -{ +guint SetBG::get_root_window(Glib::RefPtr display) { GdkAtom type; gint format; gint length; guchar *data; - SetBG::RootWindowType retval = SetBG::DEFAULT; gboolean ret = FALSE; - Glib::RefPtr rootwin; - guint wid = 0; - - rootwin = display->get_default_screen()->get_root_window(); + Glib::RefPtr rootwin = display->get_default_screen()->get_root_window(); Display *xdisp = GDK_DISPLAY_XDISPLAY(rootwin->get_display()->gobj()); + guint wid = 0; - ret = gdk_property_get(rootwin->gobj(), - gdk_atom_intern("NAUTILUS_DESKTOP_WINDOW_ID", FALSE), - gdk_atom_intern("WINDOW", FALSE), - 0L, - 4L, /* length of a window is 32bits*/ - FALSE, &type, &format, &length, &data); + ret = gdk_property_get(rootwin->gobj(), + gdk_atom_intern("NAUTILUS_DESKTOP_WINDOW_ID", FALSE), + gdk_atom_intern("WINDOW", FALSE), + 0L, + 4L, /* length of a window is 32bits*/ + FALSE, &type, &format, &length, &data); if (ret) { wid = *(guint*)data; + g_free(data); } else { // newer nautilus and nemo don't leave a hint on the root window (whyyyy) // now we have to search for it! @@ -173,6 +174,30 @@ SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr displ wid = (guint)wwid; } + return wid; +} + +/** + * Determines if Nautilus is being used to draw the root desktop. + * + * @returns True if nautilus is drawing the desktop. + */ +SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr display) +{ + GdkAtom type; + gint format; + gint length; + guchar *data; + SetBG::RootWindowType retval = SetBG::DEFAULT; + gboolean ret = FALSE; + Glib::RefPtr rootwin; + guint wid = 0; + + rootwin = display->get_default_screen()->get_root_window(); + Display *xdisp = GDK_DISPLAY_XDISPLAY(rootwin->get_display()->gobj()); + + wid = get_root_window(display); + if (wid > 0) { Atom propatom = XInternAtom(xdisp, "WM_CLASS", FALSE); @@ -199,6 +224,7 @@ SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr displ if (strclass == std::string("Xfdesktop")) retval = SetBG::XFCE; else if (strclass == std::string("Nautilus")) retval = SetBG::NAUTILUS; else if (strclass == std::string("Nemo")) retval = SetBG::NEMO; else + if (strclass == std::string("Pcmanfm")) retval = SetBG::PCMANFM; else { std::cerr << _("UNKNOWN ROOT WINDOW TYPE DETECTED, will attempt to set via normal X procedure") << "\n"; retval = SetBG::UNKNOWN; @@ -211,10 +237,6 @@ SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr displ XFree(tprop.value); } - // need to free what we got from property get - if (ret) - g_free(data); - return retval; } @@ -227,6 +249,7 @@ SetBG::RootWindowType SetBG::get_rootwindowtype(Glib::RefPtr displ FALSE, &type, &format, &length, &data); if (ret) { + g_free(data); return SetBG::NAUTILUS; // mutter uses same keys } @@ -914,6 +937,16 @@ void SetBG::disable_pixmap_save() has_set_once = true; } +/** + * Returns if this background setter should be setting the Nitrogen configuration. + * + * Override this in alternate setters that may directly touch external configurations. + */ +bool SetBG::save_to_config() +{ + return true; +} + /* * ************************************************************************** * SetBGXwindows @@ -1208,6 +1241,16 @@ void SetBGGnome::set_show_desktop() settings->set_boolean("draw-background", true); } +/** + * Returns if this background setter should be setting the Nitrogen configuration. + * + * The Gnome mode is completely external. + */ +bool SetBGGnome::save_to_config() +{ + return false; +} + /* * ************************************************************************** * SetBGNemo @@ -1234,3 +1277,232 @@ void SetBGNemo::set_show_desktop() Glib::RefPtr settings = Gio::Settings::create(Glib::ustring("org.nemo.desktop")); settings->set_boolean("draw-background", true); } + +/** + * Returns if this background setter should be setting the Nitrogen configuration. + * + * The Nemo mode is completely external. + */ +bool SetBGNemo::save_to_config() +{ + return false; +} + +/* + * ************************************************************************** + * SetBGPcmanfm + * ************************************************************************** + */ + +bool SetBGPcmanfm::set_bg(Glib::ustring &disp, Glib::ustring file, SetMode mode, Gdk::Color bgcolor) +{ + Atom ret_type; + int ret_format; + gulong ret_items; + gulong ret_bytesleft; + guchar *data; + Glib::ustring strmode; + switch(mode) { + case SetBG::SET_SCALE: strmode = "stretch"; break; + case SetBG::SET_TILE: strmode = "tile"; break; + case SetBG::SET_CENTER: strmode = "center"; break; + case SetBG::SET_ZOOM: strmode = "fit"; break; + case SetBG::SET_ZOOM_FILL: strmode = "crop"; break; + default: strmode = "fit"; break; + }; + + // default to "LXDE" for profile name + Glib::ustring profileName = Glib::ustring("LXDE"); + + // find pcmanfm process (pull off root window) + Glib::RefPtr display = Gdk::DisplayManager::get()->get_default_display(); + Display* xdisp = GDK_DISPLAY_XDISPLAY(display->gobj()); + + guint wid = get_root_window(display); + + if (wid > 0) { + // pull PID atom from pcman desktop window + Window curwindow = (Window)wid; + + Atom propatom = XInternAtom(xdisp, "_NET_WM_PID", False); + + int result = XGetWindowProperty(xdisp, + curwindow, + propatom, + 0, G_MAXLONG, + False, XA_CARDINAL, &ret_type, &ret_format, &ret_items, &ret_bytesleft, &data); + + if (result != Success) { + std::cerr << "ERROR: Could not determine pid of Pcmanfm desktop window, is _NET_WM_PID set on X root window?\n"; + return false; + } + + long pid = 0; + + if (ret_type == XA_CARDINAL && ret_format == 32 && ret_items == 1) { + pid = ((long* )data)[0]; + XFree(data); + } + + if (pid > 0) { + + // attempt to pull profile name from pcmanfm process command line + std::ostringstream ss; + ss << pid; + + std::string filename = Glib::build_filename("/", "proc", ss.str(), "cmdline"); + if (Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + std::string contents = Glib::file_get_contents(filename); + + // contents is a string with null characters. glib doesn't seem to like splitting on the null character, + // so we have to do it manually. + std::vector splits; + + std::string::const_iterator i = contents.begin(); + while (i != contents.end()) { + std::string::const_iterator next = std::find(i, (std::string::const_iterator)contents.end(), '\0'); + splits.push_back(std::string(i, next)); + i = next + 1; + } + + // use our own ArgParse + ArgParser* parser = new ArgParser(); + parser->register_option("profile", "", true); + parser->register_option("desktop"); + + if (parser->parse(splits)) { + if (parser->has_argument("profile")) + profileName = parser->get_value("profile"); + else + // found pcmanfm exe, but no profile arg? use 'default' instead of LXDE + profileName = std::string("default"); + } + + // cleanup + delete parser; + } + + // find configuration file in profile directory (do a minimal create if needed) + // mimics logic in pcmanfm_get_profile_dir + std::string configdir = Glib::build_filename(Glib::get_user_config_dir(), "pcmanfm", profileName); + if (!Glib::file_test(configdir, Glib::FILE_TEST_EXISTS)) { + Glib::RefPtr giof = Gio::File::create_for_path(configdir); + giof->make_directory_with_parents(); + } + + // SPECIAL HANDLING: for "full screen", all configs must be set to the same bg and stretch across mode + if (disp == this->get_fullscreen_key()) { + + // iterate all screens + std::map map_displays = this->get_active_displays(); + for (std::map::const_iterator i = map_displays.begin(); i != map_displays.end(); i++) { + + // skip fullscreen key, there's no config file for that + if (i->first == disp) + continue; + + // read config file, set, save + std::string configfile = Glib::build_filename(Glib::get_user_config_dir(), "pcmanfm", profileName, Glib::ustring::compose("desktop-items-%1.conf", i->first)); + + Glib::KeyFile kf; + kf.load_from_file(configfile); + + kf.set_string("*", "wallpaper_mode", "screen"); + kf.set_string("*", "wallpaper_common", "1"); + kf.set_string("*", "wallpaper", file); + kf.set_string("*", "desktop_bg", Util::color_to_string(bgcolor)); + + if (kf.has_key("*", "wallpapers_configured")) + kf.remove_key("*", "wallpapers_configured"); + if (kf.has_key("*", "wallpaper0")) + kf.remove_key("*", "wallpaper0"); + + kf.save_to_file(configfile); + } + + } else { + // read config file, set, save + std::string configfile = Glib::build_filename(Glib::get_user_config_dir(), "pcmanfm", profileName, Glib::ustring::compose("desktop-items-%1.conf", disp)); + + Glib::KeyFile kf; + kf.load_from_file(configfile); + + kf.set_string("*", "wallpaper_mode", strmode); + kf.set_string("*", "wallpaper_common", "0"); + kf.set_string("*", "wallpapers_configured", "1"); + kf.set_string("*", "wallpaper0", file); + kf.set_string("*", "desktop_bg", Util::color_to_string(bgcolor)); + + if (kf.has_key("*", "wallpaper")) + kf.remove_key("*", "wallpaper"); + + kf.save_to_file(configfile); + } + + // send USR1 to pcmanfm + kill(pid, SIGUSR1); + } else { + std::cerr << "ERROR: _NET_WM_PID set on X root window, but pid not readable.\n"; + return false; + } + } + + return true; +} + +/** + * Gets all active screens on this display. + * This is used by the main window to determine what to show in the dropdown, + * if anything. + * + * Returns a map of display string to human-readable representation. + */ +std::map SetBGPcmanfm::get_active_displays() +{ + Glib::RefPtr disp = Gdk::DisplayManager::get()->get_default_display(); + std::map map_displays; + + map_displays[this->get_fullscreen_key()] = _("Full Screen"); + + for (int i=0; i < disp->get_n_screens(); i++) { + Glib::RefPtr screen = disp->get_screen(i); + + for (int j=0; j < screen->get_n_monitors(); j++) { + std::ostringstream ostr; + ostr << _("Screen") << " " << j; + + map_displays[this->make_display_key(j)] = ostr.str(); + } + } + + return map_displays; +} + +/** + * Gets the full key for "full screen" for this setter. + * + * For Pcmanfm, simply "-1". + */ +Glib::ustring SetBGPcmanfm::get_fullscreen_key() { + return this->make_display_key(-1); +} + +/* + * Make a usable display key to pass to set_bg with a given head number. + * + * For Pcmanfm, return simply the head number. + */ +Glib::ustring SetBGPcmanfm::make_display_key(gint head) { + return Glib::ustring::compose("%1", head); +} + +/** + * Returns if this background setter should be setting the Nitrogen configuration. + * + * The Pcmanfm mode is completely external. + */ +bool SetBGPcmanfm::save_to_config() +{ + return false; +} + diff --git a/src/SetBG.h b/src/SetBG.h index e0ce085..8c55788 100644 --- a/src/SetBG.h +++ b/src/SetBG.h @@ -57,6 +57,7 @@ class SetBG { UNKNOWN, XINERAMA, NEMO, + PCMANFM, }; virtual bool set_bg(Glib::ustring &disp, @@ -82,6 +83,8 @@ class SetBG { void reset_first_pixmaps(); void disable_pixmap_save(); + virtual bool save_to_config(); + protected: virtual Glib::ustring get_prefix() = 0; @@ -97,6 +100,7 @@ class SetBG { static int handle_x_errors(Display *display, XErrorEvent *error); static int find_desktop_window(Display *display, Window curwindow); + static guint get_root_window(Glib::RefPtr display); Glib::RefPtr make_resized_pixbuf(Glib::RefPtr pixbuf, SetBG::SetMode mode, Gdk::Color bgcolor, gint tarw, gint tarh); virtual Glib::RefPtr get_display(const Glib::ustring& disp); @@ -153,6 +157,7 @@ class SetBGGnome : public SetBG { virtual std::map get_active_displays(); virtual Glib::ustring get_fullscreen_key(); + virtual bool save_to_config(); protected: virtual Glib::ustring get_prefix(); virtual Glib::ustring make_display_key(gint head); @@ -161,9 +166,25 @@ class SetBGGnome : public SetBG { }; class SetBGNemo : public SetBGGnome { + public: + virtual bool save_to_config(); protected: virtual Glib::ustring get_gsettings_key(); virtual void set_show_desktop(); }; +class SetBGPcmanfm : public SetBGGnome { + public: + virtual Glib::ustring get_fullscreen_key(); + virtual bool set_bg(Glib::ustring &disp, + Glib::ustring file, + SetMode mode, + Gdk::Color bgcolor); + + virtual std::map get_active_displays(); + virtual bool save_to_config(); + protected: + virtual Glib::ustring make_display_key(gint head); +}; + #endif diff --git a/src/Util.cc b/src/Util.cc index 99e8d91..9d289de 100644 --- a/src/Util.cc +++ b/src/Util.cc @@ -97,7 +97,7 @@ ArgParser* create_arg_parser() { parser->register_option("sort", _("How to sort the backgrounds. Valid options are:\n\t\t\t* alpha, for alphanumeric sort\n\t\t\t* ralpha, for reverse alphanumeric sort\n\t\t\t* time, for last modified time sort (oldest first)\n\t\t\t* rtime, for reverse last modified time sort (newest first)"), true); parser->register_option("set-color", _("background color in hex, #000000 by default"), true); parser->register_option("head", _("Select xinerama/multihead display in GUI, 0..n, -1 for full"), true); - parser->register_option("force-setter", _("Force setter engine: xwindows, xinerama, gnome"), true); + parser->register_option("force-setter", _("Force setter engine: xwindows, xinerama, gnome, pcmanfm"), true); parser->register_option("random", _("Choose random background from config or given directory")); // command line set modes @@ -297,4 +297,24 @@ Glib::ustring make_current_set_string(Gtk::Window* window, Glib::ustring filenam return ostr.str(); } +/** + * Converts a Gdk::Color to a string representation with a #. + * + * @param color The color to convert + * @return A hex string + */ +Glib::ustring color_to_string(Gdk::Color color) { + guchar red = guchar(color.get_red_p() * 255); + guchar green = guchar(color.get_green_p() * 255); + guchar blue = guchar(color.get_blue_p() * 255); + + char * c_str = new char[7]; + + snprintf(c_str, 7, "%.2x%.2x%.2x", red, green, blue); + Glib::ustring string = '#' + Glib::ustring(c_str); + + delete[] c_str; + return string; +} + } diff --git a/src/Util.h b/src/Util.h index b45ecaa..a8165da 100644 --- a/src/Util.h +++ b/src/Util.h @@ -40,6 +40,8 @@ namespace Util { bool is_display_relevant(Gtk::Window* window, Glib::ustring display); Glib::ustring make_current_set_string(Gtk::Window* window, Glib::ustring filename, Glib::ustring display); + + Glib::ustring color_to_string(Gdk::Color color); } #endif diff --git a/src/main.cc b/src/main.cc index 496c620..32cc618 100644 --- a/src/main.cc +++ b/src/main.cc @@ -72,9 +72,11 @@ int set_bg_once(Config *cfg, SetBG* bg_setter, Glib::ustring path, int head, Set bg_setter->disable_pixmap_save(); disp = bg_setter->make_display_key(head); - bg_setter->set_bg(disp, file, mode, col); + bool shouldSave = bg_setter->set_bg(disp, file, mode, col); + + if (save && shouldSave) + Config::get_instance()->set_bg(disp, file, mode, col); - if (save) Config::get_instance()->set_bg(disp, file, mode, col); while (Gtk::Main::events_pending()) Gtk::Main::iteration(); @@ -156,6 +158,8 @@ int main (int argc, char ** argv) { } else if (setter_str == "gnome") setter = new SetBGGnome(); + else if (setter_str == "pcmanfm") + setter = new SetBGPcmanfm(); else setter = SetBG::get_bg_setter();