diff options
author | Ulf <ulf.hofemeier@intel.com> | 2012-10-02 08:58:25 -0700 |
---|---|---|
committer | Ulf <ulf.hofemeier@intel.com> | 2012-10-02 08:58:25 -0700 |
commit | 3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c (patch) | |
tree | 1c148feadf76d107051bf15da3b7dbf1409cdb28 | |
download | openbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.tar.gz openbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.tar.bz2 openbox-3aa94bb5874991ba92ffe52b384f5fb3ddf53a1c.zip |
Initial import of openbox.submit/trunk/20121002.160201
380 files changed, 78331 insertions, 0 deletions
@@ -0,0 +1,24 @@ +Openbox authors/contributors: + +Mikael Magnusson (mikachu@comhem.se) + - Developer +Dana Jansens (danakj@orodu.net) + - Lead developer +Derek Foreman (manmower@openbox.org) + - Rendering code +Tore Anderson (tore@linpro.no) + - Directional focus, edge moving/growing actions +Audun Hove (audun@nlc.no) + - Actions code, move window to edge actions +Marius Nita (marius@cs.pdx.edu) + - Otk prototype code, design ideas. +John McKnight (jmcknight@gmail.com) + - Clearlooks themes +David Barr (david@chalkskeletons.com) + - Bear2 theme + - Clearlooks theme + - Icon +Brandon Cash + - SplitVertical gradient style +Dan Rise + - Natura theme diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..9bd1021 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,1204 @@ +3.5.0: + * New alt-tab dialog shows windows in a vertical list. + * Improved Xinerama support. + * Allow icons in menus. + * Theme options for prompt dialogs (osd.button.unpressed.*, + osd.button.pressed.*, osd.button.focused.*) + * Addresses bug #4877, #4596, #4617, #4752, #4663, #4662, #4586, #2319, + #4341, #4519, #4543, #4503, #4355, #4072, #3702, #4284 + * Lots of additional bug fixes and performance improvements. + +3.4.11.2: + * Updated Estonian and Portuguese translations. + * Fix a rare crash involving moving fullscreen windows to different monitors + * Fix a more common crash involving pressing right in a menu + +3.4.11.1: + * Updated Polish translation. + * Fixed bug #4519 (Incorrect focus in reused windows). + * Lower the default submenu open/hide delay from 200ms to 100ms. + * Fix some more problems with gnome integration. + * Update Clearlooks theme. + * Some other small fixes. + +3.4.11: + * Update Hungarian, Japanese, and Latvian translations. + * Make xdg-autostart use the OPENBOX environment by default, so you can use + OnlyShowIn=OPENBOX in an autostart .desktop and it will work as expected. + * Don't close the menu when you hold control and execute something. + * Fix bug #4503 (Adjust who shows up in the Alt-Tab list using SKIP_TASKBAR). + * Fix flickering window when moving maximized window between monitors of + different sizes. + * Fix bug #4355 (Allow multiple escaped _'s in a menu label and allow + a menu shortcut to come after an escaped _). + * Remember the maximized state of a window when it goes fullscreen, and + restore it when leaving fullscreen state. + * Fix bug #4072 (Openbox is stopped by terminal applications writing to + stdout). + * Fix bug #4492 (Mistake in openbox-gnome-session check while setting up). + * Fix obxprop to make --root and --id work correctly. + * Add _OB_APP_ROLE/CLASS/NAME/TYPE properties (replaces _OB_ROLE/CLASS/NAME). + * Make the focus cycling popup dynamic when windows appear/disappear. + * Fix bug #4411 (Crash when window appears during focus cycling). + * Allow the user to specify which properties should be shown by obxprop. + * Fix tilde expansion in the Execute action + * Make Home and End keys move to the top/bottom of the active menu. + * Use the submenuShowDelay when navigating menus with the keyboard. + +3.4.10: + * Improve keyboard navigation in Openbox menus. + * Add a --root option and a manpage for obxprop. + * Use a negative value for submenuShowDelay and submenuHideDelay to cause + an infinite delay. This means you have to click to show a submenu, rather + than just hover over it. + * Improved code for submenu show/hide delay. Added the submenuHideDelay + config file option, under the "menu" section. + * Fixed bug #4464 (Typo in openbox-gnome-session script). + * Fixed bug #4436 (Focusing a window used to stop focus cycling). + * Renamed obprop to obxprop due to collision with Open Babel (See bug #4419). + +3.4.9: + * Allow focus to move while inside an Openbox menu, or during an interactive + action such as window cycling. + * Fixed bug #3717 (Empty dock interfered with move/grow to edge actions). + * Fixed bug #4411 (Crash when switching desktops and window cycling). + * Fixed bug #4377 (Window resistance against struts). + * Fixed bug #4035 (Prevent focus from moving under the mouse after + activating a window with an Openbox menu. + * Correct the value provided by the _NET_WORKAREA hint, so desktop icons + will place across all monitors. + * Don't hide submenus immediately when moving through the parent menu. + (Resolves request #3762). + * Fix for showing Openbox menus with multiple monitors, don't restrict them + to the monitor where the mouse is. + * Fixed bug #4023 (Allow the user to have multiple keys which perform the + same function in Openbox menus/move/resize. E.g. two keys which are + both bound to Escape. + * Add a new obprop tool, which can be used to read the _OB_ROLE, _OB_NAME, + and _OB_CLASS (as well as any other UTF-8 window properties) off of a + window. + * Add _OB_ROLE, _OB_NAME, and _OB_CLASS hints on each window that show the + respective values for use in the rc.xml applications section, to modify + the window when it appears. + * Improve Openbox interoperability with gnome-session >= 2.24. + * Fixed bug #4040 (Remove desktop hints set by gdm in the openbox-session + scripts, so that Openbox can set the number of desktops (assuming + gnome-settings-daemon doesn't first)). + * Fix a bug in xdg-autostart preventing some .desktop files from working. + * Show the desktop pager popup on the primary monitor instead of on all + monitors. + * Add a new primaryMonitor config option, which is where Openbox popups + will appear. Defaults to a fixed monitor, but can be made to behave as + before with the "active" value for it. + * Correct edge detection for move/grow to edge to properly use monitor edges + for multi-monitor setups. + * Change default window placement policy to stay on the active monitor for + multi-monitor setups. + * Fixed bug #1149 (Crash with some window icon sizes). + * Respond to all strut changes, fixes moving/hiding panels. + * Fix internal code to focus windows on other desktops correctly (Fixes + bug #4413). + * Focus correctness fixups for switching desktops. + * Fixed bug #4373 (Decoration bug for shaded maximized windows). + * Fixed bug #4350 (Allow a window to be made skip_taskbar but still get + focused by the user's rc.xml). + * Fixed bug #4307 (Set a minimum time for screenEdgeWarpTime). + * Fixed bug #4253 (Support for Solaris in openbox-session scripts). + * Fixed bug #3851 (Allow transient windows to be above helper windows). + +3.4.8: + * Updated translations: Slovak. + * Allow windows to change their decorations at any time (Fix for + Google Chrome). + * Make openbox-session to respect the $XDG_CONFIG_HOME environment variable. + * Fixed bug #4344 where borders were given to windows that should not have + them. + * Merge the SessionLogout and Exit actions. They now test if connected to a + session manager and ask it to exit if so, or simply kill Openbox if not. + * Further tweaks to the _NET_ACTIVE_WINDOW message handling. Use the same + logic for focus-stealing as is used when mapping a new window. + * Don't go out of our way any more to prevent focus from moving while the + keyboard is grabbed. + * Fix openbox-gnome-session when using gnome-session > 2.22. + +3.4.8-rc2: + * Updated translations: Italian, Croatian, Ukrainian. + * When resizing a window while focus cycling with bar=no, the bar no longer + reappears. + * Correctly handle shaped windows using the ShapeInput kind, this is used + by many composited apps to pass through clicks in their transparent areas. + * Fix the <monitor> per-app setting. + * Avoid using anonymous unions. + * Windows that had their decorations removed by per-app settings were still + placed as if they still had their decorations. + * Fix event handling not to ignore events on a window when they have an unmap + event in the queue, if that unmap event doesn't cause the window to be + unmanaged. + * Show the desktop switch on every monitor in xinerama. + * Fix interpretation of struts in xinerama where the screens have different + sizes. + * Add "next" and "prev" as possible <monitor> targets in the moveto and + resizeto actions. + * Allow escaping the _ used to mark the shortcut character in menu labels. + You can now change the (first) _ in a label to __, this will be displayed + as a single underscore. The rest of the _ in the string will be unaffected, + so only double the first one. + * Only replace ~ with the home directory when it is preceded by whitespace or + is at the start of the string, and when it is followed by a space, slash, or + the end of the string. This is implemented with GRegex, and so the required + glib version has been bumped to 2.14. + * Some other small fixes. + +3.4.8-rc1: + * Updated translations: Basque, Catalan, Turkish, Italian, Spanish, Russian. + * New translations: Danish, Turkish, Lithuanian. + * Set the _MOTIF_WM_INFO atom so urxvt uses motif hints for borderless mode. + * Properly escape the xml used in session files. + * Correct a 64-bit issue related to comparing timestamps. + * There is a sneaky sentence right at the end of a big paragraph in the + wm-spec document that says windows mapping with _NET_WM_USER_TIME=0 should + not be focused initially, honor this request. + * When moving a window to another desktop with following on, bring the + window's helper windows (for example gimp image windows with the toolbox + set to utility window). + * Change the _NET_ACTIVE_WINDOW messages again, if they originate from the + app and the window is on another desktop, just set the demands_attention + flag. If the event came from the user (ie pager/panel), then the window + is still moved to the current desktop. + +3.4.7.2: + * The system I used to generate the dist tarball didn't have the + docbook-to-man command so the manpages were empty. + +3.4.7.1: + * Not to be outdone by the cairo team, I introduced a bug in the last release + which made resizing not give any feedback. This is now fixed. + +3.4.7: + * Fully updated Czech, Simplified Chinese, Traditional Chinese, German, + French, Hungarian, Norwegian, Vietnamese, Dutch, Swedish, Finnish, + Brazilian Portuguese, Japanese and Portuguese translations + * Partially updated Spanish translation + * Add an example of the "force" option for the per-app placement options to + the default rc.xml file + * Add a new xdg-autostart script. This will eventually end up in the PyXDG + distribution hopefully, but it is included in Openbox for now. This script + runs things based on the freedesktop.org autostart specification. You can + have it run GNOME, KDE, ROX, or XFCE specific things if you want. The + new default system-wide autostart.sh script runs it automatically + * Update the default menu.xml to include a lot of common apps + * Fix white font shadows (negative shadowtint) + * Update the autostart.sh to find gnome-settings-daemon correctly, as the + GNOME people have moved it to libexec + * Fix focus possibly getting stolen when using the Focus action + * Drastically speed up rendering of Vertical and SplitVertical gradients + * Speed improvements also for Horizonal and Pyramid gradients + * Add new theme options, menu.overlap.x and menu.overlap.y options, that let + you independently control the horizontal and vertical position of submenus + * Change _NET_ACTIVE_WINDOW messages to not change the current desktop, but + to bring the window to the current desktop instead. This is the industry + standard policy + * Use the pretty new openbox.png icon as the default window icon + * Allow matching per-application rules to windows by their window type + (normal, dialog, splash, etc). The default rc.xml has more details + * Add new Openbox-themed prompt windows. Use these prompt windows to ask + before killing off windows that aren't responding. This also means we + don't need to ping every window constantly forever + * Add a new <prompt> option to the Execute action. If this is set to a + string, a dialog will be shown with that string in it and "yes"/"no" + buttons. The command to be executed will only be run if the user selects + "yes" in the dialog + * Add a new <prompt> option to the Exit action, which is a boolean (not a + string). When true, Openbox will show a dialog confirming if you want to + exit. The default is to show the prompt + * Reduce Openbox's memory footprint and speed up rendering through the use + of a new icon cache, so that Openbox only needs to keep 1 copy of an icon + when 100 different windows share it + * Make Openbox menus have the "menu type" hint for compositors to see and use + * Fix the MoveResizeTo action for negative coords (opposite edges) + * Fix key bindings getting lost if multiple bindings at the same level could + not be translated (Fixes VMWare causing Openbox keybindings to stop + working) + * Fix the resize popup box for terminal windows with a base size of 0 (show + the right size values for urxvt terminals) + * Fix some off-by-one bugs with the edge growing/shrinking code + * Add new theme options for menu line separators: menu.separator.color, + menu.separator.width, menu.separator.padding.width, + menu.separator.padding.height + * Add xfce-mcs-manager to the default autostart.sh, and run it automatically + when gnome-settings-daemon is not present to have GTK apps inherit settings + from the XFCE configuration tools + * Make the send-to-desktop menu in the client-menu indicate which is the current + desktop for omnipresent windows, and don't close it if just toggling + omni-presence when ctrl-clicking + * Add a new SessionLogout action that logs out through the session manager, + when running Openbox within a session manager such as within an + GNOME/Openbox or KDE/Openbox session. The action includes a <prompt> + option which is similar to the Exit action's + * Add a new gdm-control command that lets you control gdm from within an X + session. The gdm-control lets you change GDM's behaviour for when you end + the current session. For instance, you can tell GDM to reboot, and + then immediately log out of the current session, and the computer will be + rebooted + * Show an information dialog when an error occurs for Openbox, such as when + the Execute action fails or when XML syntax errors are present in the + configuration files + * When making a window very narrow, don't draw buttons to the right of the title + on top of the ones on its left. + +3.4.6.1: + * Updated Clearlooks theme + * Add the force="yes/no" option for the per-app settings' <position> tag + * Raise and focus modal children and their direct parents together, improved + usability with direct modal transient windows + * Fix crash when using <raise> for NextWindow and there are no windows + to move focus to + * Add the <manageDesktops> option in the rc.xml <menu> section, which toggles + the "Manage Desktops" section appearing in the combined-client-list-menu + * Fix for menu headers showing the wrong text + * Fix for the <focusLast> behavior + * Treat modal direct children as one window with their parent consistently + +3.4.6: + * Added Basque translation + * Updated French, Vietnamese German, Simplified Chinese, Russian, Portuguese, + Brasilian Portuguese, Norwegian, and Finnish translations + * New Clearlooks theme, updated by David Barr + * Updated the previous Clearlooks theme, and renamed it to Clearlooks-3.4 + * Allow dialog type windows to go fullscreen (Fixes Kpdf) + * Remove the extraneous top border for undecorated windows while maximized + * Fixes for keyboard modifiers (Alt-tab dialog getting stuck on screen for + some users) + * Automatically catch changes to the keyboard map and reconfigure the key + bindings on the fly + * Fix focus moving incorrectly sometimes with focus under mouse enabled + * Make default configuration focus the desktop when you right click + * Add the <bar> and <raise> options for all window cycling actions, allowing + you to have your target window temporarily raised above all others, and to + turn the focus target indicator bar off + * Improve the LastDesktop action to not remember desktops you skipped across + * Ignore mouse clicks that are made on override-redirect (unmanaged) windows + * When opening a menu with a key binding, don't use the key binding to run + something in the menu + * Add a <monitor> option for window placement, which gives you the option + to place new windows on the active monitor, or the monitor where the mouse + is, instead of on any monitor (for xinerama multihead setups) + * Add options for placing the window move/resize coordinate popup window in + a fixed position on screen, rather than relative to the window being + moved or resized + * Prevent the dock from auto-hiding completely offscreen if the theme has + no borders for it + * New icon + * Fix race condition when running things that want to grab the keyboard + (e.g. gnome-panel-control --main-menu) + * When dialog windows ask to not appear in the taskbar, still give them focus + in normal ways (fixes new GNOME session logout dialogs) + * Fix bug with resizing corners on certain parts of the window frame + * Ping applications to tell when they are running or have become frozen. + Show a [Not Responding] message in the title bar of windows which are + frozen. + * When closing a window which is [Not Responding], kill the window's process + if it is running on the same machine as Openbox. Otherwise, just + disconnect + the window from the X display. A second attempt to close a [Not + Responding] + window will kill it forcefully (kill -9). + * Fixes for internal timers + * Add a <wmclass> option for the execute action's startup-notification. This + lets you tell Openbox that the application will map a window with the + specified class - for applications that do not support startup-notification + natively. + * Fix for empty dock taking up space onscreen after a reconfigure + * Reduce Openbox's additional memory footprint per-window and per-menu + * Faster horizontal gradient rendering + * Don't deiconify windows that aren't allowed to be directly iconified on + restart (eg toolbars), as they can be iconified by other means + * Improve support for fullscreen windows in xinerama (TwinView) and + multiple-screen setups + * Add a --config-file command line option, to specify an alternate + configuration file path + +3.4.5: + * Added Hungarian translation + * Updated Finnish, Russian, German and French translations + * Fixed some very minor memory leaks + * Hide the desktop popup when showing the focus popup + * Fix a crash when trying to access the More... menu of + client-list-combined-menu + * Fix the coordinate popup only showing up on the first monitor in xinerama + * Add --exit to exit the currently running openbox instance + +3.4.4: + * Updated Traditional Chinese translation + * Updated Norwegian translation + * Fix for MoveToEdge skipping edges sometimes + * Let you specify the vertical and horizontal padding independently in themes + * Fix so that if you select an omnipresent window on another desktop in the + client list menus, it will go to that desktop + * Make the GrowToEdge action shrink once there is no more room to grow, + similar to in 3.4.2, but shrinking to edges as well + * Move the Send To and Layers submenus to the top of the client menu + * Fix race causing omnipresent windows to lose focus when switching desktops + very quickly + * Don't focus new windows on other desktops if they aren't related to the + currently focused window + * Add corner resizing areas in the inner client border (Fixes themes such + as Onyx) + * New focus stealing prevention that is smart and not intrusive and not + annoying + * Revert a small change in 3.4.3 that caused windows to be placed funny in + Smart placement when there was a dock or something on the side of the + screen + * Show a notification when switching desktops + * Fix for delayed focus-follows-mouse interrupting move/resize or menus + * Make screen edge warp keep warping without having the move the mouse 1 + pixel + * Fix for resizing terminals in the top/left sides getting a little confused + * Fix to keep oldschool (Non-EWMH) fullscreen windows from being moved and + resized inside of the struts (Fixes Acroread) + * Accept the <command> option for the Restart command, similar to the Execute + action + * Don't make clicking on dock apps sometimes act like clicking on the root + window (Don't propogate button events up) + * Fix a bug introduced in 3.4.3 which caused the Windows key as a modifier + for bindings to not work properly + * Let windows skip across monitors in a Xinerama setup when using MoveToEdge + or Shift-arrow in an interactive move + * Make move and resize take the dock into account for resistance + * Raise new windows when it makes sense, when they aren't being focused + * Change default config to use click events for mouse wheel bindings instead + of presses + +3.4.3: + * Add Ukrainian translation + * Updated translations: + - Japanese, Dutch, Polish, Italian, Estonian, German, Portuguese, + Vietnamese, Finnish, Czech, Arabic, Spanish, Swedish, French + * Improve focus fallback - don't fallback to windows you can't focus cycle + (Alt-Tab) to + * Don't show the client border for undecorated windows when keepBorder is on, + just the outer border + * Some improvements in the smart placement to help it find a place more + often. This needs more work still. + * Don't focus windows that appear under the mouse in mouse-focus mode unless + underMouse is enabled + * Don't move focus when the window under the pointer iconifies unless + underMouse is enabled + * Respect min/max window sizes when a window is fullscreened or maximized. + This fixes problems experienced with maximizing vnc windows + * Don't XUngrabKeyboard unless we need to + * Correct calculations for the menu header width, so that they are not + ellisized unnecessarily + * Advanced support for partial struts. When you maximize a window it will + now only use the struts it needs to. This can greatly improve + xinerama functionality. + * Fixes for how the dock is placed in certain positions on screen (off by 1 + errors) + * New default bindings: + - don't unshade when clicking on the titlebar + - raise when unshading with the scrollwheel + - lower when shading with the scrollwheel + * Don't show the top resize area in the titlebar when a window is shaded + * Optimize rendering of window decorations + * Optimize splitvertical gradients - no more malloc/free during render + * Let the theme specify all colors for a splitvertical gradient with the new + .splitTo theme elements + * Improve decorations for maximized windows. Don't draw the side borders in + the titlebar. + * Don't resist against desklet windows (below layer + skip taskbar) + * New actions for dynamically adjusting your desktop workspaces: + - AddDesktopLast, RemoveDesktopLast + - AddDesktopCurrent, RemoveDestopCurrent + * New <center> option for smart placement (default is on) + * Fix MoveToCenter in Xinerama (TwinView) setups + * Let you lower a window without lowering its children or siblings + * Don't set the default Openbox icon on child windows, let them inherit the + icon from their parents + * Fix Onyx themes - fade out disabled buttons + * Don't auto-resize windows to fit on screen if they are UserSpecified + Size or Position + * Don't activate windows which raise themselves anymore. It was a nice + thought, but it caused too much problems + * Rewrite of the actions subsystem + * Removed deprecated actions: + - ShowDesktop, UnshowDesktop (Use ToggleShowDesktop) + - ResizeRelativeVert, ResizeRelativeHorz (Use ResizeRelative) + * Combined the MoveFromEdge* actions with MoveToEdge* actions, and removed + the MoveFromEdge* action names. + * Switch desktops when moving a window and you drag it to the edge of the + screen (added the <screenEdgeWarpTime> option for this) + * Add actions to add/remove the last desktop in the client list menus + * Use startup notification when launching ObConf in the default root menu + * Remove the desktop switching dialog, as it confuses people and they don't + know how to turn it off to switch desktops instantly. Some other + incarnation may return in the future. + * Add optional <finalactions> option for NextWindow, PreviousWindow, + DirectionalFocus*, and DirectionalTarget* actions + * Don't focus new helper windows unless they are alone in their group. This + fixes gucharmap + * Fancy keyboard window moving: + - Shift-arrow will move the window to the next edge + - Control-arrow will move the window by only 1 pixel + - Arrow will move the window by a larger number of pixels + * Fancy keyboard window resizing: + - The first arrow will choose an edge to resize (then you can grow or + shrink the window on that edge) + - Hitting an arrow perpendicular to your selected edge will choose a + different edge to resize + - Shift-arrow will grow/shrink the window to the next edge + - Control arrow will grow/shrink the window by only 1 pixel + - Arrow will grow/shrink the window by a larger number of pixel + * Timeout when synchronously resizing a window so you can still resize busy + or dead application windows + * Let you specify keyboard shortcuts in your root menus with _. Such as + label="XTe_rm" would make 'r' the shortcut key. + * Fixes for placing windows with per-app settings: + - you can now place windows relative to any edge of the screen instead of + only the top left. (-0 uses the far edge, +0 uses the near edge. You can + use --0 and +-0 to specify negative numbers.) + - fix bug with windows being placed far off-screen occasionally + - allow you to place windows partially outside of the screen + - let per-app settings override program-specified positions (but not if it + is user-specified) + * Add user-specified margins to the configuration file + * Fix for transient windows in groups with transient windows of the group + transients. That wasn't supposed to be legible. i.e. 2 group transients + and 1 direct transient for one of the group transients. + * Don't include child/parent windows in tests for being occluded or occluding + other windows, since you can't raise/lower above/below them. This fixes + the raiselower action for windows which have children. + * Reduce the size of the middle gradient in splitvertical gradients. This + makes the split harder than it was in 3.4.2 + * Don't include debian/ dir in releases + * Add resize bindings for the edges to the default hardcoded bindings + * Fix focus cycle indicator positioning for some windows (i.e. OpenOffice) + * Add the MoveResizeTo action. This is very xinerama aware! It also lets + you move a window to any edge of any screen. + * Make actions that look for edges include the dock's edges (e.g. MoveToEdge) + * New preferred option for the Execute action is <command> instead of + <execute> but the old one will still work. + * Fix for moving windows partially offsceen causing them to jump far away + * Fix for windows which are not on any monitor, to keep them on screen + partically too + * Require libstartupnotification 0.8 in configure (we use stuff that was not + around in some earlier versions) + * Grab the server when mapping windows to avoid race conditions in other + programs (getting MapNotify before you are visible) + * Handle case gracefully when ConfigureRequent for restacking specifies + a siblig which we aren't managing + * Use KeyRelease to run things from Openbox menus, so that the release + does not go to the focused window (causing the new window to not get + focus) + * Add new actions for controlling window decorations: + - Decorate, Undecorate + * Fix so that the values for per-app settings are not case sensitive + * Fix crash with very small windows + * Fix resize resistance for windows with aspect ratio limits + * Let you resize windows with aspect ratio limits in the vertical directions + +3.4.2: + * Add ability to set the bevel strength in themes + * Fix for focusLast with omnipresent windows + * Cache pipe menus while the menus are open + * Allow non-interactive directional focus key bindings + * Change how nested interactive key bindings work (it stays in the key chain, + like chroot, until you end the interactive action) + * Fix for gtk apps trying to get focus - mostly nullify focus stealing + prevention (Fixes Tilda) + * Send ConfigureNotify always on ConfigureRequest (Previous emacs fix + makes this possible now) + * Fix RelativeResize action for right/bottom edges + * Remove SCIM from the default autostart.sh - it has caused problems with + some (buggy) panels, such as pypanel + * Fix the titlebar changing to non-focused when iconifying a window with + animation + * Use the new osd theme hints for the dock, rather than the titlebar hints + * Fix rare crash when menu is hidden while browsing it + * End move/resize more cleanly when focus moves during a move/resize + * Don't focus new windows when browsing a menu or moving/resizing a window, + just flash the window instead, so that the menu/move/resize doesn't have to + cancel. + +3.4.1: + * Add Vietnamese translation + * Add Japanese translation + * Keep new transient windows from not getting focus when any related window + has focus already + * Change behavior of underMouse option - make focus go under the mouse when + no window has focus anymore, rather than to the last used window + * Make on-screen display dialogs (such as the Alt-Tab dialog) appear on the + active monitor when using Xinerama (a.k.a. TwinView) + * Fix for window stacking - let you lower sibling transient windows behind + eachother + * Fix directional focus actions when used without a dialog + * Fix race that could prevent ObConf from setting the desktop names correctly + * Add new theme options: + - window.active.button.toggled.image.color has been split into: + - window.active.button.toggled.unpressed.image.color + - window.active.button.toggled.pressed.image.color + - window.active.button.toggled.hover.image.color + - window.inactive.button.toggled.image.color has been split into: + - window.inactive.button.toggled.unpressed.image.color + - window.inactive.button.toggled.pressed.image.color + - window.inactive.button.toggled.hover.image.color + - window.active.button.toggled.bg has been split into: + - window.active.button.toggled.unpressed.bg + - window.active.button.toggled.pressed.bg + - window.active.button.toggled.hover.bg + - window.inactive.button.toggled.bg has been split into: + - window.inactive.button.toggled.unpressed.bg + - window.inactive.button.toggled.pressed.bg + - window.inactive.button.toggled.hover.bg + * Add new Onyx and Onyx-Citrus themes + * Updated Mikachu theme using new theme options + * Improvements to splitvertical gradients - Add a small gradient in the + middle between the upper and lower gradients + * Fix another emacs event storm, don't configure the window when it sets its + WM_NORMAL_HINTS unless we have to + * Fix for focus cycling to helper windows - Don't count dialogs as a sibling, + so they don't stop you + * Allow the use of negative numbers in per-app settings position to place a + window relative to the right/bottom edges of your monitor (taking the + struts (panels) into account) + * Fix focus moving irrationally when focusLast was off + +3.4: + * Add Indian Bengali translation + * Updated Italian translation + * Small updates to Czech, Estonian, Norwegian, and German translations + * Removed out-of-date Japanese translation (ja.po) + * Removed out-of-date Croatian translation (hr.po) + * Allow parentrelative theme textures to have borders, bevels and interlacing + * Add new theme elements: window.active.title.separator.color and + window.inactive.title.separator.color (note that globbing might set these + properties when you didnt mean to) + * Add new theme elements: osd.border.width, osd.border.color, osd.bg, + osd.label.bg, osd.label.text.color, osd.label.text.font, osd.hilight.bg, + osd.unhilight.bg + * Add new underMouse focus option + * Rename new theme element menu.items.activedisabled to + menu.items.active.disabled for consistency with other elements + * Improvements to smart placement - especially with xinerama (Twinview) + * Fixes for focus with omnipresent windows - Allow focus to fallback to them + except during desktop switches (other mechanisms exist for that) + * Fix for putting child windows (transients) on all desktops + * Changes to how Shift/Control/CapsLock are used + * Fix focus fallback when windows are not visible (showing the desktop, or + iconic windows) + * Fix bug #3196 - Unable to alt-tab away from full-screen windows + * Fix crash in parsing empty xml fields + * Fix left and right contexts resizing the wrong way in the example mouse + focus config file + * Fix so dock doesn't auto-hide when the mouse is inside it (over top of a + dock app) + * Allow new utility windows to gain focus like normal windows + * Add workarounds for Java Swing applications (Extra ConfigureNotify events) + * More fixes for modal window usability (Deiconify the modal window when you + try to focus its parent) + * Fixes to focus tracking, especially with iconify animation + * Changes to libobrender's API for ObConf 2.0 (RrThemeNew) + * Check for libxml >= 2.6.0 when compiling + * Fix for Openoffice windows (Static window gravity) + * Fix for hovering over window buttons while a menu is open + * Make the Escape key close all levels of a menu, not just one + * Change default drag threshold to 8px (Same as in XSettings) + * Fixes for windows restacking with TopIf, BottomIf, or Opposite modes. + Only compare with valid windows + * Fix for actions which find other window edges. Find all windows, and only + windows which are on the right desktop + * Improve RaiseLower action (Not lower when it doesn't need to move the + window) + * Fix for the menu hideDelay (Don't hide immediately when you click to open + the menu), make it work reliably + * Make focusLast default to true when not present in the config file + +3.3.995: + * Fix bug introduced in 3.3.993 that prevented you from raising focused + windows above fullscreen windows + * Fix bug introduced in 3.3.993 that made the desktop window keep focus + when changing desktops + * Add Arabic translations + * Added Control-Alt-Up/Down bindings to the mouse focus example rc.xml + * Don't raise on clients in the client context in the mouse focus example + rc.xml + * Fixes for when to focus new windows - improve keeping transient windows + from stealing focus, and allow new windows to get focus when any relative + window is focused + * Fixes for frame decorations layout + * Fixes for forcing window sizes to fit on the screen. Don't resize windows + if they are a user-specified size (USSize) or if they are non-normal + windows. (Fix Rox pinboard mapping across multiple monitors) + * Centre window icons when they are not square + * Fixes for Java windows behaving badly - Java makes all kinds of + non-standard-compliant assumptions which we are forced to meet + * Changed startup notification timeout from 30 to 20 seconds + * Fix possible crashes and misbehaviors with new focus cycling dialogs + * Recreate enter events when Press mouse actions move windows (e.g. Lower + action) for focus-follows-mouse + * Update Slovak translation + * Highlight the first menu entry when there is a separator/header at the top + * Only highlight the first menu entry when the menu is opened with a key + binding + * Fix crash when you press enter with nothing selected in the menu + * Fix for ignoring an unmap event after restart + * Fixes for setting the clients' colormaps + * Change focusLast option behavior to what it was in 3.3.1. It only affects + changing desktops. + * Add comments to the default rc.xml files about the various focus options + * Fix focus bug where a window had the keyboard grabbed when it unmapped + (Firefox gmarks extension) + * Update Catalan translation + * Fix alignment of the titlebar elements when the window icon is placed to + the right of the label + * Improve placement of child (transient) windows. When their parent is + omnipresent, open the child on the current desktop only. + * When a window has modal children on another desktop and you try to focus + it, bring the modal child to the current desktop to focus it instead + * Let you focus cycle to windows with a modal child on another desktop + * Fix for not seeing windows change their partial strut + * Give iconified windows a faded icon in the focus cycling (Alt-Tab) dialog, + and in the client list menus + * Make it more difficult to select items by accident in parent menus when it + changes direction. Moving the mouse only selects menu entries for a few + levels of open menus. Mouse clicks will select menu entries further down. + * Add Simplified Chinese translation + * Set the current theme in the OB_THEME root property for other applications + * Notify ksplash when we have started up if dcop is installed + * Allow you to resize the window on the client padding as well as the outer + border + * Fix to show the focus cycle indicator when focus cycling and there is only + 1 valid target + * Centre splash screens on one monitor + * Don't centre parent-less utility/menu/toolbar windows on the screen + * Remove the bottom decorations for fully maximized windows + * Fix crash when reconfiguring and no themes can be found + +3.3.994: + * Fix a bug introduced in .993 that prevented you from switching desktops + and caused general mayhem + +3.3.993: + * Update for Russian translation + * Added Dutch translation + * Fix for choosing badly sized icons from the window for the window cycling + dialog (and other things). (Fixes Pidgin icon) + * Open child (transient) windows on the current desktop if they don't have + a direct parent, or if the parents are not all on some other desktop. + (Fixes Pidgin and Gimp dialogs appearing on the wrong desktop) + * Make windows fit within the available space when they are first created, + if they are not user-positioned. (Fix xpdf making itself bigger than the + space inside the struts, and hiding behind them). + * Don't let child (transient) windows steal focus when the situation is + ambiguous + * Fix bevels and borders display in the titlebar + * Set the _NET_ACTIVE_WINDOW hint properly, when nothing is focused (This + is the proper fix for libwnck pagers.) + * Fallback focus immediately during iconify animation in all cases (was + missing RevertToParent case) + * Fix new frame elements being given 0 sizes for very small windows + * Send iconified windows to the bottom of the focus order so you can + Alt-Shift-Tab to get back what you just iconified. This makes things more + predictable. + * Show the hostname for remotely run programs in iconified titles + * Stop focus flicker when sending windows to a desktop and following them, + and when changing desktops with an omnipresent window focused + * Empty desktop names in the configuration file are not used, so you can set + a name on the second desktop without having to set one on the first + * Make control key work for menu accelerators like it does for mouse + clicking and hitting enter + * Add C-A-Up/Down mouse bindings to switch desktops, similar to the + C-A-Left/Right/Up/Down keyboard bindings + +3.3.992: + * Don't move mouse focus when the mouse is over the focus cycling dialog + * Fixes for positioning of frame decoration elements + * Update for Brazilian Portuguese translation + * Improvements in how decorations respond for maximized windows. (If they + can't be resized don't "hide" the resizing contexts) + * Updated Italian translation + * Added Czech translation + * Fix for applications that do weird things with modal windows (Make sure + you can focus cycle to them) + * Updated Spanish translation + * Fix for programs using the old WM-spec, and not setting unused fields to + 0 in _NET_ACTIVE_WINDOW messages (Rox pinboard) + * Fixes for default rc.xml configuration file. Updated comments and comment + out the applications section in an xml compatible way. + * Update for Portuguese translation + * Fix support for window gravity (eg xterm -geometry -0+0) + * Fix for libwnck with iconify animation (Unmap the parent of the client's + window) + * Force the top left corner of new windows onto the screen area, if the + position wasn't requested by the user (Fix xpdf and others hiding their + titlebar under a panel) + * Fix for determining text geometry from Pango + * Save the desktop layout and names in the session, and restore them on next + log in. + * The <desktop> section from the configuration file is only used for + defaults, and so is only applied at the start of a new log-in. (Use ObConf + 1.6.2 to change the config file for these options, and have them set + immediately, if you don't use a session manager to save them.) + * Fix updating and crashing in the old split client list menu + * Translate the default desktop names + * Update Taiwanese Chinese translation + * Less restrictions of behavior based on window type (Let you maximize + dialogs and utility windows) + * Fix for focus tracking when Press mouse actions are used + * Fix bug which let the openbox-*-session scripts take command-line arguments + * Better detection of legacy fullscreen windows vs. undercorated windows + that the user has maximized + +3.3.991: + * Added Brazilian Portuguese translation + * Added a rough Italian translation + * Added Estonian translation + * Update Polish translation + * Make focus stealing prevention a little less intrusive, especially with + default config + * Updates for Swedish translation + * Fixes for decorations of maximized windows + * Fix so that clicking in the corner of a maximized window presses the button + * Don't reload the session and reapply it when restarting + * Fix for losing the original size of a window that mapped with maximized + state (or on restart) + * Let you move splash type windows + * Fix for window placement using wrong dimensions + * Stop xemacs ConfigureNotify storm + * Fix bug stopping panels from moving themselves off-screen to hide + themselves + * Fix for GNOME/Openbox and openbox-gnome-session to use a new session name, + because GNOME won't let you replace the window manager in the saved session + * Don't restore session state when multiple windows have the same + identifiers, because we can't tell them apart then (Firefox/Thunderbird) + * Small fixes to build system to make it more reliable + * Fix for what manpage files are distributed in the source code, so they + will not be pre-built with the wrong info + * Fix pkg-config files for compiling things against Openbox libraries + +3.3.990: + * Wildcard matching for per-application (per-window) settings. Let you match + multiple rules for a window. (Props to Mark Pustjens for code to do this.) + * Added startup notification support to the menus and key/mouse bindings + with <startupnotify>. It's an option for execute actions. + * New combined client menu, accessed with + <action name="ShowMenu"><menu>client-list-combined-menu</menu></action> + * No menu titles - instead added separators with labels to be used as menu + headers anywhere in menus + * Improved client menu - placement, the options in it, and added an icon for + "All Desktops" + * Keyboard shortcuts in menus, by highlighted letter or by the first letter + * Menus dont jump around, are always placed on screen + * Menus larger than can fit on the screen are broken into a submenu at the + bottom titled "More..." + * Disabled menu items can be selected now (not used though, of course) + * Using ()'s instead of a line separator to signify iconified windows in the + client list menus + * New Root context for mouse bindings + * New "alt-tab" focus switching dialog + * openbox-session command and log-in option which provides the autostart + capability. + * SCIM support in the default Openbox session launched by the + openbox-session command + * Many code paths made significantly faster + * Sections of code rewritten to be more reliable, such as session + save/restore and input focus handling + * Handle the X server's clock going backwards, without locking up. Sometimes + the server can still lock up though, it is not recommended to do this! + * Better handling of window types such as docks, utility windows, tool bar + windows. Clicking to focus these windows is done more intelligently. Dock + windows no longer get focus when you click them, so that you don't have + focus flickering when you use a dock window with a taskbar. + * Better language support through Pango, which is now mandatory, and the + code for Pango has been greatly improved and optimized. + * Support for windows that use true 32-bit transparency + * Resize grips (with mouse binding contexts) for the top, left, and right of + the window frame + * Smarter about keeping windows on the screen without invading user's + capabilities + * Add MoveFromEdge* actions corresponding to MoveToEdge* but aligns far + edges instead of near edges, so if you have two overlapping windows you + can easily put them side by side. + * Better choice of mouse cursors for startup notification + * Focus stealing prevention + * Improve window placement across multiple Xinerama monitors + * Support 8-bit truecolor visuals (such as in vncserver) + * Better, more friendly support for omnipresent windows + * Full support for EWMH 1.4-draft2 + * Iconify/restore animation ! + * Improved support for running openbox with multiple screens (non-xinerama) + - won't steal focus from other screens, and let you focus a screen by + clicking on the desktop (root window). + * Support for more cursors through XCursor, means that startup notification + can use a pointer+watch cursor if you are using an XCursor theme + * Fully maximize terminal windows (and any resized-by-increment window) + * Show the hostname of clients running on remote computers in the titlebar + * Add <panels> and <desktop> options to NextWindow, PreviousWindow, + DirectionalFocusNorth etc.. + * Synchronize resizing with the application when possible + * Smarter subdivision of windows into 9 sections for resizing + * Set default icon on windows so applications can agree on an icon + * Better support for programs that are on more than one desktop + (multiple firefox windows for example) - move to the desktop when you're + using the application and it opens a window on another desktop. + * Chrooted key chains, and key quoting through chroots + * New BreakChroot action to break out of the current chroot + * New keychains popup, to show where you are in a keychain + * Improved show-desktop mode. When you open a new window, the old windows + are not all restored + * Add <allDesktops> option to NextWindow and PreviousWindow. The desktop + name will be shown in the focus cycling popup + * Dynamically size the window and desktop dialogs. Desktop layouts that + aren't horizontal look better in the desktop cycling dialog. + * Support for legacy fullscreen applications (that don't use EWMH) such as + VLC + * Session support for old clients that do not use the session management + protocol + * Desktop setup is only changed on startup, so as to not overwrite settings + from other apps like pagers + * Take advantage of ksmserver (KDE Session Manager) features + * KDE/Openbox and GNOME/Openbox options for display manager GDM/KDM. Use + openbox-gnome-session and openbox-kde-session to run a GNOME or KDE + session with Openbox as the window manager. + * Titlebar buttons autohide when hiding won't move other buttons (removed + the hideDisabled option from config file) + * Support user time window hint, which is good for laptops power usage + * When legacy windows raise themselves, interpreate as wanting to be + activated. Makes Firefox more pleasant. + * Don't show handles on windows that cant resize for more extreme visual + minimalism + * Let you move maximized windows between Xinerama monitors + * Improve application awareness of focus, e.g. Kopete and Firefox, so they + know when they are or aren't focused, and which window in their + application is focused + * Improvements in the distributed themes + * Add new Clearlooks and Clearlooks-Olive themes by John McKnight + * Fonts are no longer loaded from theme. They are loaded from the rc.xml + config file. Font shadow settings are still loaded from the theme though. + 5 fonts to be set: ActiveWindow InactiveWindow, MenuHeader, MenuItem and + OnScreenDisplay + * Distributed theme names have been renamed with Capitalization + * menu.items.activedisabled.text.color + * menu.border.color (note that globbing might set these properties when + you didnt mean to) + * window.active.border.color (note that globbing might set these + properties when you didnt mean to) + * window.inactive.border.color (note that globbing might set these + properties when you didnt mean to) + * menu.border.width + * Guarantee that titlebar buttons are even sized, so even sized icons + (button masks) will be centered + * Default titlebar button icons (as xbms) installed to /usr/share/doc/openbox + * New mouse binding contexts Left, Right, TLCorner, TRCorner and Top. These + need bindings in config file to be useful. Also new context Bottom which + is synonym for Handle. + * W is no longer hard coded to Mod4. It is now a shortcut for Super, which + is usually bound to Mod4. xmodmap will show your mapping. + * The per-app setting <head> has been renamed to <monitor> + * Can use "default" for anything in per-app settings + * Removed <edges_hit_layers_below> option from config file + * --reconfigure command line option + * --restart command line option + * Closed bug #886 - Reference point for client-menu + * Closed bug #898 - Add option for OB2 style menu behaviour + * Closed bug #952 - Focus sometimes gets lost when closing windows + * Closed bug #1006 - duplicate window title numbering is so-so + * Closed bug #1080 - menus dont update when using ctrl to keep them open + * Closed bug #1082 - doing window stuff while changing focus is mad slow + * Closed bug #1106 - Openbox resize problems with Beep Media Player and some + websites using Mozilla + * Closed bug #1146 - minimized windows dont unminimize from fspanel if ob + restarted + * Closed bug #1409 - Window stacking is messed up when dealing with multiple + nested dialog boxes + * Closed bug #1526 - Incorrect handling of utility windows + * Closed bug #1846 - Patch for theme overrides + * Closed bug #1974 - Limit the number of resizes per second to make window + resizing smoother + * Closed bug #2060 - Apps disappear when using composite extension + * Closed bug #2221 - _NET_WM_STATE_(ABOVE|BELOW) is being inherited + * Closed bug #2250 - openbox ignores size request ? + * Closed bug #2251 - support for legacy ""fullscreen"" + * Closed bug #2254 - segfault in render/render.c on sendtodesktop [endian?] + * Closed bug #2255 - segmentation fault on 8-bit truecolor visual + * Closed bug #2258 - long menus truncated + * Closed bug #2311 - Does not display the pressed state while a button in the + frame is toggled + * Closed bug #2321 - ResizeRelative -up and -left makes window move when at + min_size + * Closed bug #2483 - Pinned windows raise to top on all desktops when they + become focused on just one desktop + * Closed bug #2491 - Client List by App instead of by Desktop + * Closed bug #2551 - Request to be able to cycle docks (e.g. + * Closed bug #2659 - Menu header display should be configurable + * Closed bug #2769 - temporarily chroot the keychain tree + * Closed bug #2841 - Changes to the keepborder option aren't applied when + reconfiguring + * Closed bug #2850 - Openbox gives black backgrounds to windows using the + new 'rgba' or 'argb' visual + * Closed bug #2865 - openbox 3.3 is not C89 compatible + * Closed bug #2872 - Focus issues with Eclipse and Firefox + * Closed bug #2878 - Openbox crash when used with vncserver + * Closed bug #2897 - Openbox shouldn't redraw the whole screen everytime + when minimizing windows. + * Closed bug #2898 - Patch adding advanced keyboard support in popup menus. + * Closed bug #2982 - tilda doesn't work propertly + * Closed bug #3034 - themeupdate.py seems to bee broken + * Closed bug #3045 - RrPaint in obrender should be split for easier use by + external apps + * Closed bug #3050 - 'syscrash' theme update + * Closed bug #3051 - When closing windows + * Closed bug #3053 - Updates de.po for German Localisation + * Closed bug #3059 - Feature Request: NextWindowForAllWorkspaces + * Closed bug #3063 - extensions_xinerama_screens() leaks memory if Xinerama + is enabled + * Closed bug #3064 - slist_path_add() might leak memory + * Closed bug #3068 - Multimonitor focus problems + * Closed bug #3081 - window doesn't appear + * Closed bug #3092 - Config parser expects wrong values of "layer" in + per-application settings + * Closed bug #3094 - smart placement should Ignore maximized windows + * Closed bug #3100 - Netwmpager is configured with layer = "below" but OB + is ignoring it + * Closed bug #3101 - Openbox won't compile on CentOS 4.4 + * Closed bug #3102 - svn-6175 windows come not back in the foregroud + * Closed bug #3106 - Fullscreened mplayer loses focus when switching to + another desktop and back + * Closed bug #3108 - After revision 6437 the desktop dialog window stays on + screen + +3.3.1: + * Fix panels getting a border with keepBorder turned on. + * Fix a crash in mirrorhorizontal when drawing a surface with width 1. + +3.3: + * Add a showDelay option for the dock. + * Fixed onscreen-keeping code which broke a bit in -rc2. + * Fix incorrect handling of window properties on 64 bit arches. + * Fix pixelsize being the same as size for pango, now it is correct. + * Fix drawing of icons taller than wide, eg gimp. + * Add a 'mirrorhorizontal' gradient, like horizontal but mirrored in the + center and 'splitvertical' which is like vertical but nonlinear in a fancy + way. + * Translations for de, hr and zh_TW added. + * Add initial per-app settings support. + * Fix some outstanding issues with 64-bit support. + * Add an optional delay for displaying submenus so you can move the mouse + over some entries without flashing each submenu. + * Plug some minor memleaks and fix minor bugs. + * Changed algorithm for downscaling window icons. + * New themes! + * Disable support for libstartup-notification by default, it doesn't seem + to work well. + * Allow disabling of duplicate window numbering by option titleNumber in + theme section. + * Add moverelative and resizerelative actions. + * Option middle for menus that make submenus be centered instead of aligned + by the top edge to the parent entry. + * Fix transient (ie dialog) windows being placed offscreen if the parent + is close to the edge of the screen. + +3.3-rc2: + * Fixed some typos and errors in rc.xsd + * Add the noStrut option to the dock (to allow maximizing windows over it), + useful when it is not in the above layer. + * Fix transparent terminals not updating when using ToggleDecor. + * Yet more poking at the onscreen-keeping code, should now do the right + thing always. + * Add an option to hide disabled buttons instead of showing them as disabled. + * Hopefully fixed all the remaining pango issues. The new 1.10 release also + fixes shadows again. + * I think the lib linking in Makefile.am is right now, please tell me if it + isn't. + * Added a finnish translation and some minor updates to others. + * Fixed configure.ac so --enable-pango doesn't disable pango support or the + other way around, make enabled be default to get some extra testing. + +3.3-rc1: + * Poked around a bit in the code that keeps windows onscreen, if you + are using xinerama, please let me know if things are better/worse/same. + * Add support for pango, disabled by default due to a bug in pango 1.8 that + breaks shadows, it works fine with 1.6.x though. Poke the pango developers + here http://bugzilla.gnome.org/show_bug.cgi?id=169622 if you want this + to be fixed. + * Made edge resist and Send/MoveToEdge not hit windows at layers below + the current one, add option to disable this behaviour. + * Fixed directional focus not getting trapped in omnipresent windows + * Fixed focus actions when bound to the mouse, normal focus cycle is always + linear and directional focus is totally disabled, it makes no sense + anyway, just move the mouse. + * Added an option for the raise/lower/raiselower/unshaderaise/shadelower + options called group that lets you bind stuff to raise/lower the whole + window group. <action name="raise"><group>yes</group></action> + * Add various new actions. I haven't updated the web docs yet, but I think + I managed to get all of them listed in rc.xsd. + * Bugs 1783, 1812, 1863, 1905, 2005, 1957 fixed. + * Some translations added. (ca, no, pl, ru, fr) + +3.2: + * Added spanish and swedish translations + * Changed "Unnamed Desktop" to "Desktop n" where n is the actual + number of the desktop + * Hopefully no more of the hanging X totally bug + * the fullscreen property now overrides aspect hints, all programs i've + tried that use it (mplayer and tvtime) handle the aspect internally + anyway, this just makes sure you have black borders all the way to the + edge. If this breaks anything for you, let me know. + * Added M as an alias for Mod3 in the same manner that W is an alias for Mod4 + * Added probably too many options and actions + * ToggleDockAutohide to toggle the autohide option at runtime, + doesn't save it to the config + * keepBorder option to set if you want the 1-pixel border when you + toggleDecorations + * added popupShow and popupPosition to control where and when the + move/resize-popup is shown, these go in the resize section of the config + popupPosition can be Top or Center, popupShow can be Always, Never or + Nonpixel (this is the default). + * Added the xorStyle option to change the behaviour of menus along the bottom + edge, note that the default is true + * warpPointer option in the menu section to control if the mouse cursor is + to be warped by openbox when the menu moves to keep on screen to keep the + cursor in the same place relative to the menu. (I think i got that right) + * Added back the focusLast option (this controls what happens when you + change desktops and have followMouse on) + * Added an option to set which desktop to start on when you start openbox, + firstdesk, for example you might like to set it to 5 if you have a 3x3 + grid to start in the middle. + * I think that was everything, have fun + +3.1: + * Added japanese translation (bug #1029) + * Closed bug #1060 (modal unfocused window should be placed above + focused parent) + * Closed bug #1012 (Openbox stalls gnome session startup after + --replace) + * Closed bug #1073 (The Desktop action doesn't work) + * Closed bug #1023 (config_focus_raise declared as guint) + * Closed bug #1019 (Strange behavior with focus:followMouse and + RaiseLower action) + * Closed bug #1007 (use g_snprintf in openbox/session.c instead of + sprintf) + * Closed bug #1001 (gimp crashes openbox) + * Closed bug #1005 (import -frame leaves windows in a temporary + "on top" state) + * Closed bug #881 (Programs started with X become zombies upon + shutdown.) + * Closed bug #1025 (desktopnext/previous and desktopleft/right dont + quite get along) + +3.0: + * Closed bug #908 (sometimes some weird grab is left) + * Closed bug #945 (xawtv overlay visible on every desktop) + * Closed bug #944 (remove restarting/reconfiguring messages from + openbox.c) + * Closed bug #946 (use g_strsplit in parse.c:split_paths()) + * Closed bug #943 (consistent glib type (gint, gchar, etc.) usage) + * Closed bug #951 (stale openbox session files) + * Closed bug #947 (Sloppyfocus can yield two windows at the same time + with focus (!)) + * Closed bug #948 (Gets stuck in 'move' mode if mouse is let up while + dragged away from title bar) + * Closed bug #949 (dock above gnome panel behaviour) + * Closed bug #902 (Openbox raises gDesklets when clicked) + * Closed bug #904 (ToggleMaximizeHorz & Vert dont always toggle) + * Closed bug #907 (window focus is not properly indicated) + * Closed bug #919 (Temporary raise window when alt tabbing) + * Closed bug #921 (focus rectangle stays on the screen after modifier + release) + * Closed bug #925 (Invalid option: '--sm-save' & '--sm-disable') + * Closed bug #926 (problem with client-list-menu, destkop switching, + and sticky client) + * Closed bug #927 (disappearing windows, crash with I-merry theme) + * Closed bug #929 (crash (signal 11) when activating Reconfigure) + * Closed bug #940 (actions dont work on desktops with no client + windows) + * Closed bug #943 (consistent glib type (gint, gchar, etc.) usage) + * Closed bug #957 (openbox crashes when exiting out of OpenOffice + Impress) + * Closed bug #960 (look for dupes in xdg paths when setting up slists?) + * Closed bug #961 (Menu causes core dump) + * Closed bug #963 (wrapper for mkdir() in parse.c) + * Closed bug #964 (free GErrors in action.c, openbox.c) + * Closed bug #965 (add message for chdir() failure in openbox.c) + * Closed bug #966 (list->next/prev to g_[s]list_next/previous, list != + NULL to list + * Closed bug #971 (typo in code) + * Closed bug #974 (use g_getenv() in parser/parse.c) + * Closed bug #975 (void * -> gpointer) + * Closed bug #976 (use g_strerror() in openbox/session.c) + * Closed bug #978 (disable entry to go to desktop if it is the current) + * Closed bug #979 (request for version check in ob libraries) + * Closed bug #944 (remove restarting/reconfiguring messages from + openbox.c) + * Closed bug #968 (rc.xml refers to a non-existant "debian" menu file) + +3.0-rc4: + * Closed bug #921 (focus rectangle stays on the screen after modifier + release) + * Closed bug #927 (disappearing windows, crash with I-merry theme) + * Fix crash when restoring a session with a maximized window + * Fix leak of RrColors in menus + * Closed bug #929 (crash (signal 11) when activating Reconfigure) + * Allow focus to be moved around by clients (for WM_TAKE_FOCUS to + work as it is meant to). This means fullscreen apps like wine and + bzflag work now + * Reversed the default directions for the mouse wheel desktop + switching actions + * Don't wrap around in the default keyboard desktop switching bindings + * Place parentless dialogs so they avoid always-on-top windows + * Ignore fullscreen windows in placing new ones + * Closed bug #940 (actions dont work on desktops with no client + windows) + +3.0-rc3: + * Closed bug #904 (ToggleMaximizeHorz & Vert dont always toggle) + * Allow resizing of windows in only vertical/horizontal directions + (via Alt-MiddleClick-Drag with the default bindings) + * Added intelligence to window cycling and raise/lowering based on + window types + * Closed bug #902 (Openbox raises gDesklets when clicked). This has the + side effect that panels which do not set the ABOVE state (most panels + curerntly around) will no longer be kept always on top. You can set the + ABOVE state yourself by using the client menu (Alt-RightClick in the + default bindings) and selecting the Always on top Layer + * Closed bug #907 (window focus is not properly indicated) + * Added a focus indicator during focus cycling that is always visible + instead of just moving the focus hilight around. + +3.0-rc2: + * Closed bug #885 (raiseOnFocus is flawed) + * Closed bug #887 (Mouse-wheel scrolling only changes 1 desktop when + using "dialog" option) + * Closed bug #888 (some random coredump) + * Closed bug #889 (focus follow mouse not following) + * Closed bug #892 (client_under_pointer() doesn't check DESKTOP_ALL) + * Avoid the focus flicker when switching desktops + * Free the XML DOM trees properly, fixes mem leak + +3.0-rc1: + * Closed bug #875 (RaiseLower fails to funtion on windows with dialogs) + * Added the <moveButton> option for moving dock apps around inside the + dock. (Closes bug #798) + * Made the "Desktop" action show the pager/desktop dialog + * Add the <dialog> option to the Desktop and Focus switching actions + * No longer iconify fullscreen windows when they lose focus (Closes + bug #876) + * When not using focusLast, when a focused transient is closed, focus + will still fallback to its parent or transient siblings + * Improved intelligence for focus cycling with modal transients + * Removed the focusLast option + * Added the placement-policy option to select if windows are placed + under the mouse pointer + * Children inherit their window icon from their parent + * Closed bug #878 (minimal resizing of mplayer makes openbox crash) + * Fix case where opening a menu would leave Openbox in a locked state + * Upstream updates for TheBear theme + +3.0-beta6: + * Added _OB_WM_STATE_UNDECORATED hint, which can be read and controlled + by external applications + * Closed bug #874 (libtool won't install libobrender.la) + * Launch ObConf when selecting GNOME's Desktop Preferences for Windows + * Focus new children always if their direct parent is focused + +3.0-beta5: + * Initial release. diff --git a/COMPLIANCE b/COMPLIANCE new file mode 100644 index 0000000..b5fe279 --- /dev/null +++ b/COMPLIANCE @@ -0,0 +1,78 @@ +EWMH Compliance Document: +========================== + +Listed below are all the NetWM (or EWM) hints decided upon on freedesktop.org +and Openbox's current level of compliance with the spec. Beside each hint is +the version of the spec which Openbox is compliant up to for the hint. + +( + compliance : + - = none, + / = partial, + + = complete, + * = Openbox is compliant, but something else needs checking + ? = unknown +) + ++ _NET_SUPPORTED (1.3) ++ _NET_CLIENT_LIST (1.3) ++ _NET_NUMBER_OF_DESKTOPS (1.3) ++ _NET_DESKTOP_GEOMETRY (1.3) + Openbox doesn't support large desktops so these just match the + screen size. ++ _NET_DESKTOP_VIEWPORT (1.3) + Openbox doesn't support large desktops so these are just (0, 0). ++ _NET_CURRENT_DESKTOP (1.3) ++ _NET_DESKTOP_NAMES (1.3) ++ _NET_ACTIVE_WINDOW (1.3) ++ _NET_WORKAREA (1.3) ++ _NET_SUPPORTING_WM_CHECK (1.3) ++ _NET_VIRTUAL_ROOTS (1.3) + Openbox does not use virtual roots, so this is not needed. ++ _NET_DESKTOP_LAYOUT (1.3) ++ _NET_SHOWING_DESKTOP (1.3) ++ _NET_CLOSE_WINDOW (1.3) ++ _NET_MOVERESIZE_WINDOW (1.3) ++ _NET_WM_MOVERESIZE (1.3) ++ _NET_WM_NAME (1.3) ++ _NET_WM_VISIBLE_NAME (1.3) ++ _NET_WM_ICON_NAME (1.3) ++ _NET_WM_VISIBLE_ICON_NAME (1.3) ++ _NET_WM_DESKTOP (1.3) ++ _NET_WM_WINDOW_TYPE (1.3) + Openbox does not let windows change this hint after mapping. ++ _NET_WM_STATE (1.3) ++ _NET_WM_ALLOWED_ACTIONS (1.3) ++ _NET_WM_STRUT (1.3) ++ _NET_WM_STRUT_PARTIAL (1.3) + Openbox uses these to create per-monitor struts in Xinerama setups. ++ _NET_WM_ICON_GEOMETRY (1.3) ++ _NET_WM_ICON (1.3) +- _NET_WM_PID (1.3) + Openbox does not currently kill processes. +- _NET_WM_HANDLED_ICONS (1.3) + Openbox does not display icons for iconic windows. ++ _NET_WM_USER_TIME (1.3) +- _NET_WM_USER_TIME_WINDOW (1.4) +- _NET_WM_PING (1.3) + Openbox doesn't look for hung processes at this time. ++ _NET_FRAME_EXTENTS (1.3) ++ _NET_WM_STATE_DEMANDS_ATTENTION (1.3) ++ _NET_RESTACK_WINDOW (1.3) ++ _NET_WM_SYNC_REQUEST (1.3) ++ _NET_WM_FULL_PLACEMENT (1.4) ++ _NET_WM_MOVERESIZE_CANCEL (1.4) ++ _NET_REQUEST_FRAME_EXTENTS (1.3) ++ _NET_WM_ACTION_MOVE (1.3) ++ _NET_WM_ACTION_RESIZE (1.3) ++ _NET_WM_ACTION_MINIMIZE (1.3) ++ _NET_WM_ACTION_SHADE (1.3) +- _NET_WM_ACTION_STICK (1.3) + Openbox does not do large desktops, so no sticky state is needed. ++ _NET_WM_ACTION_MAXIMIZE_HORZ (1.3) ++ _NET_WM_ACTION_MAXIMIZE_VERT (1.3) ++ _NET_WM_ACTION_FULLSCREEN (1.3) ++ _NET_WM_ACTION_CHANGE_DESKTOP (1.3) ++ _NET_WM_ACTION_CLOSE (1.3) ++ _NET_WM_ACTION_ABOVE (1.4?) ++ _NET_WM_ACTION_BELOW (1.4?) @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. @@ -0,0 +1,33 @@ +dirs: + openbox - core of the WM + render - librender, rendering routines for the WM and for apps + parser - libparser, for parsing config files + +Beware the Client.transient_for. It can be set to a !NULL value of TRAN_GROUP, +which is not a valid pointer. You must ALWAYS check for TRAN_GROUP before +following transient_for. However if it is transient for the group, this +excludes other windows whom are transient for the group, and windows which +are children of the window (infinite loops would result)! + +When using coordinates/sizes of windows, make sure you use the right area. The +Client.area rect is the reference point and size of the *CLIENT* window. This +value is not what you see in any shape or form, and gravity is applied to it to +translate it into what you see. The Client.frame.area is the actual position +and size of the entire frame. This is usually the value you want to use, unless +you are in client.c (probably) and adjusting/using the position or size from +the client's perspective. + +Indentation +----------- +For openbox, we aim to have consistent coding style. Some, but surely +not all, guidelines: + * use 4 space indents + * tabs should not appear in source files + * functions should have the opening and closing braces on their own + lines + * most other constructs should have braces on the same line as the + statement + * else appears on a new line, just like an if + * when in doubt look at the rest of the source + * vim users can use "set expandtab tabstop=4 shiftwidth=4 + softtabstop=4" for some of this diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1f7427c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,604 @@ +SUBDIRS = m4 po + +docxbmdir = $(docdir)/xbm +themedir = $(datadir)/themes +localedir = $(datadir)/locale +configdir = $(sysconfdir)/xdg +rcdir = $(configdir)/openbox +xsessionsdir = $(datadir)/xsessions +gnomesessiondir = $(datadir)/gnome-session/sessions +gnomewmfilesdir = $(datadir)/gnome/wm-properties +pkgconfigdir = $(libdir)/pkgconfig +obtpubincludedir= $(includedir)/openbox/@OBT_VERSION@/obt +rrpubincludedir = $(includedir)/openbox/@RR_VERSION@/obrender +pixmapdir = $(datadir)/pixmaps +xsddir = $(datadir)/openbox +appsdir = $(datadir)/applications + +theme = Clearlooks + +AUTOMAKE_OPTIONS = subdir-objects foreign + +ACLOCAL_AMFLAGS = -I m4 + +INCLUDES = -I. + +check_PROGRAMS = \ + obrender/rendertest + +lib_LTLIBRARIES = \ + obt/libobt.la \ + obrender/libobrender.la + +bin_PROGRAMS = \ + openbox/openbox \ + tools/gdm-control/gdm-control \ + tools/gnome-panel-control/gnome-panel-control \ + tools/obxprop/obxprop + +nodist_bin_SCRIPTS = \ + data/xsession/openbox-session \ + data/xsession/openbox-gnome-session \ + data/xsession/openbox-kde-session + +dist_rc_SCRIPTS = \ + data/environment + +nodist_rc_SCRIPTS = \ + data/autostart/autostart + +dist_libexec_SCRIPTS = \ + data/autostart/openbox-xdg-autostart + +nodist_libexec_SCRIPTS = \ + data/autostart/openbox-autostart + + +## obrender ## + +obrender_rendertest_CPPFLAGS = \ + $(PANGO_CFLAGS) \ + $(GLIB_CFLAGS) \ + -DG_LOG_DOMAIN=\"RenderTest\" +obrender_rendertest_LDADD = \ + obt/libobt.la \ + obrender/libobrender.la \ + $(GLIB_LIBS) \ + $(PANGO_LIBS) \ + $(XML_LIBS) \ + $(X_LIBS) +obrender_rendertest_SOURCES = obrender/test.c + +obrender_libobrender_la_CPPFLAGS = \ + $(X_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(XML_CFLAGS) \ + $(PANGO_CFLAGS) \ + $(IMLIB2_CFLAGS) \ + -DG_LOG_DOMAIN=\"ObRender\" \ + -DDEFAULT_THEME=\"$(theme)\" +obrender_libobrender_la_LDFLAGS = \ + -version-info $(RR_CURRENT):$(RR_REVISION):$(RR_AGE) +obrender_libobrender_la_LIBADD = \ + obt/libobt.la \ + $(X_LIBS) \ + $(PANGO_LIBS) \ + $(GLIB_LIBS) \ + $(IMLIB2_LIBS) \ + $(XML_LIBS) +obrender_libobrender_la_SOURCES = \ + gettext.h \ + obrender/button.h \ + obrender/button.c \ + obrender/color.h \ + obrender/color.c \ + obrender/font.h \ + obrender/font.c \ + obrender/geom.h \ + obrender/gradient.h \ + obrender/gradient.c \ + obrender/icon.h \ + obrender/image.h \ + obrender/image.c \ + obrender/imagecache.h \ + obrender/imagecache.c \ + obrender/instance.h \ + obrender/instance.c \ + obrender/mask.h \ + obrender/mask.c \ + obrender/render.h \ + obrender/render.c \ + obrender/theme.h \ + obrender/theme.c + +## obt ## + +obt_libobt_la_CPPFLAGS = \ + $(XINERAMA_CFLAGS) \ + $(XKB_CFLAGS) \ + $(XRANDR_CFLAGS) \ + $(XSHAPE_CFLAGS) \ + $(XSYNC_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(XML_CFLAGS) \ + -DG_LOG_DOMAIN=\"Obt\" \ + -DLOCALEDIR=\"$(localedir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DCONFIGDIR=\"$(configdir)\" +obt_libobt_la_LDFLAGS = \ + -version-info $(OBT_CURRENT):$(OBT_REVISION):$(OBT_AGE) +obt_libobt_la_LIBADD = \ + $(XINERAMA_LIBS) \ + $(XKB_LIBS) \ + $(XRANDR_LIBS) \ + $(XSHAPE_LIBS) \ + $(XSYNC_LIBS) \ + $(GLIB_LIBS) \ + $(XML_LIBS) +obt_libobt_la_SOURCES = \ + obt/bsearch.h \ + obt/display.h \ + obt/display.c \ + obt/internal.h \ + obt/keyboard.h \ + obt/keyboard.c \ + obt/xml.h \ + obt/xml.c \ + obt/ddparse.h \ + obt/ddparse.c \ + obt/link.h \ + obt/link.c \ + obt/paths.h \ + obt/paths.c \ + obt/prop.h \ + obt/prop.c \ + obt/signal.h \ + obt/signal.c \ + obt/util.h \ + obt/xqueue.h \ + obt/xqueue.c + +## openbox ## + +openbox_openbox_CPPFLAGS = \ + $(SM_CFLAGS) \ + $(X_CFLAGS) \ + $(XCURSOR_CFLAGS) \ + $(SM_CFLAGS) \ + $(PANGO_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(LIBSN_CFLAGS) \ + $(XML_CFLAGS) \ + -DLOCALEDIR=\"$(localedir)\" \ + -DDATADIR=\"$(datadir)\" \ + -DCONFIGDIR=\"$(configdir)\" \ + -DG_LOG_DOMAIN=\"Openbox\" +openbox_openbox_LDADD = \ + $(XINERAMA_LIBS) \ + $(XRANDR_LIBS) \ + $(SM_LIBS) \ + $(GLIB_LIBS) \ + $(X_LIBS) \ + $(XCURSOR_LIBS) \ + $(LIBSN_LIBS) \ + $(XML_LIBS) \ + $(EFENCE_LIBS) \ + $(LIBINTL) \ + obrender/libobrender.la \ + obt/libobt.la +openbox_openbox_LDFLAGS = -export-dynamic +openbox_openbox_SOURCES = \ + gettext.h \ + openbox/actions/all.c \ + openbox/actions/all.h \ + openbox/actions/addremovedesktop.c \ + openbox/actions/breakchroot.c \ + openbox/actions/close.c \ + openbox/actions/cyclewindows.c \ + openbox/actions/debug.c \ + openbox/actions/decorations.c \ + openbox/actions/desktop.c \ + openbox/actions/dock.c \ + openbox/actions/dockautohide.c \ + openbox/actions/directionalwindows.c \ + openbox/actions/execute.c \ + openbox/actions/exit.c \ + openbox/actions/focus.c \ + openbox/actions/focustobottom.c \ + openbox/actions/fullscreen.c \ + openbox/actions/growtoedge.c \ + openbox/actions/iconify.c \ + openbox/actions/if.c \ + openbox/actions/kill.c \ + openbox/actions/layer.c \ + openbox/actions/lower.c \ + openbox/actions/maximize.c \ + openbox/actions/move.c \ + openbox/actions/moverelative.c \ + openbox/actions/moveresizeto.c \ + openbox/actions/movetoedge.c \ + openbox/actions/omnipresent.c \ + openbox/actions/raise.c \ + openbox/actions/raiselower.c \ + openbox/actions/reconfigure.c \ + openbox/actions/resize.c \ + openbox/actions/resizerelative.c \ + openbox/actions/restart.c \ + openbox/actions/shade.c \ + openbox/actions/shadelowerraise.c \ + openbox/actions/showdesktop.c \ + openbox/actions/showmenu.c \ + openbox/actions/unfocus.c \ + openbox/actions.c \ + openbox/actions.h \ + openbox/client.c \ + openbox/client.h \ + openbox/client_list_menu.c \ + openbox/client_list_menu.h \ + openbox/client_list_combined_menu.c \ + openbox/client_list_combined_menu.h \ + openbox/client_menu.c \ + openbox/client_menu.h \ + openbox/config.c \ + openbox/config.h \ + openbox/debug.c \ + openbox/debug.h \ + openbox/dock.c \ + openbox/dock.h \ + openbox/event.c \ + openbox/event.h \ + openbox/focus.c \ + openbox/focus.h \ + openbox/focus_cycle.c \ + openbox/focus_cycle.h \ + openbox/focus_cycle_indicator.c \ + openbox/focus_cycle_indicator.h \ + openbox/focus_cycle_popup.c \ + openbox/focus_cycle_popup.h \ + openbox/frame.c \ + openbox/frame.h \ + openbox/framerender.c \ + openbox/framerender.h \ + openbox/geom.h \ + openbox/grab.c \ + openbox/grab.h \ + openbox/group.c \ + openbox/group.h \ + openbox/keyboard.c \ + openbox/keyboard.h \ + openbox/keytree.c \ + openbox/keytree.h \ + openbox/menuframe.c \ + openbox/menuframe.h \ + openbox/menu.c \ + openbox/menu.h \ + openbox/misc.h \ + openbox/mouse.c \ + openbox/mouse.h \ + openbox/moveresize.c \ + openbox/moveresize.h \ + openbox/mwm.h \ + openbox/openbox.c \ + openbox/openbox.h \ + openbox/ping.c \ + openbox/ping.h \ + openbox/place.c \ + openbox/place.h \ + openbox/prompt.c \ + openbox/prompt.h \ + openbox/popup.c \ + openbox/popup.h \ + openbox/resist.c \ + openbox/resist.h \ + openbox/screen.c \ + openbox/screen.h \ + openbox/session.c \ + openbox/session.h \ + openbox/stacking.c \ + openbox/stacking.h \ + openbox/startupnotify.c \ + openbox/startupnotify.h \ + openbox/translate.c \ + openbox/translate.h \ + openbox/window.c \ + openbox/window.h + + +## gnome-panel-control ## + +tools_gnome_panel_control_gnome_panel_control_CPPFLAGS = \ + $(X_CFLAGS) +tools_gnome_panel_control_gnome_panel_control_LDADD = \ + $(X_LIBS) +tools_gnome_panel_control_gnome_panel_control_SOURCES = \ + tools/gnome-panel-control/gnome-panel-control.c + +## obxprop ## + +tools_obxprop_obxprop_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(X_CFLAGS) +tools_obxprop_obxprop_LDADD = \ + $(GLIB_LIBS) \ + $(X_LIBS) +tools_obxprop_obxprop_SOURCES = \ + tools/obxprop/obxprop.c + +## gdm-control ## + +tools_gdm_control_gdm_control_CPPFLAGS = \ + $(XAUTH_CFLAGS) \ + $(X_CFLAGS) \ + $(GLIB_CFLAGS) +tools_gdm_control_gdm_control_LDADD = \ + $(XAUTH_LIBS) \ + $(X_LIBS) \ + $(GLIB_LIBS) +tools_gdm_control_gdm_control_SOURCES = \ + tools/gdm-control/gdm-control.c + + +## default button masks ## +dist_docxbm_DATA = \ + data/xbm/bullet.xbm \ + data/xbm/close.xbm \ + data/xbm/desk_toggled.xbm \ + data/xbm/desk.xbm \ + data/xbm/iconify.xbm \ + data/xbm/max_toggled.xbm \ + data/xbm/max.xbm \ + data/xbm/shade_toggled.xbm \ + data/xbm/shade.xbm + + +## themes ## + +clearlooks_themedir = $(themedir)/Clearlooks/openbox-3 +dist_clearlooks_theme_DATA= \ + themes/Clearlooks/openbox-3/themerc + +clearlooks34_themedir = $(themedir)/Clearlooks-3.4/openbox-3 +dist_clearlooks34_theme_DATA= \ + themes/Clearlooks-3.4/openbox-3/themerc + +clearlooksolive_themedir = $(themedir)/Clearlooks-Olive/openbox-3 +dist_clearlooksolive_theme_DATA= \ + themes/Clearlooks-Olive/openbox-3/themerc + +mikachu_themedir = $(themedir)/Mikachu/openbox-3 +dist_mikachu_theme_DATA= \ + themes/Mikachu/openbox-3/bullet.xbm \ + themes/Mikachu/openbox-3/close.xbm \ + themes/Mikachu/openbox-3/desk.xbm \ + themes/Mikachu/openbox-3/iconify.xbm \ + themes/Mikachu/openbox-3/max.xbm \ + themes/Mikachu/openbox-3/themerc + + +natura_themedir = $(themedir)/Natura/openbox-3 +dist_natura_theme_DATA= \ + themes/Natura/openbox-3/close_hover.xbm \ + themes/Natura/openbox-3/close.xbm \ + themes/Natura/openbox-3/desk_toggled.xbm \ + themes/Natura/openbox-3/desk_hover.xbm \ + themes/Natura/openbox-3/desk.xbm \ + themes/Natura/openbox-3/iconify_hover.xbm \ + themes/Natura/openbox-3/iconify.xbm \ + themes/Natura/openbox-3/max_hover.xbm \ + themes/Natura/openbox-3/max_toggled.xbm \ + themes/Natura/openbox-3/max.xbm \ + themes/Natura/openbox-3/shade.xbm \ + themes/Natura/openbox-3/shade_hover.xbm \ + themes/Natura/openbox-3/themerc + +artwizboxed_themedir = $(themedir)/Artwiz-boxed/openbox-3 +dist_artwizboxed_theme_DATA= \ + themes/Artwiz-boxed/openbox-3/themerc + +bear2_themedir = $(themedir)/Bear2/openbox-3 +dist_bear2_theme_DATA= \ + themes/Bear2/openbox-3/close_pressed.xbm \ + themes/Bear2/openbox-3/close.xbm \ + themes/Bear2/openbox-3/desk_toggled.xbm \ + themes/Bear2/openbox-3/desk.xbm \ + themes/Bear2/openbox-3/iconify_pressed.xbm \ + themes/Bear2/openbox-3/iconify.xbm \ + themes/Bear2/openbox-3/max_pressed.xbm \ + themes/Bear2/openbox-3/max_toggled.xbm \ + themes/Bear2/openbox-3/max.xbm \ + themes/Bear2/openbox-3/shade_pressed.xbm \ + themes/Bear2/openbox-3/shade.xbm \ + themes/Bear2/openbox-3/themerc + +orang_themedir = $(themedir)/Orang/openbox-3 +dist_orang_theme_DATA= \ + themes/Orang/openbox-3/themerc + +onyx_themedir = $(themedir)/Onyx/openbox-3 +dist_onyx_theme_DATA= \ + themes/Onyx/openbox-3/themerc + +onyxcitrus_themedir = $(themedir)/Onyx-Citrus/openbox-3 +dist_onyxcitrus_theme_DATA= \ + themes/Onyx-Citrus/openbox-3/themerc + +syscrash_themedir = $(themedir)/Syscrash/openbox-3 +dist_syscrash_theme_DATA= \ + themes/Syscrash/openbox-3/max_disabled.xbm \ + themes/Syscrash/openbox-3/max_pressed.xbm \ + themes/Syscrash/openbox-3/max_toggled.xbm \ + themes/Syscrash/openbox-3/max.xbm \ + themes/Syscrash/openbox-3/themerc + +## public headers ## + +rrpubinclude_HEADERS = \ + obrender/color.h \ + obrender/font.h \ + obrender/geom.h \ + obrender/gradient.h \ + obrender/image.h \ + obrender/instance.h \ + obrender/mask.h \ + obrender/render.h \ + obrender/theme.h \ + obrender/version.h + +obtpubinclude_HEADERS = \ + obt/link.h \ + obt/display.h \ + obt/keyboard.h \ + obt/xml.h \ + obt/paths.h \ + obt/prop.h \ + obt/signal.h \ + obt/util.h \ + obt/version.h \ + obt/xqueue.h + +nodist_pkgconfig_DATA = \ + obrender/obrender-3.5.pc \ + obt/obt-3.5.pc + +## data ## + +dist_apps_DATA = \ + data/openbox.desktop + +dist_pixmap_DATA = \ + data/openbox.png + +dist_rc_DATA = \ + data/rc.xml \ + data/menu.xml + +edit = $(SED) \ + -e 's!@version\@!$(VERSION)!' \ + -e 's!@configdir\@!$(configdir)!' \ + -e 's!@rcdir\@!$(rcdir)!' \ + -e 's!@libexecdir\@!$(libexecdir)!' \ + -e 's!@bindir\@!$(bindir)!' + +data/autostart/autostart: $(top_srcdir)/data/autostart/autostart.in Makefile + @echo make: creating $@ + @test -d $(shell dirname $(top_builddir)/$@) || \ + mkdir $(shell dirname $(top_builddir)/$@) + @$(edit) $< >$(top_builddir)/$@ + +data/autostart/openbox-autostart: $(top_srcdir)/data/autostart/openbox-autostart.in Makefile + @echo make: creating $@ + @test -d $(shell dirname $(top_builddir)/$@) || \ + mkdir $(shell dirname $(top_builddir)/$@) + @$(edit) $< >$(top_builddir)/$@ + +%.desktop: %.desktop.in Makefile + @echo make: creating $@ + @$(edit) $< >$@ + +%-session: %-session.in Makefile + @echo make: creating $@ + @$(edit) $< >$@ + +%.1.in: %.1.sgml + @echo make: creating $@ + @docbook-to-man $< >$@ + +%.1: %.1.in Makefile + @echo make: creating $@ + @$(edit) $< >$@ + +dist_gnomewmfiles_DATA = \ + data/gnome-wm-properties/openbox.desktop + +nodist_xsessions_DATA = \ + data/xsession/openbox.desktop \ + data/xsession/openbox-gnome.desktop \ + data/xsession/openbox-kde.desktop + +dist_gnomesession_DATA = \ + data/gnome-session/openbox-gnome.session \ + data/gnome-session/openbox-gnome-fallback.session + +dist_noinst_DATA = \ + data/rc.xsd \ + data/menu.xsd \ + data/autostart/autostart.in \ + data/autostart/openbox-autostart.in \ + data/xsession/openbox.desktop.in \ + data/xsession/openbox-gnome.desktop.in \ + data/xsession/openbox-kde.desktop.in \ + data/xsession/openbox-session.in \ + data/xsession/openbox-gnome-session.in \ + data/xsession/openbox-kde-session.in \ + doc/openbox.1.sgml \ + doc/openbox.1.in \ + doc/openbox-session.1.sgml \ + doc/openbox-session.1.in \ + doc/openbox-gnome-session.1.sgml \ + doc/openbox-gnome-session.1.in \ + doc/openbox-kde-session.1.sgml \ + doc/openbox-kde-session.1.in \ + doc/obxprop.1.sgml \ + doc/obxprop.1.in \ + obrender/version.h.in \ + obrender/obrender-3.5.pc.in \ + obt/obt-3.5.pc.in \ + obt/version.h.in \ + tools/themeupdate/themeupdate.py \ + tests/hideshow.py \ + tests/Makefile \ + tests/aspect.c \ + tests/fullscreen.c \ + tests/grav.c \ + tests/grouptran.c \ + tests/icons.c \ + tests/modal2.c \ + tests/modal3.c \ + tests/modal.c \ + tests/noresize.c \ + tests/override.c \ + tests/positioned.c \ + tests/strut.c \ + tests/title.c \ + tests/urgent.c + +dist_doc_DATA = \ + COMPLIANCE \ + README \ + AUTHORS \ + CHANGELOG \ + COPYING \ + data/rc.xsd \ + data/menu.xsd \ + doc/rc-mouse-focus.xml + +nodist_man_MANS = \ + doc/openbox.1 \ + doc/openbox-session.1 \ + doc/openbox-gnome-session.1 \ + doc/openbox-kde-session.1 \ + doc/obxprop.1 + +EXTRA_DIST = \ + config.rpath + +# make clean doesn't delete these for some reason, even though they are +# built by make +CLEANFILES = \ + $(nodist_man_MANS) \ + $(nodist_bin_SCRIPTS) \ + $(nodist_xsessions_DATA) \ + $(nodist_rc_SCRIPTS) \ + $(nodist_libexec_SCRIPTS) + +#doc: +# $(MAKE) -$(MAKEFLAGS) -C doc/doxygen doc + +distclean-local: + for d in . m4 po obrender parser obt openbox; do \ + for p in core core.* gmon.out *\~ *.orig *.rej .\#*; do \ + rm -f "$$d/$$p"; \ + done \ + done + +.PHONY: doc @@ -0,0 +1,23 @@ +Openbox +Copyright (C) 2004-2007 Mikael Magnusson +Copyright (C) 2002-2007 Dana Jansens + +---- + + This software is OSI Certified Open Source Software. + OSI Certified is a certification mark of the Open Source Initiative. + +---- + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. + diff --git a/README.GIT b/README.GIT new file mode 100644 index 0000000..a6ad99b --- /dev/null +++ b/README.GIT @@ -0,0 +1,61 @@ +To build Openbox from git you need: + +A C Compiler (GNU GCC 3.2+ suggested) +-GNU Gettext 0.14.4 +-GNU Autoconf 2.50+ +-GNU Automake 1.11+ +-GNU Libtool +-Xlib library/headers (devel package) +-Pkg-Config +-Glib 2.0+ library/headers (devel package) (http://www.gtk.org) +-libxml2 2.0+ library/headers (devel package) +-Pango 1.10+ library/headers (devel package) +-cvs or git or neither, depending on how gettext was installed* + +Also you will probably want: +-Imlib2 library/headers (devel package) +-X Cursor library/headers (devel package) +-Startup Notification library/headers 0.8+ (devel package) + +We recommend the latest versions of all these packages. + +*) for versions below 0.18.1 cvs is needed, above that, see autopoint --version + +Do the following to build and install Openbox in git: + +% ./bootstrap +% ./configure +% make +su to root and +% make install +or +% sudo make install + +Don't try running it from the openbox/ directory without installing, it won't +work. It needs to be installed before it is run. + +The following commands will be available: openbox-session, + openbox-gnome-session, openbox-kde-session, and openbox. + +See the man pages for details about them. If you want to run Openbox on its + own, you probably want to run "openbox-session". + + + +---- +In Ubuntu, you want these packages: + +gcc +gettext +automake +autoconf +libtool +libpango1.0-dev +pkg-config +libglib2.0-dev +libxml2-dev +libstartup-notification0-dev +xorg-dev +libimlib2-dev + +---- diff --git a/README.NLS b/README.NLS new file mode 100644 index 0000000..7495afd --- /dev/null +++ b/README.NLS @@ -0,0 +1,3 @@ +Use this command to create a new .po + +msginit --locale xx_YY file.pot diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..6a5b10f --- /dev/null +++ b/bootstrap @@ -0,0 +1,18 @@ +#! /bin/sh + +sh() { + /bin/sh -c "set -x; $*" +} + +export WANT_AUTOMAKE=1.9 + +sh autopoint --force || exit 1 # for GNU gettext +sh libtoolize --copy --force --automake || exit 1 +sh aclocal -I m4 $ACLOCAL_FLAGS || exit 1 +#sh autoheader || exit 1 +sh autoconf || exit 1 +sh automake --include-deps --add-missing --copy || exit 1 + +echo +echo You are now ready to run ./configure +echo enjoy! diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..a183309 --- /dev/null +++ b/configure.ac @@ -0,0 +1,238 @@ +AC_PREREQ([2.54]) +AC_INIT([openbox], [3.5.0], [http://bugzilla.icculus.org]) +AC_CONFIG_SRCDIR([openbox/openbox.c]) + +AM_INIT_AUTOMAKE([foreign]) +AM_SILENT_RULES([yes]) + +OB_VERSION=$PACKAGE_VERSION +AC_SUBST(OB_VERSION) + +dnl Making releases: +dnl RR_MICRO_VERSION += 1; +dnl RR_INTERFACE_AGE += 1; +dnl R_BINARY_AGE += 1; +dnl if any functions have been added, set RR_INTERFACE_AGE to 0. +dnl if backwards compatibility has been broken, +dnl set RR_BINARY_AGE and RR_INTERFACE_AGE to 0. +dnl +dnl if MAJOR or MINOR version changes, be sure to change AC_INIT above to match +dnl +RR_MAJOR_VERSION=3 +RR_MINOR_VERSION=5 +RR_MICRO_VERSION=28 +RR_INTERFACE_AGE=1 +RR_BINARY_AGE=1 +RR_VERSION=$RR_MAJOR_VERSION.$RR_MINOR_VERSION + +OBT_MAJOR_VERSION=3 +OBT_MINOR_VERSION=5 +OBT_MICRO_VERSION=1 +OBT_INTERFACE_AGE=1 +OBT_BINARY_AGE=1 +OBT_VERSION=$OBT_MAJOR_VERSION.$OBT_MINOR_VERSION + +AC_SUBST(RR_MAJOR_VERSION) +AC_SUBST(RR_MINOR_VERSION) +AC_SUBST(RR_MICRO_VERSION) +AC_SUBST(RR_INTERFACE_AGE) +AC_SUBST(RR_BINARY_AGE) +AC_SUBST(RR_VERSION) +AC_SUBST(OBT_MAJOR_VERSION) +AC_SUBST(OBT_MINOR_VERSION) +AC_SUBST(OBT_MICRO_VERSION) +AC_SUBST(OBT_INTERFACE_AGE) +AC_SUBST(OBT_BINARY_AGE) +AC_SUBST(OBT_VERSION) + +dnl Libtool versioning +RR_RELEASE=$RR_MAJOR_VERSION.$RR_MINOR_VERSION +RR_CURRENT=`expr $RR_MICRO_VERSION - $RR_INTERFACE_AGE` +RR_REVISION=$RR_INTERFACE_AGE +RR_AGE=`expr $RR_BINARY_AGE - $RR_INTERFACE_AGE` +RR_CURRENT_MINUS_AGE=`expr $RR_CURRENT - $RR_AGE` + +OBT_RELEASE=$OBT_MAJOR_VERSION.$OBT_MINOR_VERSION +OBT_CURRENT=`expr $OBT_MICRO_VERSION - $OBT_INTERFACE_AGE` +OBT_REVISION=$OBT_INTERFACE_AGE +OBT_AGE=`expr $OBT_BINARY_AGE - $OBT_INTERFACE_AGE` +OBT_CURRENT_MINUS_AGE=`expr $OBT_CURRENT - $OBT_AGE` + +AC_SUBST(RR_RELEASE) +AC_SUBST(RR_CURRENT) +AC_SUBST(RR_REVISION) +AC_SUBST(RR_AGE) +AC_SUBST(RR_CURRENT_MINUS_AGE) +AC_SUBST(OBT_RELEASE) +AC_SUBST(OBT_CURRENT) +AC_SUBST(OBT_REVISION) +AC_SUBST(OBT_AGE) +AC_SUBST(OBT_CURRENT_MINUS_AGE) + +AC_PREFIX_DEFAULT([/usr/local]) +test "$prefix" = "NONE" && prefix=$ac_default_prefix + +dnl Determine build target +OB_DEBUG +dnl Pick compiler specific/build target flags, and set $CVS +AM_PROG_CC_C_O +OB_COMPILER_FLAGS +AC_C_CONST +AC_C_INLINE + +AC_PROG_LIBTOOL +AC_SUBST(LIBTOOL_DEPS) +LIBTOOL="$LIBTOOL --silent" + +AC_PROG_INSTALL + +AM_GNU_GETTEXT_VERSION(0.15) +AM_GNU_GETTEXT([external]) + +AC_CHECK_HEADERS(ctype.h dirent.h errno.h fcntl.h grp.h locale.h pwd.h) +AC_CHECK_HEADERS(signal.h string.h stdio.h stdlib.h unistd.h sys/stat.h) +AC_CHECK_HEADERS(sys/select.h sys/socket.h sys/time.h sys/types.h sys/wait.h) + +AC_PATH_PROG([SED], [sed], [no]) +if test "$SED" = "no"; then + AC_MSG_ERROR([The program "sed" is not available. This program is required to build Openbox.]) +fi + +AC_PATH_PROG([DIRNAME], [dirname], [no]) +if test "$DIRNAME" = "no"; then + AC_MSG_ERROR([The program "dirname" is not available. This program is required to build Openbox.]) +fi + +PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.14.0]) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +PKG_CHECK_MODULES(PANGO, [pango >= 1.8.0 pangoxft >= 1.8.0]) +AC_SUBST(PANGO_CFLAGS) +AC_SUBST(PANGO_LIBS) + +PKG_CHECK_MODULES(XML, [libxml-2.0 >= 2.6.0]) +AC_SUBST(XML_CFLAGS) +AC_SUBST(XML_LIBS) + +AC_ARG_ENABLE(startup-notification, + AC_HELP_STRING( + [--disable-startup-notification], + [disable the startup notification library. [default=enabled]] + ), + [enable_sn=$enableval], + [enable_sn=yes] +) + +if test "$enable_sn" = yes; then +PKG_CHECK_MODULES(LIBSN, [libstartup-notification-1.0 >= 0.8], + [ + AC_DEFINE(USE_LIBSN, [1], [Use startup-notification]) + AC_SUBST(LIBSN_CFLAGS) + AC_SUBST(LIBSN_LIBS) + sn_found=yes + ], + [ + sn_found=no + ] +) +else + sn_found=no +fi + +AC_ARG_ENABLE(xcursor, + AC_HELP_STRING( + [--disable-xcursor], + [disable use of the X Cursor library. [default=enabled]] + ), + [enable_xcursor=$enableval], + [enable_xcursor=yes] +) + +if test "$enable_xcursor" = yes; then +PKG_CHECK_MODULES(XCURSOR, [xcursor], + [ + AC_DEFINE(USE_XCURSOR, [1], [Use X Cursor library]) + AC_SUBST(XCURSOR_CFLAGS) + AC_SUBST(XCURSOR_LIBS) + xcursor_found=yes + ], + [ + xcursor_found=no + ] +) +else + xcursor_found=no +fi + +AC_ARG_ENABLE(imlib2, + AC_HELP_STRING( + [--disable-imlib2], + [disable use of Imlib2 image library for loading icons. [default=enabled]] + ), + [enable_imlib2=$enableval], + [enable_imlib2=yes] +) + +if test "$enable_imlib2" = yes; then +PKG_CHECK_MODULES(IMLIB2, [imlib2], + [ + AC_DEFINE(USE_IMLIB2, [1], [Use Imlib2 image library]) + AC_SUBST(IMLIB2_CFLAGS) + AC_SUBST(IMLIB2_LIBS) + # export it for the pkg-config file + PKG_CONFIG_IMLIB=imlib2 + AC_SUBST(PKG_CONFIG_IMLIB) + imlib2_found=yes + ], + [ + imlib2_found=no + ] +) +else + imlib2_found=no +fi + +AM_CONDITIONAL(USE_IMLIB2, [test $imlib2_found = yes]) + +dnl Check for session management +X11_SM + +#EFENCE_LIBS=-lefence +EFENCE_LIBS="" +AC_SUBST(EFENCE_LIBS) + +dnl Check for X11 extensions +X11_EXT_XKB +X11_EXT_XRANDR +X11_EXT_SHAPE +X11_EXT_XINERAMA +X11_EXT_SYNC +X11_EXT_AUTH + +AC_CONFIG_FILES([ + Makefile + m4/Makefile + po/Makefile.in + obrender/obrender-3.5.pc + obt/obt-3.5.pc + obrender/version.h + obt/version.h + version.h +]) +AC_CONFIG_COMMANDS([doc], + [test -d doc || mkdir doc]) +AC_CONFIG_COMMANDS([data], + [test -d data || mkdir data]) +AC_CONFIG_COMMANDS([data/xsession], + [test -d data/xsession || mkdir data/xsession]) +AC_OUTPUT + +AC_MSG_RESULT +AC_MSG_RESULT([Compiling with these options: + Startup Notification... $sn_found + X Cursor Library... $xcursor_found + Session Management... $SM + Imlib2 library... $imlib2_found + ]) +AC_MSG_RESULT([configure complete, now type "make"]) diff --git a/data/Makefile b/data/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/data/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/data/autostart/Makefile b/data/autostart/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/data/autostart/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/data/autostart/autostart.in b/data/autostart/autostart.in new file mode 100644 index 0000000..1c261a0 --- /dev/null +++ b/data/autostart/autostart.in @@ -0,0 +1,17 @@ +# +# These things are run when an Openbox X Session is started. +# You may place a similar script in $HOME/.config/openbox/autostart +# to run user-specific things. +# + +# If you want to use GNOME config tools... +# +#if test -x @libexecdir@/gnome-settings-daemon >/dev/null; then +# @libexecdir@/gnome-settings-daemon & +#elif which gnome-settings-daemon >/dev/null; then +# gnome-settings-daemon & +#fi + +# If you want to use XFCE config tools... +# +#xfce-mcs-manager & diff --git a/data/autostart/openbox-autostart.in b/data/autostart/openbox-autostart.in new file mode 100755 index 0000000..063c635 --- /dev/null +++ b/data/autostart/openbox-autostart.in @@ -0,0 +1,34 @@ +#!/bin/sh + +# Set a background color +BG="" +if which hsetroot >/dev/null; then + BG=hsetroot +elif which esetroot >/dev/null; then + BG=esetroot +elif which xsetroot >/dev/null; then + BG=xsetroot +fi +test -z $BG || $BG -solid "#303030" + +GLOBALAUTOSTART="@rcdir@/autostart" +AUTOSTART="${XDG_CONFIG_HOME:-"$HOME/.config"}/openbox/autostart" + +# Run the global openbox autostart script +if test -f $GLOBALAUTOSTART; then + sh $GLOBALAUTOSTART +elif test -f $GLOBALAUTOSTART.sh; then + sh $GLOBALAUTOSTART.sh +fi + +# Run the user openbox autostart script +if test -f $AUTOSTART; then + sh $AUTOSTART +elif test -f $AUTOSTART.sh; then + sh $AUTOSTART.sh +fi + +# Run the XDG autostart stuff. These are found in /etc/xdg/autostart and +# in $HOME/.config/autostart. This requires PyXDG to be installed. +# See openbox-xdg-autostart --help for more details. +@libexecdir@/openbox-xdg-autostart "$@" diff --git a/data/autostart/openbox-xdg-autostart b/data/autostart/openbox-xdg-autostart new file mode 100755 index 0000000..ea76028 --- /dev/null +++ b/data/autostart/openbox-xdg-autostart @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +# openbox-xdg-autostart runs things based on the XDG autostart specification +# Copyright (C) 2008 Dana Jansens +# +# XDG autostart specification can be found here: +# http://standards.freedesktop.org/autostart-spec/ +# +# +# +# LICENSE: +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +ME="openbox-xdg-autostart" +VERSION="1.1" + +import os, glob, sys +try: + from xdg import BaseDirectory + from xdg.DesktopEntry import DesktopEntry + from xdg.Exceptions import ParsingError +except ImportError: + print + print "ERROR:", ME, "requires PyXDG to be installed" + print + sys.exit(1) + +def main(argv=sys.argv): + if "--help" in argv[1:]: + show_help() + return 0 + if "--version" in argv[1:]: + show_version() + return 0 + + # get the autostart directories + autodirs = BaseDirectory.load_config_paths("autostart") + + # find all the autostart files + files = [] + for dir in autodirs: + for path in glob.glob(os.path.join(dir, '*.desktop')): + try: + autofile = AutostartFile(path) + except ParsingError: + print "Invalid .desktop file: " + path + else: + if not autofile in files: + files.append(autofile) + + list = False + if "--list" in argv[1:]: + list = True + argv.remove("--list") + + # run them ! + environments = argv[1:] + for autofile in files: + if list: autofile.display(environments) + else: autofile.run(environments) + +class AutostartFile: + def __init__(self, path): + self.path = path + self.filename = os.path.basename(path) + self.dirname = os.path.dirname(path) + self.de = DesktopEntry(path) + + def __eq__(self, other): + return self.filename == other.filename + + def __str__(self): + return self.path + " : " + self.de.getName() + + def _isexecfile(self, path): + return os.access(path, os.X_OK) + + def _findFile(self, path, search, match_func): + # check empty path + if not path: return None + # check absolute path + if path[0] == '/': + if match_func(path): return path + else: return None + else: + # check relative path + for dirname in search.split(os.pathsep): + if dirname != "": + candidate = os.path.join(dirname, path) + if (match_func(candidate)): return candidate + + def _alert(self, str, info=False): + if info: + print "\t ", str + else: + print "\t*", str + + def _showInEnvironment(self, envs, verbose=False): + default = not self.de.getOnlyShowIn() + noshow = False + force = False + for i in self.de.getOnlyShowIn(): + if i in envs: force = True + for i in self.de.getNotShowIn(): + if i in envs: noshow = True + + if verbose: + if not default and not force: + s = "" + for i in self.de.getOnlyShowIn(): + if s: s += ", " + s += i + self._alert("Excluded by: OnlyShowIn (" + s + ")") + if default and noshow and not force: + s = "" + for i in self.de.getNotShowIn(): + if s: s += ", " + s += i + self._alert("Excluded by: NotShowIn (" + s + ")") + return (default and not noshow) or force + + def _shouldRun(self, envs, verbose=False): + if not self.de.getExec(): + if verbose: self._alert("Excluded by: Missing Exec field") + return False + if self.de.getHidden(): + if verbose: self._alert("Excluded by: Hidden") + return False + if self.de.getTryExec(): + if not self._findFile(self.de.getTryExec(), os.getenv("PATH"), + self._isexecfile): + if verbose: self._alert("Excluded by: TryExec (" + + self.de.getTryExec() + ")") + return False + if not self._showInEnvironment(envs, verbose): + return False + return True + + def display(self, envs): + if self._shouldRun(envs): + print "[*] " + self.de.getName() + else: + print "[ ] " + self.de.getName() + self._alert("File: " + self.path, info=True) + if self.de.getExec(): + self._alert("Executes: " + self.de.getExec(), info=True) + self._shouldRun(envs, True) + print + + def run(self, envs): + here = os.getcwd() + if self.de.getPath(): + os.chdir(self.de.getPath()) + if self._shouldRun(envs): + args = ["/bin/sh", "-c", "exec " + self.de.getExec()] + os.spawnv(os.P_NOWAIT, args[0], args); + os.chdir(here) + +def show_help(): + print "Usage:", ME, "[OPTION]... [ENVIRONMENT]..." + print + print "This tool will run xdg autostart .desktop files" + print + print "OPTIONS" + print " --list Show a list of the files which would be run" + print " Files which would be run are marked with an asterix" + print " symbol [*]. For files which would not be run," + print " information is given for why they are excluded" + print " --help Show this help and exit" + print " --version Show version and copyright information" + print + print "ENVIRONMENT specifies a list of environments for which to run autostart" + print "applications. If none are specified, only applications which do not " + print "limit themselves to certain environments will be run." + print + print "ENVIRONMENT can be one or more of:" + print " GNOME Gnome Desktop" + print " KDE KDE Desktop" + print " ROX ROX Desktop" + print " XFCE XFCE Desktop" + print " Old Legacy systems" + print + +def show_version(): + print ME, VERSION + print "Copyright (c) 2008 Dana Jansens" + print + +if __name__ == "__main__": + sys.exit(main()) diff --git a/data/environment b/data/environment new file mode 100644 index 0000000..3311bd6 --- /dev/null +++ b/data/environment @@ -0,0 +1,10 @@ +# +# Set system-wide environment variables here for Openbox +# User-specific variables should be placed in $HOME/.config/openbox/environment +# + +# To set your language for displaying messages and time/date formats, use the following: +#LANG=en_CA.UTF8 + +# To set your keyboard layout, you need to modify your X config: +# http://www.google.com/search?q=how+to+set+keyboard+layout+xorg diff --git a/data/gnome-session/Makefile b/data/gnome-session/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/data/gnome-session/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/data/gnome-session/openbox-gnome-fallback.session b/data/gnome-session/openbox-gnome-fallback.session new file mode 100644 index 0000000..156f2c3 --- /dev/null +++ b/data/gnome-session/openbox-gnome-fallback.session @@ -0,0 +1,6 @@ +[GNOME Session] +Name=GNOME/Openbox fallback (Safe Mode) +RequiredComponents=gnome-settings-daemon; +RequiredProviders=windowmanager; +DefaultProvider-windowmanager=openbox +DesktopName=GNOME diff --git a/data/gnome-session/openbox-gnome.session b/data/gnome-session/openbox-gnome.session new file mode 100644 index 0000000..3399c2c --- /dev/null +++ b/data/gnome-session/openbox-gnome.session @@ -0,0 +1,9 @@ +[GNOME Session] +Name=GNOME/Openbox +RequiredComponents=gnome-settings-daemon; +# Try load with the gnome-panel and use the fallback if we can't load a panel +RequiredProviders=windowmanager;panel +DefaultProvider-windowmanager=openbox +DefaultProvider-panel=gnome-panel +FallbackSession=openbox-gnome-fallback +DesktopName=GNOME diff --git a/data/gnome-wm-properties/Makefile b/data/gnome-wm-properties/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/data/gnome-wm-properties/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/data/gnome-wm-properties/openbox.desktop b/data/gnome-wm-properties/openbox.desktop new file mode 100644 index 0000000..67e49e4 --- /dev/null +++ b/data/gnome-wm-properties/openbox.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +Name=Openbox +Exec=openbox + +# name we put on the WM spec check window +X-GNOME-WMName=Openbox + +# our config tool +ConfigExec=obconf + +[Window Manager] +SessionManaged=true diff --git a/data/menu.xml b/data/menu.xml new file mode 100644 index 0000000..39da04d --- /dev/null +++ b/data/menu.xml @@ -0,0 +1,394 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<openbox_menu xmlns="http://openbox.org/3.4/menu"> + +<menu id="apps-accessories-menu" label="Accessories"> + <item label="Calculator"> + <action name="Execute"> + <command>gnome-calculator</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Character Map"> + <action name="Execute"> + <command>gnome-character-map</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Ark File Archiver"> + <action name="Execute"> + <command>ark</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="apps-editors-menu" label="Editors"> + <item label="GVim"> + <action name="Execute"> + <command>gvim</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>GVim</wmclass> + </startupnotify> + </action> + </item> + <item label="Emacs"> + <action name="Execute"> + <command>emacs</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>Emacs</wmclass> + </startupnotify> + </action> + </item> + <item label="GEdit"> + <action name="Execute"> + <command>gedit</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Kate"> + <action name="Execute"> + <command>kate</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Kwrite"> + <action name="Execute"> + <command>kwrite</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="apps-term-menu" label="Terminals"> + <item label="Rxvt Unicode"> + <action name="Execute"> + <command>urxvt</command> + </action> + </item> + <item label="Gnome Terminal"> + <action name="Execute"> + <command>gnome-terminal</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Xfce Terminal"> + <action name="Execute"> + <command>xfce4-terminal</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Konsole"> + <action name="Execute"> + <command>konsole</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Xterm"> + <action name="Execute"><command>xterm</command></action> + </item> +</menu> + +<menu id="apps-net-menu" label="Internet"> + <item label="Firefox"> + <action name="Execute"> + <command>firefox</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>Firefox</wmclass> + </startupnotify> + </action> + </item> + <item label="Opera"> + <action name="Execute"> + <command>opera</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>Opera</wmclass> + </startupnotify> + </action> + </item> + <item label="Konqueror"> + <action name="Execute"> + <command>konqueror</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Epiphany"> + <action name="Execute"> + <command>epiphany</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Pidgin Instant Messenger"> + <action name="Execute"> + <command>pidgin</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Kopete Instant Messenger"> + <action name="Execute"> + <command>kopete</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="XChat"> + <action name="Execute"> + <command>xchat</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="apps-office-menu" label="Office"> + <item label="OpenOffice Base"> + <action name="Execute"> + <command>ooffice -base</command> + </action> + </item> + <item label="OpenOffice Calc"> + <action name="Execute"> + <command>ooffice -calc</command> + </action> + </item> + <item label="OpenOffice Draw"> + <action name="Execute"> + <command>ooffice -draw</command> + </action> + </item> + <item label="OpenOffice Impress"> + <action name="Execute"> + <command>ooffice -impress</command> + </action> + </item> + <item label="OpenOffice Math"> + <action name="Execute"> + <command>ooffice -math</command> + </action> + </item> + <item label="OpenOffice Printer Administration"> + <action name="Execute"> + <command>ooffice-printeradmin</command> + </action> + </item> + <item label="OpenOffice Writer"> + <action name="Execute"> + <command>ooffice -writer</command> + </action> + </item> +</menu> + +<menu id="apps-multimedia-menu" label="Multimedia"> + <item label="Amarok"> + <action name="Execute"> + <command>amarok</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Rhythmbox"> + <action name="Execute"> + <command>rhythmbox</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="K3b"> + <action name="Execute"> + <command>k3b</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="MPlayer"> + <action name="Execute"> + <command>gmplayer</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>MPlayer</wmclass> + </startupnotify> + </action> + </item> + <item label="Totem"> + <action name="Execute"> + <command>totem</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="apps-fileman-menu" label="File Managers"> + <item label="Nautilus"> + <action name="Execute"> + <command>nautilus --no-desktop --browser</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Thunar"> + <action name="Execute"> + <command>Thunar</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="KDE File Manager"> + <action name="Execute"> + <command>kfmclient openURL ~</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Rox"> + <action name="Execute"> + <command>rox</command> + <startupnotify> + <enabled>yes</enabled> + <wmclass>ROX-Filer</wmclass> + </startupnotify> + </action> + </item> + <item label="PCMan File Manager"> + <action name="Execute"> + <command>pcmanfm</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="apps-graphics-menu" label="Graphics"> + <item label="Gimp"> + <action name="Execute"> + <command>gimp</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Gwenview"> + <action name="Execute"> + <command>gwenview</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Dia Diagram Editor"> + <action name="Execute"> + <command>dia</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> + <item label="Inkscape"> + <action name="Execute"> + <command>inkscape</command> + <startupnotify> + <enabled>yes</enabled> + </startupnotify> + </action> + </item> +</menu> + +<menu id="system-menu" label="System"> + <item label="Openbox Configuration Manager"> + <action name="Execute"> + <command>obconf</command> + <startupnotify><enabled>yes</enabled></startupnotify> + </action> + </item> + <item label="Gnome Control Center"> + <action name="Execute"> + <command>gnome-control-center</command> + <startupnotify><enabled>yes</enabled></startupnotify> + </action> + </item> + <item label="KDE Control Center"> + <action name="Execute"> + <command>kcontrol</command> + <startupnotify><enabled>yes</enabled></startupnotify> + </action> + </item> + <item label="Xfce Settings"> + <action name="Execute"> + <command>xfce-setting-show</command> + <startupnotify><enabled>yes</enabled></startupnotify> + </action> + </item> + <item label="Manage Cups Printers"> + <action name="Execute"> + <command>xdg-open http://localhost:631/</command> + <startupnotify> + <enabled>no</enabled> + <icon>cups</icon> + </startupnotify> + </action> + </item> + <separator /> + <item label="Reconfigure Openbox"> + <action name="Reconfigure" /> + </item> +</menu> + +<menu id="root-menu" label="Openbox 3"> + <separator label="Applications" /> + <menu id="apps-accessories-menu"/> + <menu id="apps-editors-menu"/> + <menu id="apps-graphics-menu"/> + <menu id="apps-net-menu"/> + <menu id="apps-office-menu"/> + <menu id="apps-multimedia-menu"/> + <menu id="apps-term-menu"/> + <menu id="apps-fileman-menu"/> + <separator label="System" /> + <menu id="system-menu"/> + <separator /> + <item label="Log Out"> + <action name="Exit"> + <prompt>yes</prompt> + </action> + </item> +</menu> + +</openbox_menu> diff --git a/data/menu.xsd b/data/menu.xsd new file mode 100644 index 0000000..2ff76b7 --- /dev/null +++ b/data/menu.xsd @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- XML Schema for the Openbox window manager menu file --> + +<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" + "http://www.w3.org/2001/XMLSchema.dtd" [ +<!ATTLIST schema xmlns:ob CDATA #IMPLIED> +<!ENTITY % p "xsd:"> +<!ENTITY % s ":xsd"> +]> + +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://openbox.org/3.4/menu" + xmlns:ob="http://openbox.org/3.4/menu" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + <!-- + root node + --> + <xsd:element name="openbox_menu"> + <xsd:complexType mixed="false"> + <xsd:sequence maxOccurs="unbounded" minOccurs="1"> + <xsd:element name="menu" type="ob:menu"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <!-- + complex types + --> + <!-- menu --> + <xsd:complexType name="menu"> + <xsd:choice maxOccurs="unbounded" minOccurs="0"> + <xsd:element name="menu" type="ob:menu"/> + <xsd:element name="item" type="ob:item"/> + <xsd:element name="separator" type="ob:separator"/> + </xsd:choice> + <xsd:attribute name="label" type="xsd:string" use="optional"/> + <xsd:attribute name="execute" type="xsd:string" use="optional"/> + <xsd:attribute name="id" type="xsd:string" use="required"/> + </xsd:complexType> + + <!-- separator --> + <xsd:complexType name="separator"> + <xsd:attribute name="label" type="xsd:string" use="optional"/> + </xsd:complexType> + + <!-- empty --> + <xsd:complexType name="empty"> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"/> + </xsd:complexContent> + </xsd:complexType> + + <!-- item --> + <xsd:complexType name="item"> + <xsd:sequence> + <xsd:element name="action"> + <xsd:complexType> + <xsd:all> + <xsd:element minOccurs="0" name="execute" type="xsd:string"/> + <xsd:element minOccurs="0" name="startupnotify" type="ob:notify"/> + <xsd:element minOccurs="0" name="command" type="xsd:string"/> + <xsd:element minOccurs="0" name="allDesktops" type="ob:bool"/> + <xsd:element minOccurs="0" name="menu" type="xsd:string"/> + <xsd:element minOccurs="0" name="delta" type="xsd:integer"/> + <xsd:element minOccurs="0" name="x" type="xsd:integer"/> + <xsd:element minOccurs="0" name="y" type="xsd:integer"/> + <xsd:element minOccurs="0" name="left" type="xsd:integer"/> + <xsd:element minOccurs="0" name="right" type="xsd:integer"/> + <xsd:element minOccurs="0" name="up" type="xsd:integer"/> + <xsd:element minOccurs="0" name="down" type="xsd:integer"/> + <xsd:element minOccurs="0" name="desktop"> + <xsd:simpleType> + <xsd:union memberTypes="xsd:integer ob:bool"/> + </xsd:simpleType> + </xsd:element> + <xsd:element minOccurs="0" name="edge" type="xsd:string"/> + <xsd:element minOccurs="0" name="wrap" type="ob:bool"/> + <xsd:element minOccurs="0" name="follow" type="ob:bool"/> + <xsd:element minOccurs="0" name="dialog" type="ob:bool"/> + <xsd:element minOccurs="0" name="panels" type="ob:bool"/> + <xsd:element minOccurs="0" name="here" type="ob:bool"/> + <xsd:element minOccurs="0" name="linear" type="ob:bool"/> + <xsd:element minOccurs="0" name="group" type="ob:bool"/> + </xsd:all> + <xsd:attribute name="name" type="ob:actionname" use="required"/> + </xsd:complexType> + </xsd:element> + </xsd:sequence> + <xsd:attribute name="label" type="xsd:string" use="required"/> + </xsd:complexType> + + <!-- startupnotify --> + <xsd:complexType name="startupnotify"> + <xsd:all> + <xsd:element minOccurs="1" name="enabled" type="xsd:string"/> + <xsd:element minOccurs="0" name="icon" type="xsd:string"/> + <xsd:element minOccurs="0" name="wmclass" type="xsd:string"/> + </xsd:all> + </xsd:complexType> + <xsd:simpleType name="bool"> + <!-- this is copied to maximization. Keep that in sync. --> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="yes"/> + <xsd:enumeration value="no"/> + <xsd:enumeration value="true"/> + <xsd:enumeration value="false"/> + <xsd:enumeration value="on"/> + <xsd:enumeration value="off"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:complexType name="notify"> + <xsd:all> + <xsd:element minOccurs="0" name="enabled" type="ob:bool"/> + <xsd:element minOccurs="0" name="name" type="xsd:string"/> + <xsd:element minOccurs="0" name="icon" type="xsd:string"/> + </xsd:all> + </xsd:complexType> + <!-- + simple types / restrictions + --> + <xsd:simpleType name="actionname"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[Aa][Cc][Tt][Ii][Vv][Aa][Tt][Ee]"/> + <xsd:pattern value="[Bb][Rr][Ee][Aa][Kk][Cc][Hh][Rr][Oo][Oo][Tt]"/> + <xsd:pattern value="[Cc][Ll][Oo][Ss][Ee]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Ee][Xx][Ee][Cc][Uu][Tt][Ee]"/> + <xsd:pattern value="[Ee][Xx][Ii][Tt]"/> + <xsd:pattern value="[Ss][Ee][Ss][Ss][Ii][Oo][Nn][Ll][Oo][Gg][Oo][Uu][Tt]"/> + <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss]"/> + <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Ii][Cc][Oo][Nn][Ii][Ff][Yy]"/> + <xsd:pattern value="[Kk][Ii][Ll][Ll]"/> + <xsd:pattern value="[Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Cc][Ee][Nn][Tt][Ee][Rr]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Nn][Ee][Xx][Tt][Ww][Ii][Nn][Dd][Oo][Ww]"/> + <xsd:pattern value="[Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss][Ww][Ii][Nn][Dd][Oo][Ww]"/> + <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee]"/> + <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee][Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Rr][Ee][Cc][Oo][Nn][Ff][Ii][Gg][Uu][Rr][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Rr][Ee][Ss][Tt][Aa][Rr][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Nn][Oo][Rr][Mm][Aa][Ll][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Tt][Oo][Pp][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee][Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Ss][Hh][Oo][Ww][Mm][Ee][Nn][Uu]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Bb][Oo][Tt][Tt][Oo][Mm]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Tt][Oo][Pp]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Ee][Cc][Oo][Rr][Aa][Tt][Ii][Oo][Nn][Ss]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Oo][Cc][Kk][Aa][Uu][Tt][Oo][Hh][Ii][Dd][Ee]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ff][Uu][Ll][Ll][Ss][Cc][Rr][Ee][Ee][Nn]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Oo][Mm][Nn][Ii][Pp][Rr][Ee][Ss][Ee][Nn][Tt]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Uu][Nn][Ff][Oo][Cc][Uu][Ss]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee][Rr][Aa][Ii][Ss][Ee]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + </xsd:restriction> + </xsd:simpleType> +</xsd:schema> diff --git a/data/openbox.desktop b/data/openbox.desktop new file mode 100644 index 0000000..d49ae22 --- /dev/null +++ b/data/openbox.desktop @@ -0,0 +1,16 @@ +[Desktop Entry] +Type=Application +Encoding=UTF-8 +Name=Openbox +Exec=openbox +Icon=openbox +NoDisplay=true +# name we put on the WM spec check window +X-GNOME-WMName=Openbox +# gnome-session autostart +X-GNOME-Autostart-Phase=WindowManager +X-GNOME-Provides=windowmanager +# Ubuntu stuff +X-Ubuntu-Gettext-Domain=openbox +# back compat +X-GNOME-Autostart-Notify=true diff --git a/data/openbox.png b/data/openbox.png Binary files differnew file mode 100644 index 0000000..70d1f07 --- /dev/null +++ b/data/openbox.png diff --git a/data/rc.xml b/data/rc.xml new file mode 100644 index 0000000..209cc2d --- /dev/null +++ b/data/rc.xml @@ -0,0 +1,730 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- Do not edit this file, it will be overwritten on install. + Copy the file to $HOME/.config/openbox/ instead. --> + +<openbox_config xmlns="http://openbox.org/3.4/rc" + xmlns:xi="http://www.w3.org/2001/XInclude"> + +<resistance> + <strength>10</strength> + <screen_edge_strength>20</screen_edge_strength> +</resistance> + +<focus> + <focusNew>yes</focusNew> + <!-- always try to focus new windows when they appear. other rules do + apply --> + <followMouse>no</followMouse> + <!-- move focus to a window when you move the mouse into it --> + <focusLast>yes</focusLast> + <!-- focus the last used window when changing desktops, instead of the one + under the mouse pointer. when followMouse is enabled --> + <underMouse>no</underMouse> + <!-- move focus under the mouse, even when the mouse is not moving --> + <focusDelay>200</focusDelay> + <!-- when followMouse is enabled, the mouse must be inside the window for + this many milliseconds (1000 = 1 sec) before moving focus to it --> + <raiseOnFocus>no</raiseOnFocus> + <!-- when followMouse is enabled, and a window is given focus by moving the + mouse into it, also raise the window --> +</focus> + +<placement> + <policy>Smart</policy> + <!-- 'Smart' or 'UnderMouse' --> + <center>yes</center> + <!-- whether to place windows in the center of the free area found or + the top left corner --> + <monitor>Primary</monitor> + <!-- with Smart placement on a multi-monitor system, try to place new windows + on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where + the active window is, 'Primary' - only on the primary monitor --> + <primaryMonitor>1</primaryMonitor> + <!-- The monitor where Openbox should place popup dialogs such as the + focus cycling popup, or the desktop switch popup. It can be an index + from 1, specifying a particular monitor. Or it can be one of the + following: 'Mouse' - where the mouse is, or + 'Active' - where the active window is --> +</placement> + +<theme> + <name>Clearlooks</name> + <titleLayout>NLIMC</titleLayout> + <!-- + available characters are NDSLIMC, each can occur at most once. + N: window icon + L: window label (AKA title). + I: iconify + M: maximize + C: close + S: shade (roll up/down) + D: omnipresent (on all desktops). + --> + <keepBorder>yes</keepBorder> + <animateIconify>yes</animateIconify> + <font place="ActiveWindow"> + <name>sans</name> + <size>8</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="InactiveWindow"> + <name>sans</name> + <size>8</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="MenuHeader"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>normal</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="MenuItem"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>normal</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="ActiveOnScreenDisplay"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="InactiveOnScreenDisplay"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> +</theme> + +<desktops> + <!-- this stuff is only used at startup, pagers allow you to change them + during a session + + these are default values to use when other ones are not already set + by other applications, or saved in your session + + use obconf if you want to change these without having to log out + and back in --> + <number>4</number> + <firstdesk>1</firstdesk> + <names> + <!-- set names up here if you want to, like this: + <name>desktop 1</name> + <name>desktop 2</name> + --> + </names> + <popupTime>875</popupTime> + <!-- The number of milliseconds to show the popup for when switching + desktops. Set this to 0 to disable the popup. --> +</desktops> + +<resize> + <drawContents>yes</drawContents> + <popupShow>Nonpixel</popupShow> + <!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) --> + <popupPosition>Center</popupPosition> + <!-- 'Center', 'Top', or 'Fixed' --> + <popupFixedPosition> + <!-- these are used if popupPosition is set to 'Fixed' --> + + <x>10</x> + <!-- positive number for distance from left edge, negative number for + distance from right edge, or 'Center' --> + <y>10</y> + <!-- positive number for distance from top edge, negative number for + distance from bottom edge, or 'Center' --> + </popupFixedPosition> +</resize> + +<!-- You can reserve a portion of your screen where windows will not cover when + they are maximized, or when they are initially placed. + Many programs reserve space automatically, but you can use this in other + cases. --> +<margins> + <top>0</top> + <bottom>0</bottom> + <left>0</left> + <right>0</right> +</margins> + +<dock> + <position>TopLeft</position> + <!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating --> + <floatingX>0</floatingX> + <floatingY>0</floatingY> + <noStrut>no</noStrut> + <stacking>Above</stacking> + <!-- 'Above', 'Normal', or 'Below' --> + <direction>Vertical</direction> + <!-- 'Vertical' or 'Horizontal' --> + <autoHide>no</autoHide> + <hideDelay>300</hideDelay> + <!-- in milliseconds (1000 = 1 second) --> + <showDelay>300</showDelay> + <!-- in milliseconds (1000 = 1 second) --> + <moveButton>Middle</moveButton> + <!-- 'Left', 'Middle', 'Right' --> +</dock> + +<keyboard> + <chainQuitKey>C-g</chainQuitKey> + + <!-- Keybindings for desktop switching --> + <keybind key="C-A-Left"> + <action name="GoToDesktop"><to>left</to><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Right"> + <action name="GoToDesktop"><to>right</to><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Up"> + <action name="GoToDesktop"><to>up</to><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Down"> + <action name="GoToDesktop"><to>down</to><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Left"> + <action name="SendToDesktop"><to>left</to><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Right"> + <action name="SendToDesktop"><to>right</to><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Up"> + <action name="SendToDesktop"><to>up</to><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Down"> + <action name="SendToDesktop"><to>down</to><wrap>no</wrap></action> + </keybind> + <keybind key="W-F1"> + <action name="GoToDesktop"><to>1</to></action> + </keybind> + <keybind key="W-F2"> + <action name="GoToDesktop"><to>2</to></action> + </keybind> + <keybind key="W-F3"> + <action name="GoToDesktop"><to>3</to></action> + </keybind> + <keybind key="W-F4"> + <action name="GoToDesktop"><to>4</to></action> + </keybind> + <keybind key="W-d"> + <action name="ToggleShowDesktop"/> + </keybind> + + <!-- Keybindings for windows --> + <keybind key="A-F4"> + <action name="Close"/> + </keybind> + <keybind key="A-Escape"> + <action name="Lower"/> + <action name="FocusToBottom"/> + <action name="Unfocus"/> + </keybind> + <keybind key="A-space"> + <action name="ShowMenu"><menu>client-menu</menu></action> + </keybind> + + <!-- Keybindings for window switching --> + <keybind key="A-Tab"> + <action name="NextWindow"> + <finalactions> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </finalactions> + </action> + </keybind> + <keybind key="A-S-Tab"> + <action name="PreviousWindow"> + <finalactions> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </finalactions> + </action> + </keybind> + <keybind key="C-A-Tab"> + <action name="NextWindow"> + <panels>yes</panels><desktop>yes</desktop> + <finalactions> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </finalactions> + </action> + </keybind> + + <!-- Keybindings for window switching with the arrow keys --> + <keybind key="W-S-Right"> + <action name="DirectionalCycleWindows"> + <direction>right</direction> + </action> + </keybind> + <keybind key="W-S-Left"> + <action name="DirectionalCycleWindows"> + <direction>left</direction> + </action> + </keybind> + <keybind key="W-S-Up"> + <action name="DirectionalCycleWindows"> + <direction>up</direction> + </action> + </keybind> + <keybind key="W-S-Down"> + <action name="DirectionalCycleWindows"> + <direction>down</direction> + </action> + </keybind> + + <!-- Keybindings for running applications --> + <keybind key="W-e"> + <action name="Execute"> + <startupnotify> + <enabled>true</enabled> + <name>Konqueror</name> + </startupnotify> + <command>kfmclient openProfile filemanagement</command> + </action> + </keybind> +</keyboard> + +<mouse> + <dragThreshold>1</dragThreshold> + <!-- number of pixels the mouse must move before a drag begins --> + <doubleClickTime>500</doubleClickTime> + <!-- in milliseconds (1000 = 1 second) --> + <screenEdgeWarpTime>400</screenEdgeWarpTime> + <!-- Time before changing desktops when the pointer touches the edge of the + screen while moving a window, in milliseconds (1000 = 1 second). + Set this to 0 to disable warping --> + <screenEdgeWarpMouse>false</screenEdgeWarpMouse> + <!-- Set this to TRUE to move the mouse pointer across the desktop when + switching due to hitting the edge of the screen --> + + <context name="Frame"> + <mousebind button="A-Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="A-Left" action="Click"> + <action name="Unshade"/> + </mousebind> + <mousebind button="A-Left" action="Drag"> + <action name="Move"/> + </mousebind> + + <mousebind button="A-Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="A-Right" action="Drag"> + <action name="Resize"/> + </mousebind> + + <mousebind button="A-Middle" action="Press"> + <action name="Lower"/> + <action name="FocusToBottom"/> + <action name="Unfocus"/> + </mousebind> + + <mousebind button="A-Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="A-Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + <mousebind button="C-A-Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="C-A-Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + <mousebind button="A-S-Up" action="Click"> + <action name="SendToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="A-S-Down" action="Click"> + <action name="SendToDesktop"><to>next</to></action> + </mousebind> + </context> + + <context name="Titlebar"> + <mousebind button="Left" action="Drag"> + <action name="Move"/> + </mousebind> + <mousebind button="Left" action="DoubleClick"> + <action name="ToggleMaximize"/> + </mousebind> + + <mousebind button="Up" action="Click"> + <action name="if"> + <shaded>no</shaded> + <then> + <action name="Shade"/> + <action name="FocusToBottom"/> + <action name="Unfocus"/> + <action name="Lower"/> + </then> + </action> + </mousebind> + <mousebind button="Down" action="Click"> + <action name="if"> + <shaded>yes</shaded> + <then> + <action name="Unshade"/> + <action name="Raise"/> + </then> + </action> + </mousebind> + </context> + + <context name="Titlebar Top Right Bottom Left TLCorner TRCorner BRCorner BLCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + + <mousebind button="Middle" action="Press"> + <action name="Lower"/> + <action name="FocusToBottom"/> + <action name="Unfocus"/> + </mousebind> + + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="Top"> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>top</edge></action> + </mousebind> + </context> + + <context name="Left"> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>left</edge></action> + </mousebind> + </context> + + <context name="Right"> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>right</edge></action> + </mousebind> + </context> + + <context name="Bottom"> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>bottom</edge></action> + </mousebind> + + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="TRCorner BRCorner TLCorner BLCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"/> + </mousebind> + </context> + + <context name="Client"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + </context> + + <context name="Icon"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="AllDesktops"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleOmnipresent"/> + </mousebind> + </context> + + <context name="Shade"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleShade"/> + </mousebind> + </context> + + <context name="Iconify"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Iconify"/> + </mousebind> + </context> + + <context name="Maximize"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleMaximize"/> + </mousebind> + <mousebind button="Middle" action="Click"> + <action name="ToggleMaximize"><direction>vertical</direction></action> + </mousebind> + <mousebind button="Right" action="Click"> + <action name="ToggleMaximize"><direction>horizontal</direction></action> + </mousebind> + </context> + + <context name="Close"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + <action name="Unshade"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Close"/> + </mousebind> + </context> + + <context name="Desktop"> + <mousebind button="Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + + <mousebind button="A-Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="A-Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + <mousebind button="C-A-Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="C-A-Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="Raise"/> + </mousebind> + </context> + + <context name="Root"> + <!-- Menus --> + <mousebind button="Middle" action="Press"> + <action name="ShowMenu"><menu>client-list-combined-menu</menu></action> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="ShowMenu"><menu>root-menu</menu></action> + </mousebind> + </context> + + <context name="MoveResize"> + <mousebind button="Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + <mousebind button="A-Up" action="Click"> + <action name="GoToDesktop"><to>previous</to></action> + </mousebind> + <mousebind button="A-Down" action="Click"> + <action name="GoToDesktop"><to>next</to></action> + </mousebind> + </context> +</mouse> + +<menu> + <!-- You can specify more than one menu file in here and they are all loaded, + just don't make menu ids clash or, well, it'll be kind of pointless --> + + <!-- default menu file (or custom one in $HOME/.config/openbox/) --> + <file>menu.xml</file> + <hideDelay>200</hideDelay> + <!-- if a press-release lasts longer than this setting (in milliseconds), the + menu is hidden again --> + <middle>no</middle> + <!-- center submenus vertically about the parent entry --> + <submenuShowDelay>100</submenuShowDelay> + <!-- time to delay before showing a submenu after hovering over the parent + entry. + if this is a negative value, then the delay is infinite and the + submenu will not be shown until it is clicked on --> + <submenuHideDelay>400</submenuHideDelay> + <!-- time to delay before hiding a submenu when selecting another + entry in parent menu --> + if this is a negative value, then the delay is infinite and the + submenu will not be hidden until a different submenu is opened --> + <applicationIcons>yes</applicationIcons> + <!-- controls if icons appear in the client-list-(combined-)menu --> + <manageDesktops>yes</manageDesktops> + <!-- show the manage desktops section in the client-list-(combined-)menu --> +</menu> + +<applications> +<!-- + # this is an example with comments through out. use these to make your + # own rules, but without the comments of course. + # you may use one or more of the name/class/role/title/type rules to specify + # windows to match + + <application name="the window's _OB_APP_NAME property (see obxprop)" + class="the window's _OB_APP_CLASS property (see obxprop)" + role="the window's _OB_APP_ROLE property (see obxprop)" + title="the window's _OB_APP_TITLE property (see obxprop)" + type="the window's _OB_APP_TYPE property (see obxprob).. + (if unspecified, then it is 'dialog' for child windows)"> + # you may set only one of name/class/role/title/type, or you may use more + # than one together to restrict your matches. + + # the name, class, role, and title use simple wildcard matching such as those + # used by a shell. you can use * to match any characters and ? to match + # any single character. + + # the type is one of: normal, dialog, splash, utility, menu, toolbar, dock, + # or desktop + + # when multiple rules match a window, they will all be applied, in the + # order that they appear in this list + + + # each rule element can be left out or set to 'default' to specify to not + # change that attribute of the window + + <decor>yes</decor> + # enable or disable window decorations + + <shade>no</shade> + # make the window shaded when it appears, or not + + <position force="no"> + # the position is only used if both an x and y coordinate are provided + # (and not set to 'default') + # when force is "yes", then the window will be placed here even if it + # says you want it placed elsewhere. this is to override buggy + # applications who refuse to behave + <x>center</x> + # a number like 50, or 'center' to center on screen. use a negative number + # to start from the right (or bottom for <y>), ie -50 is 50 pixels from the + # right edge (or bottom). + <y>200</y> + <monitor>1</monitor> + # specifies the monitor in a xinerama setup. + # 1 is the first head, or 'mouse' for wherever the mouse is + </position> + + <focus>yes</focus> + # if the window should try be given focus when it appears. if this is set + # to yes it doesn't guarantee the window will be given focus. some + # restrictions may apply, but Openbox will try to + + <desktop>1</desktop> + # 1 is the first desktop, 'all' for all desktops + + <layer>normal</layer> + # 'above', 'normal', or 'below' + + <iconic>no</iconic> + # make the window iconified when it appears, or not + + <skip_pager>no</skip_pager> + # asks to not be shown in pagers + + <skip_taskbar>no</skip_taskbar> + # asks to not be shown in taskbars. window cycling actions will also + # skip past such windows + + <fullscreen>yes</fullscreen> + # make the window in fullscreen mode when it appears + + <maximized>true</maximized> + # 'Horizontal', 'Vertical' or boolean (yes/no) + </application> + + # end of the example +--> +</applications> + +</openbox_config> diff --git a/data/rc.xsd b/data/rc.xsd new file mode 100644 index 0000000..ad96994 --- /dev/null +++ b/data/rc.xsd @@ -0,0 +1,551 @@ +<?xml version="1.0" encoding="UTF-8"?> <!-- -*- nxml -*- --> + +<!-- XML Schema for the Openbox window manager configuration file --> + +<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" + "http://www.w3.org/2001/XMLSchema.dtd" [ +<!ATTLIST schema xmlns:ob CDATA #IMPLIED> +<!ENTITY % p "xsd:"> +<!ENTITY % s ":xsd"> +]> + +<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://openbox.org/4.0/rc" + xmlns:ob="http://openbox.org/4.0/rc" + elementFormDefault="qualified" + attributeFormDefault="unqualified"> + <!-- + root node + --> + <xsd:element name="openbox_config"> + <xsd:annotation> + <xsd:documentation>all these elements are expected in a openbox config file</xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:all> + <xsd:element name="resistance" type="ob:resistance"/> + <xsd:element name="focus" type="ob:focus"/> + <xsd:element name="placement" type="ob:placement"/> + <xsd:element name="theme" type="ob:theme"/> + <xsd:element name="desktops" type="ob:desktops"/> + <xsd:element name="resize" type="ob:resize"/> + <xsd:element minOccurs="0" name="margins" type="ob:margins"/> + <xsd:element name="dock" type="ob:dock"/> + <xsd:element name="keyboard" type="ob:keyboard"/> + <xsd:element name="mouse" type="ob:mouse"/> + <xsd:element name="menu" type="ob:menu"/> + <xsd:element name="applications" type="ob:applications"/> + </xsd:all> + </xsd:complexType> + </xsd:element> + <!-- + complex types + --> + <xsd:complexType name="resistance"> + <xsd:annotation> + <xsd:documentation>defines behaviour of windows when close to each other or the screen edge</xsd:documentation> + </xsd:annotation> + <xsd:all> + <xsd:element minOccurs="0" name="strength" type="xsd:integer"/> + <xsd:element minOccurs="0" name="screen_edge_strength" type="xsd:integer"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="focus"> + <xsd:annotation> + <xsd:documentation>defines aspects of window focus</xsd:documentation> + </xsd:annotation> + <xsd:all> + <xsd:element minOccurs="0" name="focusNew" type="ob:bool"/> + <xsd:element minOccurs="0" name="focusLast" type="ob:bool"/> + <xsd:element minOccurs="0" name="followMouse" type="ob:bool"/> + <xsd:element minOccurs="0" name="underMouse" type="ob:bool"/> + <xsd:element minOccurs="0" name="focusDelay" type="xsd:integer"/> + <xsd:element minOccurs="0" name="raiseOnFocus" type="ob:bool"/> + <xsd:element minOccurs="0" name="unfocusOnLeave" type="ob:bool"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="placement"> + <xsd:annotation> + <xsd:documentation>defines how new windows are placed</xsd:documentation> + </xsd:annotation> + <xsd:sequence> + <xsd:element minOccurs="0" name="policy" type="ob:placementpolicy"/> + <xsd:element minOccurs="0" name="center" type="ob:bool"/> + <xsd:element minOccurs="0" name="monitor" type="ob:placementmonitor"/> + <xsd:element minOccurs="0" name="monitor" type="ob:primarymonitor"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="margins"> + <xsd:annotation> + <xsd:documentation>defines desktop margins</xsd:documentation> + </xsd:annotation> + <xsd:all> + <xsd:element minOccurs="0" name="top" type="xsd:integer"/> + <xsd:element minOccurs="0" name="left" type="xsd:integer"/> + <xsd:element minOccurs="0" name="right" type="xsd:integer"/> + <xsd:element minOccurs="0" name="bottom" type="xsd:integer"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="theme"> + <xsd:sequence> + <xsd:element minOccurs="0" name="name" type="xsd:string"/> + <xsd:element minOccurs="0" name="titleLayout" type="xsd:string"/> + <xsd:element minOccurs="0" name="keepBorder" type="ob:bool"/> + <xsd:element minOccurs="0" name="animateIconify" type="ob:bool"/> + <xsd:element minOccurs="0" maxOccurs="unbounded" name="font" type="ob:font"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="font"> + <xsd:all> + <xsd:element minOccurs="0" name="name" type="xsd:string"/> + <xsd:element minOccurs="0" name="size" type="xsd:integer"/> + <xsd:element minOccurs="0" name="weight" type="ob:fontweight"/> + <xsd:element minOccurs="0" name="slant" type="ob:fontslant"/> + </xsd:all> + <xsd:attribute name="place" type="ob:fontplace" use="required"/> + </xsd:complexType> + <xsd:complexType name="desktops"> + <xsd:annotation> + <xsd:documentation>defines the number and names of desktops</xsd:documentation> + </xsd:annotation> + <xsd:all> + <xsd:element minOccurs="0" name="number" type="xsd:integer"/> + <xsd:element minOccurs="0" name="firstdesk" type="xsd:integer"/> + <xsd:element minOccurs="0" name="names"> + <xsd:complexType> + <xsd:sequence> + <xsd:element minOccurs="0" maxOccurs="unbounded" name="name" type="xsd:string"/> + </xsd:sequence> + </xsd:complexType> + </xsd:element> + <xsd:element minOccurs="0" name="popupTime" type="xsd:integer"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="resize"> + <xsd:all> + <xsd:element minOccurs="0" name="drawContents" type="ob:bool"/> + <xsd:element minOccurs="0" name="popupShow" type="ob:popupshow"/> + <xsd:element minOccurs="0" name="popupPosition" type="ob:popupposition"/> + <xsd:element minOccurs="0" name="popupFixedPosition" type="ob:popupfixedposition"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="popupfixedposition"> + <xsd:all> + <xsd:element minOccurs="0" name="x" type="ob:center_or_int"/> + <xsd:element minOccurs="0" name="y" type="ob:center_or_int"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="dock"> + <xsd:all> + <xsd:element minOccurs="0" name="position" type="ob:dock_position"/> + <xsd:element minOccurs="0" name="floatingX" type="xsd:integer"/> + <xsd:element minOccurs="0" name="floatingY" type="xsd:integer"/> + <xsd:element minOccurs="0" name="noStrut" type="ob:bool"/> + <xsd:element minOccurs="0" name="stacking" type="ob:layer"/> + <xsd:element minOccurs="0" name="direction" type="ob:direction"/> + <xsd:element minOccurs="0" name="autoHide" type="ob:bool"/> + <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/> + <xsd:element minOccurs="0" name="showDelay" type="xsd:integer"/> + <xsd:element minOccurs="0" name="moveButton" type="ob:button"/> + </xsd:all> + </xsd:complexType> + <xsd:complexType name="action"> + <xsd:all> + <xsd:element minOccurs="0" name="execute" type="xsd:string"/> + <xsd:element minOccurs="0" name="startupnotify" type="ob:notify"/> + <xsd:element minOccurs="0" name="command" type="xsd:string"/> + <xsd:element minOccurs="0" name="allDesktops" type="ob:bool"/> + <xsd:element minOccurs="0" name="menu" type="xsd:string"/> + <xsd:element minOccurs="0" name="delta" type="xsd:integer"/> + <xsd:element minOccurs="0" name="x" type="xsd:integer"/> + <xsd:element minOccurs="0" name="y" type="xsd:integer"/> + <xsd:element minOccurs="0" name="left" type="xsd:integer"/> + <xsd:element minOccurs="0" name="right" type="xsd:integer"/> + <xsd:element minOccurs="0" name="up" type="xsd:integer"/> + <xsd:element minOccurs="0" name="down" type="xsd:integer"/> + <xsd:element minOccurs="0" name="desktop"> + <xsd:simpleType> + <xsd:union memberTypes="xsd:integer ob:bool"/> + </xsd:simpleType> + </xsd:element> + <xsd:element minOccurs="0" name="edge" type="xsd:string"/> + <xsd:element minOccurs="0" name="wrap" type="ob:bool"/> + <xsd:element minOccurs="0" name="follow" type="ob:bool"/> + <xsd:element minOccurs="0" name="dialog" type="ob:dialogtype"/> + <xsd:element minOccurs="0" name="panels" type="ob:bool"/> + <xsd:element minOccurs="0" name="here" type="ob:bool"/> + <xsd:element minOccurs="0" name="linear" type="ob:bool"/> + <xsd:element minOccurs="0" name="group" type="ob:bool"/> + </xsd:all> + <xsd:attribute name="name" type="ob:actionname" use="required"/> + </xsd:complexType> + <xsd:complexType name="keybind"> + <xsd:choice> + <xsd:element maxOccurs="unbounded" name="action" type="ob:action"/> + <xsd:element maxOccurs="unbounded" name="keybind" type="ob:keybind"/> + </xsd:choice> + <xsd:attribute name="chroot" type="ob:bool"/> + <xsd:attribute name="key" type="ob:keyname" use="required"/> + </xsd:complexType> + <xsd:complexType name="keyboard"> + <xsd:sequence> + <xsd:element minOccurs="0" name="chainQuitKey" type="ob:keyname"/> + <xsd:element maxOccurs="unbounded" name="keybind" type="ob:keybind"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="mousebind"> + <xsd:sequence> + <xsd:element maxOccurs="unbounded" name="action" type="ob:action"/> + </xsd:sequence> + <xsd:attribute name="action" type="ob:mouseaction" use="required"/> + <xsd:attribute name="button" type="ob:button" use="required"/> + </xsd:complexType> + <xsd:complexType name="context"> + <xsd:sequence> + <xsd:element maxOccurs="unbounded" name="mousebind" type="ob:mousebind"/> + </xsd:sequence> + <xsd:attribute name="name" type="ob:contextname" use="required"/> + </xsd:complexType> + <xsd:complexType name="mouse"> + <xsd:sequence> + <xsd:element minOccurs="0" name="dragThreshold" type="xsd:integer"/> + <xsd:element minOccurs="0" name="doubleClickTime" type="xsd:integer"/> + <xsd:element minOccurs="0" name="screenEdgeWarpTime" type="xsd:integer"/> + <xsd:element minOccurs="0" name="screenEdgeWarpMouse" type="ob:bool"/> + <xsd:element maxOccurs="unbounded" name="context" type="ob:context"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="menu"> + <xsd:sequence> + <xsd:element maxOccurs="unbounded" name="file" type="xsd:string"/> + <xsd:element minOccurs="0" name="hideDelay" type="xsd:integer"/> + <xsd:element minOccurs="0" name="middle" type="ob:bool"/> + <xsd:element minOccurs="0" name="submenuShowDelay" type="xsd:integer"/> + <xsd:element minOccurs="0" name="showIcons" type="ob:bool"/> + <xsd:element minOccurs="0" name="manageDesktops" type="ob:bool"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="window_position"> + <xsd:all> + <xsd:element name="x" type="ob:center_or_int"/> + <xsd:element name="y" type="ob:center_or_int"/> + <xsd:element minOccurs="0" name="monitor" type="ob:mouse_or_int"/> + <xsd:element minOccurs="0" name="head" type="xsd:string"/> + </xsd:all> + <xsd:attribute name="force" type="ob:bool"/> + </xsd:complexType> + <xsd:complexType name="application"> + <xsd:all> + <xsd:element minOccurs="0" name="decor" type="ob:bool"/> + <xsd:element minOccurs="0" name="shade" type="ob:bool"/> + <xsd:element minOccurs="0" name="position" type="ob:window_position"/> + <xsd:element minOccurs="0" name="focus" type="xsd:string"/> + <xsd:element minOccurs="0" name="desktop" type="xsd:integer"/> + <xsd:element minOccurs="0" name="layer" type="ob:layer"/> + <xsd:element minOccurs="0" name="iconic" type="ob:bool"/> + <xsd:element minOccurs="0" name="skip_pager" type="ob:bool"/> + <xsd:element minOccurs="0" name="skip_taskbar" type="ob:bool"/> + <xsd:element minOccurs="0" name="fullscreen" type="ob:bool"/> + <xsd:element minOccurs="0" name="maximized" type="ob:maximization"/> + </xsd:all> + <!-- at least one of these must be present --> + <xsd:attribute name="role" type="xsd:string"/> + <xsd:attribute name="title" type="xsd:string"/> + <xsd:attribute name="type" type="ob:clienttype"/> + <xsd:attribute name="name" type="xsd:string"/> + <xsd:attribute name="class" type="xsd:string"/> + </xsd:complexType> + <xsd:complexType name="applications"> + <xsd:sequence> + <xsd:element minOccurs="0" maxOccurs="unbounded" name="application" type="ob:application"/> + </xsd:sequence> + </xsd:complexType> + <xsd:complexType name="notify"> + <xsd:all> + <xsd:element minOccurs="0" name="enabled" type="ob:bool"/> + <xsd:element minOccurs="0" name="name" type="xsd:string"/> + <xsd:element minOccurs="0" name="icon" type="xsd:string"/> + </xsd:all> + </xsd:complexType> + <!-- + simple types / restrictions + --> + <xsd:simpleType name="actionname"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[Ii][Ff]"/> + <xsd:pattern value="[Aa][Cc][Tt][Ii][Vv][Aa][Tt][Ee]"/> + <xsd:pattern value="[Bb][Rr][Ee][Aa][Kk][Cc][Hh][Rr][Oo][Oo][Tt]"/> + <xsd:pattern value="[Cc][Ll][Oo][Ss][Ee]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/> + <xsd:pattern value="[Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Nn][Oo][Rr][Tt][Hh][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ss][Oo][Uu][Tt][Hh][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Dd][Ii][Rr][Ee][Cc][Tt][Ii][Oo][Nn][Aa][Ll][Ff][Oo][Cc][Uu][Ss][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Ee][Xx][Ee][Cc][Uu][Tt][Ee]"/> + <xsd:pattern value="[Ee][Xx][Ii][Tt]"/> + <xsd:pattern value="[Ss][Ee][Ss][Ss][Ii][Oo][Nn][Ll][Oo][Gg][Oo][Uu][Tt]"/> + <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss]"/> + <xsd:pattern value="[Ff][Oo][Cc][Uu][Ss][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Gg][Rr][Oo][Ww][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Ii][Cc][Oo][Nn][Ii][Ff][Yy]"/> + <xsd:pattern value="[Kk][Ii][Ll][Ll]"/> + <xsd:pattern value="[Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Cc][Ee][Nn][Tt][Ee][Rr]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Ff][Rr][Oo][Mm][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ee][Aa][Ss][Tt]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Nn][Oo][Rr][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ss][Oo][Uu][Tt][Hh]"/> + <xsd:pattern value="[Mm][Oo][Vv][Ee][Tt][Oo][Ee][Dd][Gg][Ee][Ww][Ee][Ss][Tt]"/> + <xsd:pattern value="[Nn][Ee][Xx][Tt][Ww][Ii][Nn][Dd][Oo][Ww]"/> + <xsd:pattern value="[Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss][Ww][Ii][Nn][Dd][Oo][Ww]"/> + <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee]"/> + <xsd:pattern value="[Rr][Aa][Ii][Ss][Ee][Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Rr][Ee][Cc][Oo][Nn][Ff][Ii][Gg][Uu][Rr][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Rr][Ee][Ss][Ii][Zz][Ee][Rr][Ee][Ll][Aa][Tt][Ii][Vv][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Rr][Ee][Ss][Tt][Aa][Rr][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Bb][Oo][Tt][Tt][Oo][Mm][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Dd][Oo][Ww][Nn]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Ll][Ee][Ff][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Nn][Ee][Xx][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Pp][Rr][Ee][Vv][Ii][Oo][Uu][Ss]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Rr][Ii][Gg][Hh][Tt]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Dd][Ee][Ss][Kk][Tt][Oo][Pp][Uu][Pp]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Nn][Oo][Rr][Mm][Aa][Ll][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Ee][Nn][Dd][Tt][Oo][Tt][Oo][Pp][Ll][Aa][Yy][Ee][Rr]"/> + <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Ss][Hh][Aa][Dd][Ee][Ll][Oo][Ww][Ee][Rr]"/> + <xsd:pattern value="[Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Ss][Hh][Oo][Ww][Mm][Ee][Nn][Uu]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Bb][Oo][Tt][Tt][Oo][Mm]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Aa][Ll][Ww][Aa][Yy][Ss][Oo][Nn][Tt][Oo][Pp]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Ee][Cc][Oo][Rr][Aa][Tt][Ii][Oo][Nn][Ss]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Dd][Oo][Cc][Kk][Aa][Uu][Tt][Oo][Hh][Ii][Dd][Ee]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ff][Uu][Ll][Ll][Ss][Cc][Rr][Ee][Ee][Nn]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Oo][Mm][Nn][Ii][Pp][Rr][Ee][Ss][Ee][Nn][Tt]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Tt][Oo][Gg][Gg][Ll][Ee][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + <xsd:pattern value="[Uu][Nn][Ff][Oo][Cc][Uu][Ss]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Ff][Uu][Ll][Ll]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Hh][Oo][Rr][Zz]"/> + <xsd:pattern value="[Uu][Nn][Mm][Aa][Xx][Ii][Mm][Ii][Zz][Ee][Vv][Ee][Rr][Tt]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Aa][Dd][Ee][Rr][Aa][Ii][Ss][Ee]"/> + <xsd:pattern value="[Uu][Nn][Ss][Hh][Oo][Ww][Dd][Ee][Ss][Kk][Tt][Oo][Pp]"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="clienttype"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="desktop"/> + <xsd:enumeration value="dock"/> + <xsd:enumeration value="toolbar"/> + <xsd:enumeration value="menu"/> + <xsd:enumeration value="splash"/> + <xsd:enumeration value="utility"/> + <xsd:enumeration value="dialog"/> + <xsd:enumeration value="normal"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="bool"> + <!-- this is copied to maximization. Keep that in sync. --> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="yes"/> + <xsd:enumeration value="no"/> + <xsd:enumeration value="true"/> + <xsd:enumeration value="false"/> + <xsd:enumeration value="on"/> + <xsd:enumeration value="off"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="fontplace"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="ActiveWindow"/> + <xsd:enumeration value="InactiveWindow"/> + <xsd:enumeration value="MenuHeader"/> + <xsd:enumeration value="MenuItem"/> + <xsd:enumeration value="OnScreenDisplay"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="fontweight"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="normal"/> + <xsd:enumeration value="bold"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="fontslant"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="normal"/> + <xsd:enumeration value="italic"/> + <xsd:enumeration value="opaque"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="button"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="(([ACMSW]|Mod[1-5])-){,5}(Left|Middle|Right|Up|Down|Button[0-9]+)"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="center_or_int"> + <xsd:restriction base="xsd:string"> + <!-- ob: atoi($_) unless $_ eq 'center'; --> + <!-- I think the regexp DTRT WRT atoi. --> + <xsd:pattern value="center|-?(0|[1-9][0-9]*)"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="mouse_or_int"> + <xsd:restriction base="xsd:string"> + <!-- ob: atoi($_) unless $_ eq 'center'; --> + <!-- I think the regexp DTRT WRT atoi. --> + <xsd:pattern value="mouse|0|[1-9][0-9]*"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="contextname"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Desktop"/> + <xsd:enumeration value="Root"/> + <xsd:enumeration value="Client"/> + <xsd:enumeration value="Titlebar"/> + <xsd:enumeration value="Frame"/> + <xsd:enumeration value="TLCorner"/> + <xsd:enumeration value="TRCorner"/> + <xsd:enumeration value="BLCorner"/> + <xsd:enumeration value="BRCorner"/> + <xsd:enumeration value="Top"/> + <xsd:enumeration value="Left"/> + <xsd:enumeration value="Right"/> + <xsd:enumeration value="Bottom"/> + <xsd:enumeration value="Maximize"/> + <xsd:enumeration value="AllDesktops"/> + <xsd:enumeration value="Shade"/> + <xsd:enumeration value="Iconify"/> + <xsd:enumeration value="Icon"/> + <xsd:enumeration value="Close"/> + <xsd:enumeration value="MoveResize"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="direction"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Horizontal"/> + <xsd:enumeration value="Vertical"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="dock_position"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="TopLeft"/> + <xsd:enumeration value="Top"/> + <xsd:enumeration value="TopRight"/> + <xsd:enumeration value="Right"/> + <xsd:enumeration value="BottomRight"/> + <xsd:enumeration value="Bottom"/> + <xsd:enumeration value="BottomLeft"/> + <xsd:enumeration value="Left"/> + <xsd:enumeration value="Floating"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="keyname"> + <xsd:restriction base="xsd:string"> + <!-- FIXME: M, Mod2, Mod5 in addition to S, A, C --> + <!-- how do we do all substrings and permutations? --> + <xsd:pattern value="(([ACMSW]|Mod[1-5])-){,5}[a-zA-Z0-9]*"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="layer"> + <xsd:restriction base="xsd:string"> + <xsd:pattern value="[Aa][Bb][Oo][Vv][Ee]"/> + <xsd:pattern value="[Nn][Oo][Rr][Mm][Aa][Ll]"/> + <xsd:pattern value="[Bb][Ee][Ll][Oo][Ww]"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="maximization"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Horizontal"/> + <xsd:enumeration value="Vertical"/> + <!-- this is a copy of ob:bool. Keep it in sync. --> + <xsd:enumeration value="yes"/> + <xsd:enumeration value="no"/> + <xsd:enumeration value="true"/> + <xsd:enumeration value="false"/> + <xsd:enumeration value="on"/> + <xsd:enumeration value="off"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="mouseaction"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Click"/> + <xsd:enumeration value="DoubleClick"/> + <xsd:enumeration value="Drag"/> + <xsd:enumeration value="Press"/> + <xsd:enumeration value="Release"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="placementpolicy"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Smart"/> + <xsd:enumeration value="UnderMouse"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="placementmonitor"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Any"/> + <xsd:enumeration value="Mouse"/> + <xsd:enumeration value="Active"/> + <xsd:enumeration value="Primary"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="primarymonitor"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Mouse"/> + <xsd:enumeration value="Active"/> + <xsd:enumeration value="[0-9][0-9][0-9][0-9][0-9]"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="popupposition"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Top"/> + <xsd:enumeration value="Center"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="popupshow"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="Always"/> + <xsd:enumeration value="Never"/> + <xsd:enumeration value="Nonpixel"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="dialogtype"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="None"/> + <xsd:enumeration value="Icons"/> + <xsd:enumeration value="List"/> + </xsd:restriction> + </xsd:simpleType> +</xsd:schema> diff --git a/data/xbm/bullet.xbm b/data/xbm/bullet.xbm new file mode 100644 index 0000000..88481ec --- /dev/null +++ b/data/xbm/bullet.xbm @@ -0,0 +1,4 @@ +#define bullet_width 4 +#define bullet_height 7 +static unsigned char bullet_bits[] = { + 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 }; diff --git a/data/xbm/close.xbm b/data/xbm/close.xbm new file mode 100644 index 0000000..4a88cff --- /dev/null +++ b/data/xbm/close.xbm @@ -0,0 +1,4 @@ +#define close_width 6 +#define close_height 6 +static unsigned char close_bits[] = { + 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }; diff --git a/data/xbm/desk.xbm b/data/xbm/desk.xbm new file mode 100644 index 0000000..3e327e3 --- /dev/null +++ b/data/xbm/desk.xbm @@ -0,0 +1,4 @@ +#define desk_width 6 +#define desk_height 6 +static unsigned char desk_bits[] = { + 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }; diff --git a/data/xbm/desk_toggled.xbm b/data/xbm/desk_toggled.xbm new file mode 100644 index 0000000..d7e045e --- /dev/null +++ b/data/xbm/desk_toggled.xbm @@ -0,0 +1,4 @@ +#define desk_toggle_width 6 +#define desk_toggle_height 6 +static unsigned char desk_toggle_bits[] = { + 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }; diff --git a/data/xbm/iconify.xbm b/data/xbm/iconify.xbm new file mode 100644 index 0000000..2304866 --- /dev/null +++ b/data/xbm/iconify.xbm @@ -0,0 +1,4 @@ +#define iconify_width 6 +#define iconify_height 6 +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }; diff --git a/data/xbm/max.xbm b/data/xbm/max.xbm new file mode 100644 index 0000000..6d030af --- /dev/null +++ b/data/xbm/max.xbm @@ -0,0 +1,4 @@ +#define max_width 6 +#define max_height 6 +static unsigned char max_bits[] = { + 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; diff --git a/data/xbm/max_toggled.xbm b/data/xbm/max_toggled.xbm new file mode 100644 index 0000000..44c7cef --- /dev/null +++ b/data/xbm/max_toggled.xbm @@ -0,0 +1,4 @@ +#define max_width 6 +#define max_height 6 +static unsigned char max_bits[] = { + 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }; diff --git a/data/xbm/shade.xbm b/data/xbm/shade.xbm new file mode 100644 index 0000000..edb3b17 --- /dev/null +++ b/data/xbm/shade.xbm @@ -0,0 +1,4 @@ +#define iconify_width 6 +#define iconify_height 6 +static unsigned char iconify_bits[] = { + 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 }; diff --git a/data/xbm/shade_toggled.xbm b/data/xbm/shade_toggled.xbm new file mode 100644 index 0000000..edb3b17 --- /dev/null +++ b/data/xbm/shade_toggled.xbm @@ -0,0 +1,4 @@ +#define iconify_width 6 +#define iconify_height 6 +static unsigned char iconify_bits[] = { + 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 }; diff --git a/data/xsession/Makefile b/data/xsession/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/data/xsession/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/data/xsession/openbox-gnome-session.in b/data/xsession/openbox-gnome-session.in new file mode 100644 index 0000000..f31c9ad --- /dev/null +++ b/data/xsession/openbox-gnome-session.in @@ -0,0 +1,66 @@ +#!/bin/sh + +if test -n "$1"; then + echo "Syntax: openbox-gnome-session" + echo + echo "See the openbox-gnome-session(1) manpage for help." + exit +fi + +# Clean up after GDM +xprop -root -remove _NET_NUMBER_OF_DESKTOPS \ + -remove _NET_DESKTOP_NAMES \ + -remove _NET_CURRENT_DESKTOP 2> /dev/null + +VER=$(gnome-session --version 2>/dev/null | \ + sed -e 's/[^0-9.]*\([0-9.]\+\)/\1/') + +MAJOR=$(echo $VER | cut -d . -f 1) +MINOR=$(echo $VER | cut -d . -f 2) + +# run GNOME with Openbox as its window manager + +if test $MAJOR -lt 2 || (test $MAJOR = 2 && test $MINOR -le 22); then + # older gnome-session was easy to work with + export WINDOW_MANAGER="@bindir@/openbox" + exec gnome-session --choose-session=openbox-session "$@" +elif test $MAJOR -lt 3; then + # old gnome-session requires openbox to be set in gconf and an + # openbox.desktop to be installed in the applications directory + + SPATH=/desktop/gnome/session + + # get the current default session + SESSION=$(gconftool-2 -g $SPATH/default_session 2> /dev/null) + + # make sure openbox is going to be run + if test -z "$SESSION"; then + # if its empty then just run openbox + SESSION="[openbox]" + elif ! echo "$SESSION" | grep -q openbox; then + # if openbox isn't in the session then append it + SESSION="${SESSION%]},openbox]" + fi + + # get the current GNOME/Openbox session + OB_SESSION=$(gconftool-2 -g $SPATH/openbox_session 2> /dev/null) + + # update the GNOME/Openbox session if needed + if test x$OB_SESSION != x$SESSION; then + # the default session changed or we didn't run GNOME/Openbox before + gconftool-2 -t list --list-type=strings -s $SPATH/openbox_session \ + "$SESSION" 2> /dev/null + fi + + # run GNOME/Openbox + exec gnome-session --default-session-key $SPATH/openbox_session "$@" +else + # new gnome-session requires session file installed in + # /usr/share/gnome-session/sessions as well as openbox.desktop to be + # installed in the applications directory + + exec gnome-session --session=openbox-gnome +fi + + + diff --git a/data/xsession/openbox-gnome.desktop.in b/data/xsession/openbox-gnome.desktop.in new file mode 100644 index 0000000..19ae82e --- /dev/null +++ b/data/xsession/openbox-gnome.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=GNOME/Openbox +Comment=Use the Openbox window manager inside of the GNOME desktop environment +Exec=@bindir@/openbox-gnome-session +TryExec=gnome-session +Icon=openbox.png +Type=XSession diff --git a/data/xsession/openbox-kde-session.in b/data/xsession/openbox-kde-session.in new file mode 100644 index 0000000..3572279 --- /dev/null +++ b/data/xsession/openbox-kde-session.in @@ -0,0 +1,20 @@ +#!/bin/sh + +if test -n "$1"; then + echo "Syntax: openbox-kde-session" + echo + echo "See the openbox-kde-session(1) manpage for help." + exit +fi + +# Set the prefix for the menu layout to use +export XDG_MENU_PREFIX="kde-4-" + +# Clean up after GDM +xprop -root -remove _NET_NUMBER_OF_DESKTOPS \ + -remove _NET_DESKTOP_NAMES \ + -remove _NET_CURRENT_DESKTOP 2> /dev/null + +# Run KDE with Openbox as its window manager +export KDEWM="@bindir@/openbox" +exec startkde "$@" diff --git a/data/xsession/openbox-kde.desktop.in b/data/xsession/openbox-kde.desktop.in new file mode 100644 index 0000000..ddfc72d --- /dev/null +++ b/data/xsession/openbox-kde.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=KDE/Openbox +Comment=Use the Openbox window manager inside of the K Desktop Environment +Exec=@bindir@/openbox-kde-session +TryExec=startkde +Icon=openbox.png +Type=XSession diff --git a/data/xsession/openbox-session.in b/data/xsession/openbox-session.in new file mode 100644 index 0000000..3cf3571 --- /dev/null +++ b/data/xsession/openbox-session.in @@ -0,0 +1,22 @@ +#!/bin/sh + +if test -n "$1"; then + echo "Syntax: openbox-session" + echo + echo "See the openbox-session(1) manpage for help." + exit +fi + +# Clean up after GDM +xprop -root -remove _NET_NUMBER_OF_DESKTOPS \ + -remove _NET_DESKTOP_NAMES \ + -remove _NET_CURRENT_DESKTOP 2> /dev/null + +# Set up the environment +A="@configdir@/openbox/environment" +test -r $A && . $A +A="${XDG_CONFIG_HOME:-"$HOME/.config"}/openbox/environment" +test -r $A && . $A + +# Run Openbox, and have it run the autostart stuff +exec @bindir@/openbox --startup "@libexecdir@/openbox-autostart OPENBOX" "$@" diff --git a/data/xsession/openbox.desktop.in b/data/xsession/openbox.desktop.in new file mode 100644 index 0000000..0914e5b --- /dev/null +++ b/data/xsession/openbox.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Openbox +Comment=Log in using the Openbox window manager (without a session manager) +Exec=@bindir@/openbox-session +TryExec=@bindir@/openbox-session +Icon=openbox.png +Type=XSession diff --git a/debian/changelog.in b/debian/changelog.in new file mode 100644 index 0000000..a217280 --- /dev/null +++ b/debian/changelog.in @@ -0,0 +1,6 @@ +openbox (@version@) unstable; urgency=low + + * Upstream release. + + -- Dana Jansens <danakj@orodu.net> @time@ + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/conffiles b/debian/conffiles new file mode 100644 index 0000000..6319a21 --- /dev/null +++ b/debian/conffiles @@ -0,0 +1,2 @@ +/etc/xdg/openbox/rc.xml +/etc/xdg/openbox/menu.xml diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..61f72cc --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: openbox +Section: x11 +Priority: optional +Maintainer: Dana Jansens <danakj@orodu.net> +Build-Depends: debhelper (>= 5), coreutils, sed, autotools-dev, gcc, automake1.9, autoconf, libtool, pkg-config, libpango1.0-dev, libglib2.0-dev, libxml2-dev, libxcursor-dev, libstartup-notification0-dev, xlibs-dev, libxinerama-dev, libxft2-dev +Standards-Version: 3.7.2 + +Package: openbox +Architecture: i386 +Depends: libc6, libglib2.0-0, libxcursor1, libx11-6, libpango1.0-0, libice6, libsm6, libxext6, libxinerama1, libxml2, libstartup-notification0, libxft2 +Recommends: obconf, ttf-bitstream-vera +Suggests: menu, x-display-manager, ksmserver | gnome-session, kdm | gdm +Conflicts: menu (<< 2.1.12) +Provides: x-window-manager +Description: A minimalistic, highly configurable, next generation window manager with extensive standards support. + http://openbox.org/ diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..6c93400 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,21 @@ +Homepage: http://openbox.org/ + +The main upstream author is Dana Jansens <danakj@orodu.net>. To see +a more complete listing of contributors, look at the AUTHORS file. + +Copyright: + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + On Debian GNU/Linux systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL'. + +Copyright for the Debian-related build-scripts: + + Copyright (C) 2007 Dana Jansens <danakj@orodu.net> + + These scripts are placed under the BSD license, which may be found in + the file `/usr/share/common-licenses/BSD' on most Debian systems. diff --git a/debian/menu b/debian/menu new file mode 100644 index 0000000..97d2d14 --- /dev/null +++ b/debian/menu @@ -0,0 +1,6 @@ +?package(openbox):\ + needs="wm"\ + section="WindowManagers"\ + title="Openbox"\ + command="/usr/bin/openbox"\ + icon="/usr/share/pixmaps/openbox.png" diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..aa69037 --- /dev/null +++ b/debian/postinst @@ -0,0 +1,42 @@ +#!/bin/sh +# postinst script for openbox +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * <postinst> `configure' <most-recently-configured-version> +# * <old-postinst> `abort-upgrade' <new version> +# * <conflictor's-postinst> `abort-remove' `in-favour' <package> +# <new-version> +# * <postinst> `abort-remove' +# * <deconfigured's-postinst> `abort-deconfigure' `in-favour' +# <failed-install-package> <version> `removing' +# <conflicting-package> <version> +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + if [ -x /usr/bin/update-menus ]; then update-menus; fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..641c6b9 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,43 @@ +#!/bin/sh +# postrm script for openbox +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * <postrm> `remove' +# * <postrm> `purge' +# * <old-postrm> `upgrade' <new-version> +# * <new-postrm> `failed-upgrade' <old-version> +# * <new-postrm> `abort-install' +# * <new-postrm> `abort-install' <old-version> +# * <new-postrm> `abort-upgrade' <old-version> +# * <disappearer's-postrm> `disappear' <overwriter> +# <overwriter-version> +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +case "$1" in + purge) + ;; + remove) + if [ -x /usr/bin/update-menus ]; then update-menus ; fi + ;; + upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..dea855b --- /dev/null +++ b/debian/rules @@ -0,0 +1,107 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +config.status: configure + dh_testdir + # Add here commands to configure the package. + ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr --sysconfdir=/etc --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info CFLAGS="$(CFLAGS)" LDFLAGS="-Wl,-z,defs" + + +build: build-stamp + +build-stamp: config.status + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + #docbook-to-man debian/openbox.sgml > openbox.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean +ifneq "$(wildcard /usr/share/misc/config.sub)" "" + cp -f /usr/share/misc/config.sub config.sub +endif +ifneq "$(wildcard /usr/share/misc/config.guess)" "" + cp -f /usr/share/misc/config.guess config.guess +endif + + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/openbox. + $(MAKE) DESTDIR=$(CURDIR)/debian/openbox install + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs CHANGELOG +# dh_installdocs +# dh_installexamples +# dh_install + dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo +# dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/doc/doxygen/comments b/doc/doxygen/comments new file mode 100644 index 0000000..7930f36 --- /dev/null +++ b/doc/doxygen/comments @@ -0,0 +1,11 @@ +// Further comments for doxygen on the src/ directory + +/*! @namespace ob + The namespace containing the %Openbox window manager application +*/ + +/*! @namespace otk + The namespace containing a toolkit used by the Openbox window manager, + and also for use by external applications that wish to present a similar + visual appearance to Openbox. +*/ diff --git a/doc/doxygen/doxygen.conf.in b/doc/doxygen/doxygen.conf.in new file mode 100644 index 0000000..768dc19 --- /dev/null +++ b/doc/doxygen/doxygen.conf.in @@ -0,0 +1,192 @@ +# Doxyfile 1.2.18 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = Openbox +PROJECT_NUMBER = @version@ +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = @basedir@ +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = NO +CASE_SENSE_NAMES = NO +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = YES +DETAILS_AT_TOP = YES +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 2 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../../src ../../otk comments +FILE_PATTERNS = *.hh *.cc +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = *_wrap.cc +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = OB OTK _ +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 200 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = letter +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = DOXYGEN_IGNORE +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/doc/obxprop.1.in b/doc/obxprop.1.in new file mode 100644 index 0000000..90f01ae --- /dev/null +++ b/doc/obxprop.1.in @@ -0,0 +1,46 @@ +.TH "OBXPROP" "1" +.SH "NAME" +openbox \(em A minimalistic, highly configurable, next generation window +manager with extensive standards support. +.SH "SYNOPSIS" +.PP +\fBopenbox\fR [\-\-help] [\-\-display DISPLAY] [\-\-id ID] [\-\-root] [\-\-] [PROPERTY \&...] +.SH "DESCRIPTION" +.PP +obxprop is a tool for displaying the properties on an x +window. +.PP +This tool has a similar functionality to \fBxprop\fR, +but obxprop allows you to see UTF-8 strings as text. +.PP +You may use the \fB\-\-id\fR option to specify a window +identifier, otherwise obxprop will allow you to select a window by +clicking on it. +.PP +Primarily, this tool exists for Openbox users to see the value of the +_OB_APP_NAME, _OB_APP_CLASS, _OB_APP_ROLE, and _OB_APP_TYPE properties, +which Openbox uses for +matching windows against user-defined application rules. +.SH "OPTIONS" +.PP +These are the possible options that \fBopenbox\fR accepts: +.IP "\fB\-\-help\fP" 10 +Show a summary of the options available. +.IP "\fB\-\-display\fP DISPLAY" 10 +Specify the X display to use. +.IP "\fB\-\-id\fP ID" 10 +Specify the window identifier for the window whose properties +will be displayed. +.PP +Similar to \fBxprop\fR, +you may pass one or more property names to have +\fBopenbox\fR limit its output to only the properties +requested. +.SH "SEE ALSO" +.PP +openbox (1), openbox-session(1), openbox-gnome-session(1), +openbox-kde-session(1). +.PP +Please report bugs to: \fBhttp://bugzilla.icculus.org/ +\fP +.\" created by instant / docbook-to-man, Mon 08 Feb 2010, 15:03 diff --git a/doc/obxprop.1.sgml b/doc/obxprop.1.sgml new file mode 100644 index 0000000..bf89442 --- /dev/null +++ b/doc/obxprop.1.sgml @@ -0,0 +1,124 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + <!ENTITY dhsection "<manvolnum>1</manvolnum>"> + <!ENTITY dhucpackage "<refentrytitle>OBXPROP</refentrytitle>"> + <!ENTITY dhpackage "openbox"> +]> + +<refentry> + <refmeta> + &dhucpackage; + + &dhsection; + </refmeta> + <refnamediv> + <refname>&dhpackage;</refname> + + <refpurpose>A minimalistic, highly configurable, next generation window + manager with extensive standards support.</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>&dhpackage;</command> + <arg choice="Opt">--help</arg> + <arg choice="Opt">--display DISPLAY</arg> + <arg choice="Opt">--id ID</arg> + <arg choice="Opt">--root</arg> + <arg choice="Opt">--</arg> + <arg choice="Opt" rep="Repeat">PROPERTY</arg> + </cmdsynopsis> + </refsynopsisdiv> + <refsect1> + <title>DESCRIPTION</title> + + <para>obxprop is a tool for displaying the properties on an x + window.</para> + + <para>This tool has a similar functionality to <command>xprop</command>, + but obxprop allows you to see UTF-8 strings as text.</para> + + <para>You may use the <command>--id</command> option to specify a window + identifier, otherwise obxprop will allow you to select a window by + clicking on it.</para> + + <para>Primarily, this tool exists for Openbox users to see the value of the + _OB_APP_NAME, _OB_APP_CLASS, _OB_APP_ROLE, and _OB_APP_TYPE properties, + which Openbox uses for + matching windows against user-defined application rules.</para> + </refsect1> + <refsect1> + <title>OPTIONS</title> + + <para>These are the possible options that <command>&dhpackage;</command> accepts:</para> + <variablelist> + <varlistentry> + <term><option>--help</option></term> + <listitem> + <para>Show a summary of the options available.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--display</option> DISPLAY</term> + <listitem> + <para>Specify the X display to use.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--id</option> ID</term> + <listitem> + <para>Specify the window identifier for the window whose properties + will be displayed.</para> + </listitem> + </varlistentry> + </variablelist> + + <para>Similar to <command>xprop</command>, + you may pass one or more property names to have + <command>&dhpackage;</command> limit its output to only the properties + requested.</para> + </refsect1> + <refsect1> + <title>SEE ALSO</title> + + <para>openbox (1), openbox-session(1), openbox-gnome-session(1), + openbox-kde-session(1).</para> + + <para>Please report bugs to: <literal>http://bugzilla.icculus.org/ + </literal></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> + + diff --git a/doc/openbox-gnome-session.1.in b/doc/openbox-gnome-session.1.in new file mode 100644 index 0000000..7256874 --- /dev/null +++ b/doc/openbox-gnome-session.1.in @@ -0,0 +1,22 @@ +.TH "OPENBOX" "1" +.SH "NAME" +openbox-gnome-session \(em Run a GNOME session with Openbox as the window manager + +.SH "DESCRIPTION" +.PP +\fBopenbox-gnome-session\fR runs a GNOME session with +Openbox as the window manager. +.PP +\fBopenbox-gnome-session\fR does not take any command line +arguments. +.SH "SEE ALSO" +.PP +openbox(1), openbox-kde-session(1), openbox-session(1). + +.PP +The program's full documentation is available on the website: +\fBhttp://openbox.org/\fP +.PP +Please report bugs to: \fBhttp://bugzilla.icculus.org/ +\fP +.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 diff --git a/doc/openbox-gnome-session.1.sgml b/doc/openbox-gnome-session.1.sgml new file mode 100644 index 0000000..66add1b --- /dev/null +++ b/doc/openbox-gnome-session.1.sgml @@ -0,0 +1,76 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + <!ENTITY dhsection "<manvolnum>1</manvolnum>"> + <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>"> + <!ENTITY dhpackage "openbox-gnome-session"> +]> + +<refentry> + <refmeta> + &dhucpackage; + + &dhsection; + </refmeta> + <refnamediv> + <refname>&dhpackage;</refname> + + <refpurpose>Run a GNOME session with Openbox as the window manager + </refpurpose> + </refnamediv> + <refsect1> + <title>DESCRIPTION</title> + + <para><command>&dhpackage;</command> runs a GNOME session with + Openbox as the window manager.</para> + + <para><command>&dhpackage;</command> does not take any command line + arguments.</para> + </refsect1> + <refsect1> + <title>SEE ALSO</title> + + <para>openbox(1), openbox-kde-session(1), openbox-session(1). + </para> + + <para>The program's full documentation is available on the website: + <literal>http://openbox.org/</literal></para> + + <para>Please report bugs to: <literal>http://bugzilla.icculus.org/ + </literal></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> + + diff --git a/doc/openbox-kde-session.1.in b/doc/openbox-kde-session.1.in new file mode 100644 index 0000000..9a96275 --- /dev/null +++ b/doc/openbox-kde-session.1.in @@ -0,0 +1,22 @@ +.TH "OPENBOX" "1" +.SH "NAME" +openbox-kde-session \(em Run a KDE session with Openbox as the window manager + +.SH "DESCRIPTION" +.PP +\fBopenbox-kde-session\fR runs a KDE session with +Openbox as the window manager. +.PP +\fBopenbox-kde-session\fR does not take any command line +arguments. +.SH "SEE ALSO" +.PP +openbox(1), openbox-gnome-session(1), openbox-session(1). + +.PP +The program's full documentation is available on the website: +\fBhttp://openbox.org/\fP +.PP +Please report bugs to: \fBhttp://bugzilla.icculus.org/ +\fP +.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 diff --git a/doc/openbox-kde-session.1.sgml b/doc/openbox-kde-session.1.sgml new file mode 100644 index 0000000..70c5117 --- /dev/null +++ b/doc/openbox-kde-session.1.sgml @@ -0,0 +1,76 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + <!ENTITY dhsection "<manvolnum>1</manvolnum>"> + <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>"> + <!ENTITY dhpackage "openbox-kde-session"> +]> + +<refentry> + <refmeta> + &dhucpackage; + + &dhsection; + </refmeta> + <refnamediv> + <refname>&dhpackage;</refname> + + <refpurpose>Run a KDE session with Openbox as the window manager + </refpurpose> + </refnamediv> + <refsect1> + <title>DESCRIPTION</title> + + <para><command>&dhpackage;</command> runs a KDE session with + Openbox as the window manager.</para> + + <para><command>&dhpackage;</command> does not take any command line + arguments.</para> + </refsect1> + <refsect1> + <title>SEE ALSO</title> + + <para>openbox(1), openbox-gnome-session(1), openbox-session(1). + </para> + + <para>The program's full documentation is available on the website: + <literal>http://openbox.org/</literal></para> + + <para>Please report bugs to: <literal>http://bugzilla.icculus.org/ + </literal></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> + + diff --git a/doc/openbox-session.1.in b/doc/openbox-session.1.in new file mode 100644 index 0000000..483cdfd --- /dev/null +++ b/doc/openbox-session.1.in @@ -0,0 +1,42 @@ +.TH "OPENBOX" "1" +.SH "NAME" +openbox-session \(em Runs an Openbox session without any session manager. + +.SH "DESCRIPTION" +.PP +\fBopenbox-session\fR runs an openbox session without +any session manager. Without a session manager, you will not be able +to save your state from one log in to the next. +.PP +\fBopenbox-session\fR does not take any command line +arguments. +.PP +On log in, \fBopenbox-session\fR will run the +~/.config/openbox/autostart.sh script if it exists, and will run the +system-wide script @configdir@/openbox/autostart.sh otherwise. You may +place anything you want to run automatically in those files, for example: + +.PP +.RS +.PP +.nf +xsetroot \-solid grey & +gnome-settings-daemon & +.fi +.RE +.PP +Make sure that each line is followed by a "&" or else the script will +stop there and further commands will not be executed. You can use the +@configdir@/openbox/autostart.sh file as an example for creating your +own. +.SH "SEE ALSO" +.PP +openbox(1), openbox-session(1), openbox-gnome-session(1). + +.PP +The program's full documentation is available on the website: +\fBhttp://openbox.org/\fP +.PP +Please report bugs to: \fBhttp://bugzilla.icculus.org/ +\fP +.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 diff --git a/doc/openbox-session.1.sgml b/doc/openbox-session.1.sgml new file mode 100644 index 0000000..ccece7a --- /dev/null +++ b/doc/openbox-session.1.sgml @@ -0,0 +1,93 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + <!ENTITY dhsection "<manvolnum>1</manvolnum>"> + <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>"> + <!ENTITY dhpackage "openbox-session"> +]> + +<refentry> + <refmeta> + &dhucpackage; + + &dhsection; + </refmeta> + <refnamediv> + <refname>&dhpackage;</refname> + + <refpurpose>Runs an Openbox session without any session manager. + </refpurpose> + </refnamediv> + <refsect1> + <title>DESCRIPTION</title> + + <para><command>&dhpackage;</command> runs an openbox session without + any session manager. Without a session manager, you will not be able + to save your state from one log in to the next.</para> + + <para><command>&dhpackage;</command> does not take any command line + arguments.</para> + + <para>On log in, <command>&dhpackage;</command> will run the + ~/.config/openbox/autostart.sh script if it exists, and will run the + system-wide script @configdir@/openbox/autostart.sh otherwise. You may + place anything you want to run automatically in those files, for example: + </para> + + <blockquote><literallayout> + xsetroot -solid grey & + gnome-settings-daemon &</literallayout></blockquote> + + <para>Make sure that each line is followed by a "&" or else the script will + stop there and further commands will not be executed. You can use the + @configdir@/openbox/autostart.sh file as an example for creating your + own.</para> + + </refsect1> + <refsect1> + <title>SEE ALSO</title> + + <para>openbox(1), openbox-session(1), openbox-gnome-session(1). + </para> + + <para>The program's full documentation is available on the website: + <literal>http://openbox.org/</literal></para> + + <para>Please report bugs to: <literal>http://bugzilla.icculus.org/ + </literal></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> + + diff --git a/doc/openbox.1.in b/doc/openbox.1.in new file mode 100644 index 0000000..d508e8c --- /dev/null +++ b/doc/openbox.1.in @@ -0,0 +1,95 @@ +.TH "OPENBOX" "1" +.SH "NAME" +openbox \(em A minimalistic, highly configurable, next generation window +manager with extensive standards support. +.SH "SYNOPSIS" +.PP +\fBopenbox\fR [\fB\-\-help\fP] [\fB\-\-version\fP] [\fB\-\-replace\fP] [\fB\-\-reconfigure\fP] [\fB\-\-restart\fP] [\fB\-\-sm-disable\fP] [\fB\-\-sync\fP] [\fB\-\-debug\fP] [\fB\-\-debug-focus\fP] [\fB\-\-debug-xinerama\fP] +.SH "DESCRIPTION" +.PP +Openbox is minimalistic, highly configurable, next generation window +manager with extensive standards support. +.PP +You can start Openbox in three ways: +.PP +If you run a display manager such as GDM, you will find 3 entries +in the login session type menu for Openbox: +\fBGNOME/Openbox\fR, \fBKDE/Openbox\fR and \fBOpenbox\fR. If you want to use Openbox +within GNOME or KDE, you can choose the appropriate entry, and it will +launch GNOME or KDE with Openbox as the window manager. +.PP +The third option at log in, which is \fBOpenbox\fR without a session manager, uses the \fBopenbox-session\fR command to start Openbox. On log in, \fBopenbox\fR will +run the ~/.config/openbox/autostart.sh script if it exists, and will run +the system-wide script @configdir@/openbox/autostart.sh otherwise. You +may place anything you want to run automatically in those files, for +example: + +.PP +.RS +.PP +.nf +xsetroot \-solid grey & +gnome-settings-daemon & +.fi +.RE +.PP +Make sure that each line is followed by a "&" or else the script will +stop there and further commands will not be executed. You can use the +@configdir@/openbox/autostart.sh file as an example for creating your +own. +.PP +The default @configdir@/openbox/autostart.sh runs a number of things +with Openbox. +.PP +Lastly, if you use \fBstartx\fR to launch your X +session, you can set up a ~/.xinitrc file to run +\fBopenbox-session\fR and follow the same directions as +above regarding the autostart.sh file. +.PP +You can use the \fBobconf\fR tool to configure Openbox +easily with a graphical interface, however more in-depth configuration +is possible by editing the configuration files by hand. +.PP +The default configuration and menu files are installed in +@configdir@/openbox/, and the user configuration is placed in +~/.config/openbox/. You can copy the default configuration and menus +to ~/.config/openbox and edit it to your liking. +.SH "OPTIONS" +.PP +These are the possible options that \fBopenbox\fR accepts: +.IP "\fB\-\-help\fP" 10 +Show a summary of the options available. +.IP "\fB\-\-version\fP" 10 +Show the version of the program. +.IP "\fB\-\-replace\fP" 10 +Replace the currently running window manager. +.IP "\fB\-\-reconfigure\fP" 10 +If Openbox is already running on the display, tell it to +reload its configuration. +.IP "\fB\-\-restart\fP" 10 +If Openbox is already running on the display, tell it to +restart. This is useful if you have upgraded Openbox and don't +want to restart X. +.IP "\fB\-\-sm-disable\fP" 10 +Do not connect to the session manager. +.IP "\fB\-\-sync\fP" 10 +Run in synchronous mode (for debugging). +.IP "\fB\-\-debug\fP" 10 +Display debugging output. +.IP "\fB\-\-debug-focus\fP" 10 +Display debugging output for focus handling. +.IP "\fB\-\-debug-xinerama\fP" 10 +Split the display into two fake xinerama regions, if +xinerama is not already enabled. This is for debugging +xinerama support. +.SH "SEE ALSO" +.PP +obconf (1), openbox-session(1), openbox-gnome-session(1), +openbox-kde-session(1). +.PP +The program's full documentation is available on the website: +\fBhttp://openbox.org/\fP +.PP +Please report bugs to: \fBhttp://bugzilla.icculus.org/ +\fP +.\" created by instant / docbook-to-man, Wed 06 Jan 2010, 13:40 diff --git a/doc/openbox.1.sgml b/doc/openbox.1.sgml new file mode 100644 index 0000000..275c324 --- /dev/null +++ b/doc/openbox.1.sgml @@ -0,0 +1,204 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [ + +<!-- Process this file with docbook-to-man to generate an nroff manual + page: `docbook-to-man manpage.sgml > manpage.1'. You may view + the manual page with: `docbook-to-man manpage.sgml | nroff -man | + less'. A typical entry in a Makefile or Makefile.am is: + +manpage.1: manpage.sgml + docbook-to-man $< > $@ + + + The docbook-to-man binary is found in the docbook-to-man package. + Please remember that if you create the nroff version in one of the + debian/rules file targets (such as build), you will need to include + docbook-to-man in your Build-Depends control field. + + --> + + <!ENTITY dhsection "<manvolnum>1</manvolnum>"> + <!ENTITY dhucpackage "<refentrytitle>OPENBOX</refentrytitle>"> + <!ENTITY dhpackage "openbox"> +]> + +<refentry> + <refmeta> + &dhucpackage; + + &dhsection; + </refmeta> + <refnamediv> + <refname>&dhpackage;</refname> + + <refpurpose>A minimalistic, highly configurable, next generation window + manager with extensive standards support.</refpurpose> + </refnamediv> + <refsynopsisdiv> + <cmdsynopsis> + <command>&dhpackage;</command> + <arg><option>--help</option></arg> + <arg><option>--version</option></arg> + <arg><option>--replace</option></arg> + <arg><option>--reconfigure</option></arg> + <arg><option>--restart</option></arg> + <arg><option>--sm-disable</option></arg> + <arg><option>--sync</option></arg> + <arg><option>--debug</option></arg> + <arg><option>--debug-focus</option></arg> + <arg><option>--debug-xinerama</option></arg> + </cmdsynopsis> + </refsynopsisdiv> + <refsect1> + <title>DESCRIPTION</title> + + <para>Openbox is minimalistic, highly configurable, next generation window + manager with extensive standards support.</para> + + <para>You can start Openbox in three ways:</para> + + <para>If you run a display manager such as GDM, you will find 3 entries + in the login session type menu for Openbox: + <command>GNOME/Openbox</command>, <command>KDE/Openbox</command> + and <command>Openbox</command>. If you want to use Openbox + within GNOME or KDE, you can choose the appropriate entry, and it will + launch GNOME or KDE with Openbox as the window manager.</para> + + <para>The third option at log in, which is <command>Openbox</command> + without a session manager, uses the <command>openbox-session</command> + command to start Openbox. On log in, <command>&dhpackage;</command> will + run the ~/.config/openbox/autostart.sh script if it exists, and will run + the system-wide script @configdir@/openbox/autostart.sh otherwise. You + may place anything you want to run automatically in those files, for + example: + </para> + + <blockquote><literallayout> + xsetroot -solid grey & + gnome-settings-daemon &</literallayout></blockquote> + + <para>Make sure that each line is followed by a "&" or else the script will + stop there and further commands will not be executed. You can use the + @configdir@/openbox/autostart.sh file as an example for creating your + own.</para> + + <para>The default @configdir@/openbox/autostart.sh runs a number of things + with Openbox.</para> + + <para>Lastly, if you use <command>startx</command> to launch your X + session, you can set up a ~/.xinitrc file to run + <command>openbox-session</command> and follow the same directions as + above regarding the autostart.sh file.</para> + + <para>You can use the <command>obconf</command> tool to configure Openbox + easily with a graphical interface, however more in-depth configuration + is possible by editing the configuration files by hand.</para> + + <para>The default configuration and menu files are installed in + @configdir@/openbox/, and the user configuration is placed in + ~/.config/openbox/. You can copy the default configuration and menus + to ~/.config/openbox and edit it to your liking.</para> + </refsect1> + <refsect1> + <title>OPTIONS</title> + + <para>These are the possible options that <command>&dhpackage;</command> accepts:</para> + <variablelist> + <varlistentry> + <term><option>--help</option></term> + <listitem> + <para>Show a summary of the options available.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--version</option></term> + <listitem> + <para>Show the version of the program.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--replace</option></term> + <listitem> + <para>Replace the currently running window manager.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--reconfigure</option></term> + <listitem> + <para>If Openbox is already running on the display, tell it to + reload its configuration.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--restart</option></term> + <listitem> + <para>If Openbox is already running on the display, tell it to + restart. This is useful if you have upgraded Openbox and don't + want to restart X.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--sm-disable</option></term> + <listitem> + <para>Do not connect to the session manager.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--sync</option></term> + <listitem> + <para>Run in synchronous mode (for debugging).</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--debug</option></term> + <listitem> + <para>Display debugging output.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--debug-focus</option></term> + <listitem> + <para>Display debugging output for focus handling.</para> + </listitem> + </varlistentry> + <varlistentry> + <term><option>--debug-xinerama</option></term> + <listitem> + <para>Split the display into two fake xinerama regions, if + xinerama is not already enabled. This is for debugging + xinerama support.</para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + <refsect1> + <title>SEE ALSO</title> + + <para>obconf (1), openbox-session(1), openbox-gnome-session(1), + openbox-kde-session(1).</para> + + <para>The program's full documentation is available on the website: + <literal>http://openbox.org/</literal></para> + + <para>Please report bugs to: <literal>http://bugzilla.icculus.org/ + </literal></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> + + diff --git a/doc/rc-mouse-focus.xml b/doc/rc-mouse-focus.xml new file mode 100644 index 0000000..dc7f2e9 --- /dev/null +++ b/doc/rc-mouse-focus.xml @@ -0,0 +1,636 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- Do not edit this file, it will be overwritten on install. + Copy the file to $HOME/.config/openbox/ instead. --> + +<openbox_config xmlns="http://openbox.org/3.4/rc" + xmlns:xi="http://www.w3.org/2001/XInclude"> + +<resistance> + <strength>10</strength> + <screen_edge_strength>20</screen_edge_strength> +</resistance> + +<focus> + <focusNew>yes</focusNew> + <!-- always try to focus new windows when they appear. other rules do + apply --> + <followMouse>yes</followMouse> + <!-- move focus to a window when you move the mouse into it --> + <focusLast>no</focusLast> + <!-- focus the last used window when changing desktops, instead of the one + under the mouse pointer. when followMouse is enabled --> + <underMouse>yes</underMouse> + <!-- move focus under the mouse, even when the mouse is not moving --> + <focusDelay>200</focusDelay> + <!-- when followMouse is enabled, the mouse must be inside the window for + this many milliseconds (1000 = 1 sec) before moving focus to it --> + <raiseOnFocus>no</raiseOnFocus> + <!-- when followMouse is enabled, and a window is given focus by moving the + mouse into it, also raise the window --> +</focus> + +<placement> + <policy>Smart</policy> + <!-- 'Smart' or 'UnderMouse' --> +</placement> + +<theme> + <name>Clearlooks</name> + <titleLayout>NLIMC</titleLayout> + <!-- + avaible characters are NDSLIMC, each can occur at most once. + N: window icon + L: window label (AKA title). + I: iconify + M: maximize + C: close + S: shade (roll up/down) + D: omnipresent (on all desktops). + --> + <keepBorder>yes</keepBorder> + <animateIconify>yes</animateIconify> + <font place="ActiveWindow"> + <name>sans</name> + <size>8</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="InactiveWindow"> + <name>sans</name> + <size>8</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="MenuHeader"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>normal</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="MenuItem"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>normal</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> + <font place="OnScreenDisplay"> + <name>sans</name> + <size>9</size> + <!-- font size in points --> + <weight>bold</weight> + <!-- 'bold' or 'normal' --> + <slant>normal</slant> + <!-- 'italic' or 'normal' --> + </font> +</theme> + +<desktops> + <!-- this stuff is only used at startup, pagers allow you to change them + during a session + + these are default values to use when other ones are not already set + by other applications, or saved in your session + + use obconf if you want to change these without having to log out + and back in --> + <number>4</number> + <firstdesk>1</firstdesk> + <names> + <!-- set names up here if you want to, like this: + <name>desktop 1</name> + <name>desktop 2</name> + --> + </names> +</desktops> + +<resize> + <drawContents>yes</drawContents> + <popupShow>Nonpixel</popupShow> + <!-- 'Always', 'Never', or 'Nonpixel' (xterms and such) --> + <popupPosition>Center</popupPosition> + <!-- 'Center' or 'Top' --> +</resize> + +<dock> + <position>TopLeft</position> + <!-- (Top|Bottom)(Left|Right|)|Top|Bottom|Left|Right|Floating --> + <floatingX>0</floatingX> + <floatingY>0</floatingY> + <noStrut>no</noStrut> + <stacking>Above</stacking> + <!-- 'Above', 'Normal', or 'Below' --> + <direction>Vertical</direction> + <!-- 'Vertical' or 'Horizontal' --> + <autoHide>no</autoHide> + <hideDelay>300</hideDelay> + <!-- in milliseconds (1000 = 1 second) --> + <showDelay>300</showDelay> + <!-- in milliseconds (1000 = 1 second) --> + <moveButton>Middle</moveButton> + <!-- 'Left', 'Middle', 'Right' --> +</dock> + +<keyboard> + <chainQuitKey>C-g</chainQuitKey> + + <!-- Keybindings for desktop switching --> + <keybind key="C-A-Left"> + <action name="DesktopLeft"><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Right"> + <action name="DesktopRight"><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Up"> + <action name="DesktopUp"><wrap>no</wrap></action> + </keybind> + <keybind key="C-A-Down"> + <action name="DesktopDown"><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Left"> + <action name="SendToDesktopLeft"><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Right"> + <action name="SendToDesktopRight"><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Up"> + <action name="SendToDesktopUp"><wrap>no</wrap></action> + </keybind> + <keybind key="S-A-Down"> + <action name="SendToDesktopDown"><wrap>no</wrap></action> + </keybind> + <keybind key="W-F1"> + <action name="Desktop"><desktop>1</desktop></action> + </keybind> + <keybind key="W-F2"> + <action name="Desktop"><desktop>2</desktop></action> + </keybind> + <keybind key="W-F3"> + <action name="Desktop"><desktop>3</desktop></action> + </keybind> + <keybind key="W-F4"> + <action name="Desktop"><desktop>4</desktop></action> + </keybind> + <keybind key="W-d"> + <action name="ToggleShowDesktop"/> + </keybind> + + <!-- Keybindings for windows --> + <keybind key="A-F4"> + <action name="Close"/> + </keybind> + <keybind key="A-Escape"> + <action name="Lower"/> + </keybind> + <keybind key="A-space"> + <action name="ShowMenu"><menu>client-menu</menu></action> + </keybind> + + <!-- Keybindings for window switching --> + <keybind key="A-Tab"> + <action name="NextWindow"/> + </keybind> + <keybind key="A-S-Tab"> + <action name="PreviousWindow"/> + </keybind> + <keybind key="C-A-Tab"> + <action name="NextWindow"> + <panels>yes</panels><desktop>yes</desktop> + </action> + </keybind> + + <!-- Keybindings for running applications --> + <keybind key="W-e"> + <action name="Execute"> + <startupnotify> + <enabled>true</enabled> + <name>Konqueror</name> + </startupnotify> + <command>kfmclient openProfile filemanagement</command> + </action> + </keybind> +</keyboard> + +<mouse> + <dragThreshold>8</dragThreshold> + <!-- number of pixels the mouse must move before a drag begins --> + <doubleClickTime>500</doubleClickTime> + <!-- in milliseconds (1000 = 1 second) --> + + <context name="Frame"> + <mousebind button="A-Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="A-Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="A-Left" action="Drag"> + <action name="Move"/> + </mousebind> + + <mousebind button="A-Right" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="A-Right" action="Press"> + <action name="Raise"/> + </mousebind> + <mousebind button="A-Right" action="Drag"> + <action name="Resize"/> + </mousebind> + + <mousebind button="A-Middle" action="Press"> + <action name="Focus"/> + <action name="Lower"/> + </mousebind> + + <mousebind button="A-Up" action="Click"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="A-Down" action="Click"> + <action name="DesktopNext"/> + </mousebind> + <mousebind button="C-A-Up" action="Click"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="C-A-Down" action="Click"> + <action name="DesktopNext"/> + </mousebind> + <mousebind button="A-S-Up" action="Click"> + <action name="SendToDesktopPrevious"/> + </mousebind> + <mousebind button="A-S-Down" action="Click"> + <action name="SendToDesktopNext"/> + </mousebind> + </context> + + <context name="Titlebar"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Move"/> + </mousebind> + <mousebind button="Left" action="DoubleClick"> + <action name="ToggleMaximizeFull"/> + </mousebind> + + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + <action name="Lower"/> + </mousebind> + + <mousebind button="Up" action="Click"> + <action name="Shade"/> + </mousebind> + <mousebind button="Down" action="Click"> + <action name="Unshade"/> + </mousebind> + + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="Top"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>top</edge></action> + </mousebind> + </context> + + <context name="Left"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>left</edge></action> + </mousebind> + </context> + + <context name="Right"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>right</edge></action> + </mousebind> + </context> + + <context name="Handle"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"><edge>bottom</edge></action> + </mousebind> + + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + <action name="Lower"/> + </mousebind> + + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="BLCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"/> + </mousebind> + </context> + + <context name="BRCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"/> + </mousebind> + </context> + + <context name="TLCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"/> + </mousebind> + </context> + + <context name="TRCorner"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Raise"/> + </mousebind> + <mousebind button="Left" action="Drag"> + <action name="Resize"/> + </mousebind> + </context> + + <context name="Client"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + </mousebind> + </context> + + <context name="Icon"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + <action name="ShowMenu"><menu>client-menu</menu></action> + </mousebind> + </context> + + <context name="AllDesktops"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleOmnipresent"/> + </mousebind> + </context> + + <context name="Shade"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleShade"/> + </mousebind> + </context> + + <context name="Iconify"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Iconify"/> + </mousebind> + </context> + + <context name="Maximize"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Middle" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="ToggleMaximizeFull"/> + </mousebind> + <mousebind button="Middle" action="Click"> + <action name="ToggleMaximizeVert"/> + </mousebind> + <mousebind button="Right" action="Click"> + <action name="ToggleMaximizeHorz"/> + </mousebind> + </context> + + <context name="Close"> + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Left" action="Click"> + <action name="Close"/> + </mousebind> + </context> + + <context name="Desktop"> + <mousebind button="Up" action="Press"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="Down" action="Press"> + <action name="DesktopNext"/> + </mousebind> + + <mousebind button="A-Up" action="Press"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="A-Down" action="Press"> + <action name="DesktopNext"/> + </mousebind> + <mousebind button="C-A-Up" action="Press"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="C-A-Down" action="Press"> + <action name="DesktopNext"/> + </mousebind> + + <mousebind button="Left" action="Press"> + <action name="Focus"/> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="Focus"/> + </mousebind> + </context> + + <context name="Root"> + <!-- Menus --> + <mousebind button="Middle" action="Press"> + <action name="ShowMenu"><menu>client-list-combined-menu</menu></action> + </mousebind> + <mousebind button="Right" action="Press"> + <action name="ShowMenu"><menu>root-menu</menu></action> + </mousebind> + </context> + + <context name="MoveResize"> + <mousebind button="Up" action="Press"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="Down" action="Press"> + <action name="DesktopNext"/> + </mousebind> + <mousebind button="A-Up" action="Press"> + <action name="DesktopPrevious"/> + </mousebind> + <mousebind button="A-Down" action="Press"> + <action name="DesktopNext"/> + </mousebind> + </context> +</mouse> + +<menu> + <!-- You can specify more than one menu file in here and they are all loaded, + just don't make menu ids clash or, well, it'll be kind of pointless --> + + <!-- default menu file (or custom one in $HOME/.config/openbox/) --> + <file>menu.xml</file> + <hideDelay>200</hideDelay> + <middle>no</middle> + <submenuShowDelay>100</submenuShowDelay> + <applicationIcons>yes</applicationIcons> +</menu> + +<applications> +<!-- + # this is an example with comments through out. use these to make your + # own rules, but without the comments of course. + + <application name="first element of window's WM_CLASS property (see xprop)" + class="second element of window's WM_CLASS property (see xprop)" + role="the window's WM_WINDOW_ROLE property (see xprop)"> + # the name or the class can be set, or both. this is used to match + # windows when they appear. role can optionally be set as well, to + # further restrict your matches. + + # the name, class, and role use simple wildcard matching such as those + # used by a shell. you can use * to match any characters and ? to match + # any single character. + + # when multiple rules match a window, they will all be applied, in the + # order that they appear in this list + + + # each element can be left out or set to 'default' to specify to not + # change that attribute of the window + + <decor>yes</decor> + # enable or disable window decorations + + <shade>no</shade> + # make the window shaded when it appears, or not + + <position> + # the position is only used if both an x and y coordinate are provided + # (and not set to 'default') + <x>center</x> + # a number like 50, or 'center' to center on screen + <y>200</y> + # a number like 50, or 'center' to center on screen + <monitor>1</monitor> + # specifies the monitor in a xinerama setup. + # 1 is the first head, or 'mouse' for wherever the mouse is + </position> + + <focus>yes</focus> + # if the window should try be given focus when it appears. if this is set + # to yes it doesn't guarantee the window will be given focus. some + # restrictions may apply, but Openbox will try to + + <desktop>1</desktop> + # 1 is the first desktop, 'all' for all desktops + + <layer>normal</layer> + # 'above', 'normal', or 'below' + + <iconic>no</iconic> + # make the window iconified when it appears, or not + + <skip_pager>no</skip_pager> + # asks to not be shown in pagers + + <skip_taskbar>no</skip_taskbar> + # asks to not be shown in taskbars. window cycling actions will also + # skip past such windows + + <fullscreen>yes</fullscreen> + # make the window in fullscreen mode when it appears + + <maximized>true</maximized> + # 'Horizontal', 'Vertical' or boolean (yes/no) + </application> + + # end of the example +--> +</applications> + +</openbox_config> diff --git a/gettext.h b/gettext.h new file mode 100644 index 0000000..d71caf0 --- /dev/null +++ b/gettext.h @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + gettext.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +/* Convenience header for conditional use of GNU <libintl.h>. + Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include <libintl.h> + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of <locale.h> a NOP. We don't include <libintl.h> + as well because people using "gettext.h" will not include <libintl.h>, + and also including <libintl.h> would fail on SunOS 4, whereas <locale.h> + is OK. */ +#if defined(__sun) +# include <locale.h> +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# define gettext(Msgid) ((const char *) (Msgid)) +# define dgettext(Domainname, Msgid) ((const char *) (Msgid)) +# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) +# define textdomain(Domainname) ((const char *) (Domainname)) +# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) +# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) + +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +/* Custom macro to make life easier */ +#define _(str) gettext(str) + +#endif /* _LIBGETTEXT_H */ diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..36f96f3 --- /dev/null +++ b/install-sh @@ -0,0 +1,276 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd=$cpprog + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "$0: no input file specified" >&2 + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d "$dst" ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "$0: $src does not exist" >&2 + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "$0: no destination specified" >&2 + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d "$dst" ] + then + dst=$dst/`basename "$src"` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-$defaultIFS}" + +oIFS=$IFS +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS=$oIFS + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp=$pathcomp$1 + shift + + if [ ! -d "$pathcomp" ] ; + then + $mkdirprog "$pathcomp" + else + : + fi + + pathcomp=$pathcomp/ +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd "$dst" && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename "$dst"` + else + dstfile=`basename "$dst" $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename "$dst"` + else + : + fi + +# Make a couple of temp file names in the proper directory. + + dsttmp=$dstdir/#inst.$$# + rmtmp=$dstdir/#rm.$$# + +# Trap to clean up temp files at exit. + + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + +# Move or copy the file name to the temp name + + $doit $instcmd "$src" "$dsttmp" && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi && + +# Now remove or move aside any old file at destination location. We try this +# two ways since rm can't unlink itself on some systems and the destination +# file might be busy for other reasons. In this case, the final cleanup +# might fail but the new file should still install successfully. + +{ + if [ -f "$dstdir/$dstfile" ] + then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null || + $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null || + { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi +} && + +# Now rename the file to the real destination. + + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + +fi && + +# The final little trick to "correctly" pass the exit status to the exit trap. + +{ + (exit 0); exit +} diff --git a/m4/Makefile.am b/m4/Makefile.am new file mode 100644 index 0000000..da7255e --- /dev/null +++ b/m4/Makefile.am @@ -0,0 +1,33 @@ +EXTRA_DIST=\ + codeset.m4 \ + gettext.m4 \ + glibc21.m4 \ + glibc2.m4 \ + iconv.m4 \ + intdiv0.m4 \ + intmax.m4 \ + inttypes_h.m4 \ + inttypes-h.m4 \ + inttypes-pri.m4 \ + lcmessage.m4 \ + lib-ld.m4 \ + lib-link.m4 \ + lib-prefix.m4 \ + lock.m4 \ + longdouble.m4 \ + longlong.m4 \ + nls.m4 \ + openbox.m4 \ + po.m4 \ + printf-posix.m4 \ + progtest.m4 \ + signed.m4 \ + size_max.m4 \ + stdint_h.m4 \ + uintmax_t.m4 \ + ulonglong.m4 \ + visibility.m4 \ + wchar_t.m4 \ + wint_t.m4 \ + x11.m4 \ + xsize.m4 diff --git a/m4/openbox.m4 b/m4/openbox.m4 new file mode 100644 index 0000000..0c37a58 --- /dev/null +++ b/m4/openbox.m4 @@ -0,0 +1,122 @@ +# OB_DEBUG() +# +# Check if the user has requested a debug build. +# Sets the DEBUG or NDEBUG variables as appropriate +# Sets the CVS environment variable when building CVS sources. +AC_DEFUN([OB_DEBUG], +[ + AC_MSG_CHECKING([build type]) + + AC_ARG_ENABLE([strict-ansi], + AC_HELP_STRING([--enable-strict-ansi],[Enable strict ANSI compliance build [[default=no]]]), + [STRICT=$enableval], [STRICT="no"]) + + AC_ARG_ENABLE([super-warnings], + AC_HELP_STRING([--enable-super-warnings],[Enable extra compiler warnings [[default=no]]]), + [SUPERWARN=$enableval], [SUPERWARN="no"]) + + AC_ARG_ENABLE([debug], + AC_HELP_STRING([--enable-debug],[build a debug version [[default=no]]]), + [DEBUG=$enableval], [DEBUG="no"]) + + AC_ARG_ENABLE([gprof], + AC_HELP_STRING([--enable-gprof],[Enable gprof profiling output [[default=no]]]), + [PROF=$enableval], [PROF="no"]) + + AC_ARG_ENABLE([gprof-libc], + AC_HELP_STRING([--enable-gprof-libc],[Link against libc with profiling support [[default=no]]]), + [PROFLC=$enableval], [PROFLC="no"]) + + if test "$PROFLC" = "yes"; then + PROF="yes" # always enable profiling then + fi + + TEST="" + test "${PACKAGE_VERSION%*alpha*}" != "$PACKAGE_VERSION" && TEST="yes" + test "${PACKAGE_VERSION%*beta*}" != "$PACKAGE_VERSION" && TEST="yes" + test "$TEST" = "yes" && DEBUG="yes" + + if test "$DEBUG" = "yes"; then + MSG="DEBUG" + else + MSG="RELEASE" + fi + if test "$TEST" = "yes"; then + MSG="$MSG (test release)" + fi + if test "$STRICT" = "yes"; then + MSG="$MSG with strict ANSI compliance" + fi + if test "$SUPERWARN" = "yes"; then + MSG="$MSG with super warnings" + fi + AC_MSG_RESULT([$MSG]) + + test "$DEBUG" = "yes" && \ + AC_DEFINE([DEBUG], [1], [Creating a debug build]) +]) + + +# OB_COMPILER_FLAGS() +# +# Check what compiler is being used for compilation. +# It sets the CFLAGS variable appropriately for the compiler, including flags +# for debug builds. +AC_DEFUN([OB_COMPILER_FLAGS], +[ + AC_REQUIRE([AC_PROG_CPP]) + AC_REQUIRE([AC_PROG_CC]) + + FLAGS="" + L="" + + if test "$DEBUG" = "yes"; then + FLAGS="-DDEBUG" + else + FLAGS="-DNDEBUG -DG_DISABLE_ASSERT" + fi + + # Check what compiler we are using + AC_MSG_CHECKING([for GNU CC]) + if test "$GCC" = "yes"; then + AC_MSG_RESULT([yes]) + if test "$DEBUG" = "yes"; then + FLAGS="$FLAGS -O0 -ggdb -fno-inline -Wwrite-strings" + FLAGS="$FLAGS -Wall -Wsign-compare -Waggregate-return" + FLAGS="$FLAGS -Wbad-function-cast -Wpointer-arith" + FLAGS="$FLAGS -Wno-write-strings" + # for Python.h + #FLAGS="$FLAGS -Wno-long-long" + fi + if test "$SUPERWARN" = "yes"; then + # glib can't handle -Wcast-qual + FLAGS="$FLAGS -Wcast-qual -Wextra" + fi + if test "$STRICT" = "yes"; then + FLAGS="$FLAGS -ansi -pedantic -D_XOPEN_SOURCE" + fi + if test "$PROF" = "yes"; then + FLAGS="$FLAGS -pg -fno-inline" + fi + if test "$PROFLC" = "yes"; then + L="$L -lc_p -lm_p" + fi + FLAGS="$FLAGS -fno-strict-aliasing" + fi + AC_MSG_CHECKING([for compiler specific flags]) + AC_MSG_RESULT([$FLAGS]) + CFLAGS="$CFLAGS $FLAGS" + LIBS="$LIBS $L" +]) + +AC_DEFUN([OB_NLS], +[ + AC_ARG_ENABLE([nls], + AC_HELP_STRING([--enable-nls],[Enable NLS translations [[default=yes]]]), + [NLS=$enableval], [NLS="yes"]) + + if test "$NLS" = yes; then + DEFS="$DEFS -DENABLE_NLS" + fi +]) + diff --git a/m4/x11.m4 b/m4/x11.m4 new file mode 100644 index 0000000..f3b1ede --- /dev/null +++ b/m4/x11.m4 @@ -0,0 +1,443 @@ +# X11_DEVEL() +# +# Check for the presence of the X Window System headers and libraries. +# Sets the CPPFLAGS and LIBS variables as appropriate. +AC_DEFUN([X11_DEVEL], +[ + AC_PATH_XTRA + test "$no_x" = "yes" && \ + AC_MSG_ERROR([The X Window System could not be found.]) + + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + X_LIBS="$X_PRE_LIBS $X_LIBS -lX11" + LIBS="$LIBS $X_LIBS" + + # Check for required functions in -lX11 + AC_CHECK_LIB( + [X11], [XOpenDisplay], + , + AC_MSG_ERROR([Could not find XOpenDisplay in -lX11.]) + ) + + # Restore the old values. Use X_CFLAGS and X_LIBS in + # the Makefiles + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS +]) + +# X11_EXT_XKB() +# +# Check for the presence of the "Xkb" X Window System extension. +# Defines "XKB" and sets the $(XKB) variable to "yes" if the extension is +# present. +AC_DEFUN([X11_EXT_XKB], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([xkb], + AC_HELP_STRING( + [--disable-xkb], + [build without support for xkb extension [default=enabled]]), + [USE=$enableval], [USE="yes"]) + + if test "$USE" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS" + + AC_CHECK_LIB([X11], [XkbBell], + AC_MSG_CHECKING([for X11/XKBlib.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <X11/XKBlib.h> + ], + [ + Display *d; + Window w; + XkbBell(d, w, 0, 0); + ], + [ + AC_MSG_RESULT([yes]) + XKB="yes" + AC_DEFINE([XKB], [1], [Found the XKB extension]) + + XKB_CFLAGS="" + XKB_LIBS="" + AC_SUBST(XKB_CFLAGS) + AC_SUBST(XKB_LIBS) + ], + [ + AC_MSG_RESULT([no]) + XKB="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for the Xkb extension]) + if test "$XKB" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + +# X11_EXT_XRANDR() +# +# Check for the presence of the "XRandR" X Window System extension. +# Defines "XRANDR" and sets the $(XRANDR) variable to "yes" if the extension is +# present. +AC_DEFUN([X11_EXT_XRANDR], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([xrandr], + AC_HELP_STRING( + [--disable-xrandr], + [build without support for xrandr extension [default=enabled]]), + [USE=$enableval], [USE="yes"]) + + if test "$USE" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS -lXext -lXrender -lXrandr" + + AC_CHECK_LIB([Xrandr], [XRRSelectInput], + AC_MSG_CHECKING([for X11/extensions/Xrandr.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/extensions/Xrandr.h> + ], + [ + Display *d; + Drawable r; + int i; + XRRQueryExtension(d, &i, &i); + XRRGetScreenInfo(d, r); + ], + [ + AC_MSG_RESULT([yes]) + XRANDR="yes" + AC_DEFINE([XRANDR], [1], [Found the XRandR extension]) + + XRANDR_CFLAGS="" + XRANDR_LIBS="-lXext -lXrender -lXrandr" + AC_SUBST(XRANDR_CFLAGS) + AC_SUBST(XRANDR_LIBS) + ], + [ + AC_MSG_RESULT([no]) + XRANDR="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for the XRandR extension]) + if test "$XRANDR" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + +# X11_EXT_SHAPE() +# +# Check for the presence of the "Shape" X Window System extension. +# Defines "SHAPE", sets the $(SHAPE) variable to "yes", and sets the $(LIBS) +# appropriately if the extension is present. +AC_DEFUN([X11_EXT_SHAPE], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([xshape], + AC_HELP_STRING( + [--disable-xshape], + [build without support for xshape extension [default=enabled]]), + [USE=$enableval], [USE="yes"]) + + if test "$USE" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS" + + AC_CHECK_LIB([Xext], [XShapeCombineShape], + AC_MSG_CHECKING([for X11/extensions/shape.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <X11/extensions/shape.h> + ], + [ + long foo = ShapeSet; + ], + [ + AC_MSG_RESULT([yes]) + SHAPE="yes" + AC_DEFINE([SHAPE], [1], [Found the XShape extension]) + + XSHAPE_CFLAGS="" + XSHAPE_LIBS="-lXext" + AC_SUBST(XSHAPE_CFLAGS) + AC_SUBST(XSHAPE_LIBS) + ], + [ + AC_MSG_RESULT([no]) + SHAPE="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for the Shape extension]) + if test "$SHAPE" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + + +# X11_EXT_XINERAMA() +# +# Check for the presence of the "Xinerama" X Window System extension. +# Defines "XINERAMA", sets the $(XINERAMA) variable to "yes", and sets the +# $(LIBS) appropriately if the extension is present. +AC_DEFUN([X11_EXT_XINERAMA], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([xinerama], + AC_HELP_STRING( + [--disable-xinerama], + [build without support for xinerama [default=enabled]]), + [USE=$enableval], [USE="yes"]) + + if test "$USE" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS -lXext" + + AC_CHECK_LIB([Xinerama], [XineramaQueryExtension], + [ + AC_MSG_CHECKING([for X11/extensions/Xinerama.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/extensions/Xinerama.h> + ], + [ + XineramaScreenInfo foo; + ], + [ + AC_MSG_RESULT([yes]) + XINERAMA="yes" + AC_DEFINE([XINERAMA], [1], [Enable support of the Xinerama extension]) + XINERAMA_LIBS="-lXext -lXinerama" + AC_SUBST(XINERAMA_LIBS) + ], + [ + AC_MSG_RESULT([no]) + XINERAMA="no" + ]) + ]) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for the Xinerama extension]) + if test "$XINERAMA" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + +# X11_EXT_SYNC() +# +# Check for the presence of the "Sync" X Window System extension. +# Defines "SYNC", sets the $(SYNC) variable to "yes", and sets the $(LIBS) +# appropriately if the extension is present. +AC_DEFUN([X11_EXT_SYNC], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([xsync], + AC_HELP_STRING( + [--disable-xsync], + [build without support for xsync extension [default=enabled]]), + [USE=$enableval], [USE="yes"]) + + if test "$USE" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS" + + AC_CHECK_LIB([Xext], [XSyncInitialize], + AC_MSG_CHECKING([for X11/extensions/sync.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <X11/extensions/sync.h> + ], + [ + XSyncValueType foo; + ], + [ + AC_MSG_RESULT([yes]) + SYNC="yes" + AC_DEFINE([SYNC], [1], [Found the XSync extension]) + + XSYNC_CFLAGS="" + XSYNC_LIBS="-lXext" + AC_SUBST(XSYNC_CFLAGS) + AC_SUBST(XSYNC_LIBS) + ], + [ + AC_MSG_RESULT([no]) + SYNC="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for the Sync extension]) + if test "$SYNC" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + +# X11_EXT_AUTH() +# +# Check for the presence of the "Xau" X Window System extension. +# Defines "AUTH, sets the $(AUTH) variable to "yes", and sets the $(LIBS) +# appropriately if the extension is present. +AC_DEFUN([X11_EXT_AUTH], +[ + AC_REQUIRE([X11_DEVEL]) + + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS" + + AC_CHECK_LIB([Xau], [XauReadAuth], + AC_MSG_CHECKING([for X11/Xauth.h]) + AC_TRY_LINK( + [ + #include <X11/Xlib.h> + #include <X11/Xutil.h> + #include <X11/Xauth.h> + ], + [ + ], + [ + AC_MSG_RESULT([yes]) + AUTH="yes" + AC_DEFINE([AUTH], [1], [Found the Xauth extension]) + + XAUTH_CFLAGS="" + XAUTH_LIBS="-lXau" + AC_SUBST(XAUTH_CFLAGS) + AC_SUBST(XAUTH_LIBS) + ], + [ + AC_MSG_RESULT([no]) + AUTH="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + + AC_MSG_CHECKING([for the Xauth extension]) + if test "$AUTH" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + +# X11_SM() +# +# Check for the presence of SMlib for session management. +# Defines "USE_SM" if SMlib is present. +AC_DEFUN([X11_SM], +[ + AC_REQUIRE([X11_DEVEL]) + + AC_ARG_ENABLE([session-management], + AC_HELP_STRING( + [--disable-session-management], + [build without support for session managers [default=enabled]]), + [SM=$enableval], [SM="yes"]) + + if test "$SM" = "yes"; then + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_LIBS" + + SM="no" + + AC_CHECK_LIB([SM], [SmcSaveYourselfDone], [ + AC_CHECK_HEADERS([X11/SM/SMlib.h], [ + SM_CFLAGS="$X_CFLAGS" + SM_LIBS="-lSM -lICE" + AC_DEFINE(USE_SM, 1, [Use session management]) + AC_SUBST(SM_CFLAGS) + AC_SUBST(SM_LIBS) + SM="yes" + ]) + ]) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + fi + + AC_MSG_CHECKING([for session management support]) + if test "$SM" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) @@ -0,0 +1,33 @@ +#!/bin/sh + +if test "$1" = "ubuntu"; then + APPEND="ubuntu1"; +else + if test "$1" = "debian"; then + APPEND=""; + else + echo "Usage: makedeb [ubuntu|debian]"; + exit 1; + fi +fi + +# get the version from configure.ac +VERSION=$(grep AC_INIT\(\\\[openbox\\] configure.ac|cut -d [ -f 3-|cut -d ] -f 1) + +echo +echo "Building deb for openbox-$VERSION-0$APPEND" +echo + +TIME="$(date '+%a, %d %B %Y %T %z')" + +IN=debian/changelog.in +OUT=debian/changelog + +# make sure configure gets run with the right parameters +make distclean > /dev/null || rm -f config.status + +rm -f $OUT + +sed -e "s!@time@!$TIME!" -e "s!@version@!$VERSION-0$APPEND!" $IN >$OUT && \ +fakeroot debian/rules binary && \ +make distclean > /dev/null diff --git a/obrender/Makefile b/obrender/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/obrender/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/obrender/button.c b/obrender/button.c new file mode 100644 index 0000000..14a454d --- /dev/null +++ b/obrender/button.c @@ -0,0 +1,117 @@ +#include "render.h" +#include "button.h" +#include "instance.h" +#include "mask.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <string.h> + +RrButton *RrButtonNew (const RrInstance *inst) +{ + RrButton *out = NULL; + + out = g_new(RrButton, 1); + out->inst = inst; + + /* no need to alloc colors, set them null (for freeing later) */ + out->focused_unpressed_color = NULL; + out->unfocused_unpressed_color = NULL; + out->focused_pressed_color = NULL; + out->unfocused_pressed_color = NULL; + out->disabled_focused_color = NULL; + out->disabled_unfocused_color = NULL; + out->hover_focused_color = NULL; + out->hover_unfocused_color = NULL; + out->toggled_hover_focused_color = NULL; + out->toggled_hover_unfocused_color = NULL; + out->toggled_focused_pressed_color = NULL; + out->toggled_unfocused_pressed_color = NULL; + out->toggled_focused_unpressed_color = NULL; + out->toggled_unfocused_unpressed_color = NULL; + + /* same with masks */ + out->mask = NULL; + out->pressed_mask = NULL; + out->disabled_mask = NULL; + out->hover_mask = NULL; + out->toggled_mask = NULL; + out->toggled_hover_mask = NULL; + out->toggled_pressed_mask = NULL; + + /* allocate appearances */ + out->a_focused_unpressed = RrAppearanceNew(inst, 1); + out->a_unfocused_unpressed = RrAppearanceNew(inst, 1); + out->a_focused_pressed = RrAppearanceNew(inst, 1); + out->a_unfocused_pressed = RrAppearanceNew(inst, 1); + out->a_disabled_focused = RrAppearanceNew(inst, 1); + out->a_disabled_unfocused = RrAppearanceNew(inst, 1); + out->a_hover_focused = RrAppearanceNew(inst, 1); + out->a_hover_unfocused = RrAppearanceNew(inst, 1); + out->a_toggled_focused_unpressed = RrAppearanceNew(inst, 1); + out->a_toggled_unfocused_unpressed = RrAppearanceNew(inst, 1); + out->a_toggled_focused_pressed = RrAppearanceNew(inst, 1); + out->a_toggled_unfocused_pressed = RrAppearanceNew(inst, 1); + out->a_toggled_hover_focused = RrAppearanceNew(inst, 1); + out->a_toggled_hover_unfocused = RrAppearanceNew(inst, 1); + + return out; +} + +void RrButtonFree(RrButton *b) +{ + /* colors */ + if (b->focused_unpressed_color) + RrColorFree(b->focused_unpressed_color); + if (b->unfocused_unpressed_color) + RrColorFree(b->unfocused_unpressed_color); + if (b->focused_pressed_color) + RrColorFree(b->focused_pressed_color); + if (b->unfocused_pressed_color) + RrColorFree(b->unfocused_pressed_color); + if (b->disabled_focused_color) + RrColorFree(b->disabled_focused_color); + if (b->disabled_unfocused_color) + RrColorFree(b->disabled_unfocused_color); + if (b->hover_focused_color) + RrColorFree(b->hover_focused_color); + if (b->hover_unfocused_color) + RrColorFree(b->hover_unfocused_color); + if (b->toggled_hover_focused_color) + RrColorFree(b->toggled_hover_focused_color); + if (b->toggled_hover_unfocused_color) + RrColorFree(b->toggled_hover_unfocused_color); + if (b->toggled_focused_pressed_color) + RrColorFree(b->toggled_focused_pressed_color); + if (b->toggled_unfocused_pressed_color) + RrColorFree(b->toggled_unfocused_pressed_color); + if (b->toggled_focused_unpressed_color) + RrColorFree(b->toggled_focused_unpressed_color); + if (b->toggled_unfocused_unpressed_color) + RrColorFree(b->toggled_unfocused_unpressed_color); + + /* masks */ + if (b->mask) RrPixmapMaskFree(b->mask); + if (b->pressed_mask) RrPixmapMaskFree(b->pressed_mask); + if (b->disabled_mask) RrPixmapMaskFree(b->disabled_mask); + if (b->hover_mask) RrPixmapMaskFree(b->hover_mask); + if (b->toggled_mask) RrPixmapMaskFree(b->toggled_mask); + if (b->toggled_hover_mask) RrPixmapMaskFree(b->toggled_hover_mask); + if (b->toggled_pressed_mask) RrPixmapMaskFree(b->toggled_pressed_mask); + + /* appearances */ + RrAppearanceFree(b->a_focused_unpressed); + RrAppearanceFree(b->a_unfocused_unpressed); + RrAppearanceFree(b->a_focused_pressed); + RrAppearanceFree(b->a_unfocused_pressed); + RrAppearanceFree(b->a_disabled_focused); + RrAppearanceFree(b->a_disabled_unfocused); + RrAppearanceFree(b->a_hover_focused); + RrAppearanceFree(b->a_hover_unfocused); + RrAppearanceFree(b->a_toggled_focused_unpressed); + RrAppearanceFree(b->a_toggled_unfocused_unpressed); + RrAppearanceFree(b->a_toggled_focused_pressed); + RrAppearanceFree(b->a_toggled_unfocused_pressed); + RrAppearanceFree(b->a_toggled_hover_focused); + RrAppearanceFree(b->a_toggled_hover_unfocused); +} diff --git a/obrender/button.h b/obrender/button.h new file mode 100644 index 0000000..659c3ea --- /dev/null +++ b/obrender/button.h @@ -0,0 +1,11 @@ +#ifndef __button_h +#define __button_h + +#include "render.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <glib.h> + + +#endif /* __button_h */ diff --git a/obrender/color.c b/obrender/color.c new file mode 100644 index 0000000..221ebc4 --- /dev/null +++ b/obrender/color.c @@ -0,0 +1,366 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + color.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "color.h" +#include "instance.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <string.h> + +void RrColorAllocateGC(RrColor *in) +{ + XGCValues gcv; + + gcv.foreground = in->pixel; + gcv.cap_style = CapProjecting; + in->gc = XCreateGC(RrDisplay(in->inst), + RrRootWindow(in->inst), + GCForeground | GCCapStyle, &gcv); +} + +RrColor *RrColorParse(const RrInstance *inst, gchar *colorname) +{ + XColor xcol; + + g_assert(colorname != NULL); + /* get rgb values from colorname */ + + xcol.red = 0; + xcol.green = 0; + xcol.blue = 0; + xcol.pixel = 0; + if (!XParseColor(RrDisplay(inst), RrColormap(inst), colorname, &xcol)) { + g_message("Unable to parse color '%s'", colorname); + return NULL; + } + return RrColorNew(inst, xcol.red >> 8, xcol.green >> 8, xcol.blue >> 8); +} + +/*#define NO_COLOR_CACHE*/ +#ifdef DEBUG +gint id; +#endif + +RrColor *RrColorNew(const RrInstance *inst, gint r, gint g, gint b) +{ + /* this should be replaced with something far cooler */ + RrColor *out = NULL; + XColor xcol; + gint key; + + g_assert(r >= 0 && r < 256); + g_assert(g >= 0 && g < 256); + g_assert(b >= 0 && b < 256); + + key = (r << 24) + (g << 16) + (b << 8); +#ifndef NO_COLOR_CACHE + if ((out = g_hash_table_lookup(RrColorHash(inst), &key))) { + out->refcount++; + } else { +#endif + xcol.red = (r << 8) | r; + xcol.green = (g << 8) | g; + xcol.blue = (b << 8) | b; + if (XAllocColor(RrDisplay(inst), RrColormap(inst), &xcol)) { + out = g_slice_new(RrColor); + out->inst = inst; + out->r = xcol.red >> 8; + out->g = xcol.green >> 8; + out->b = xcol.blue >> 8; + out->gc = None; + out->pixel = xcol.pixel; + out->key = key; + out->refcount = 1; +#ifdef DEBUG + out->id = id++; +#endif +#ifndef NO_COLOR_CACHE + g_hash_table_insert(RrColorHash(inst), &out->key, out); + } +#endif + } + return out; +} + +RrColor *RrColorCopy(RrColor* c) +{ + return RrColorNew(c->inst, c->r, c->g, c->b); +} + +void RrColorFree(RrColor *c) +{ + if (c) { + if (--c->refcount < 1) { +#ifndef NO_COLOR_CACHE + g_assert(g_hash_table_lookup(RrColorHash(c->inst), &c->key)); + g_hash_table_remove(RrColorHash(c->inst), &c->key); +#endif + if (c->pixel) XFreeColors(RrDisplay(c->inst), RrColormap(c->inst), + &c->pixel, 1, 0); + if (c->gc) XFreeGC(RrDisplay(c->inst), c->gc); + g_slice_free(RrColor, c); + } + } +} + +void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im) +{ + gint r, g, b; + gint x,y; + RrPixel32 *p32 = (RrPixel32 *) im->data; + RrPixel16 *p16 = (RrPixel16 *) im->data; + RrPixel8 *p8 = (RrPixel8 *) im->data; + switch (im->bits_per_pixel) { + case 32: + if ((RrRedOffset(inst) != RrDefaultRedOffset) || + (RrBlueOffset(inst) != RrDefaultBlueOffset) || + (RrGreenOffset(inst) != RrDefaultGreenOffset)) { + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + r = (data[x] >> RrDefaultRedOffset) & 0xFF; + g = (data[x] >> RrDefaultGreenOffset) & 0xFF; + b = (data[x] >> RrDefaultBlueOffset) & 0xFF; + p32[x] = (r << RrRedOffset(inst)) + + (g << RrGreenOffset(inst)) + + (b << RrBlueOffset(inst)); + } + data += im->width; + p32 += im->width; + } + } else im->data = (gchar*) data; + break; + case 24: + { + /* reverse the ordering, shifting left 16bit should be the first byte + out of three, etc */ + const guint roff = (16 - RrRedOffset(inst)) / 8; + const guint goff = (16 - RrGreenOffset(inst)) / 8; + const guint boff = (16 - RrBlueOffset(inst)) / 8; + gint outx; + for (y = 0; y < im->height; y++) { + for (x = 0, outx = 0; x < im->width; x++, outx += 3) { + r = (data[x] >> RrDefaultRedOffset) & 0xFF; + g = (data[x] >> RrDefaultGreenOffset) & 0xFF; + b = (data[x] >> RrDefaultBlueOffset) & 0xFF; + p8[outx+roff] = r; + p8[outx+goff] = g; + p8[outx+boff] = b; + } + data += im->width; + p8 += im->bytes_per_line; + } + break; + } + case 16: + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + r = (data[x] >> RrDefaultRedOffset) & 0xFF; + r = r >> RrRedShift(inst); + g = (data[x] >> RrDefaultGreenOffset) & 0xFF; + g = g >> RrGreenShift(inst); + b = (data[x] >> RrDefaultBlueOffset) & 0xFF; + b = b >> RrBlueShift(inst); + p16[x] = (r << RrRedOffset(inst)) + + (g << RrGreenOffset(inst)) + + (b << RrBlueOffset(inst)); + } + data += im->width; + p16 += im->bytes_per_line/2; + } + break; + case 8: + if (RrVisual(inst)->class == TrueColor) { + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + r = (data[x] >> RrDefaultRedOffset) & 0xFF; + r = r >> RrRedShift(inst); + g = (data[x] >> RrDefaultGreenOffset) & 0xFF; + g = g >> RrGreenShift(inst); + b = (data[x] >> RrDefaultBlueOffset) & 0xFF; + b = b >> RrBlueShift(inst); + p8[x] = (r << RrRedOffset(inst)) + + (g << RrGreenOffset(inst)) + + (b << RrBlueOffset(inst)); + } + data += im->width; + p8 += im->bytes_per_line; + } + } else { + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + p8[x] = RrPickColor(inst, + data[x] >> RrDefaultRedOffset, + data[x] >> RrDefaultGreenOffset, + data[x] >> RrDefaultBlueOffset)->pixel; + } + data += im->width; + p8 += im->bytes_per_line; + } + } + break; + default: + g_error("This image bit depth (%i) is currently unhandled", im->bits_per_pixel); + + } +} + +XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b) +{ + r = (r & 0xff) >> (8-RrPseudoBPC(inst)); + g = (g & 0xff) >> (8-RrPseudoBPC(inst)); + b = (b & 0xff) >> (8-RrPseudoBPC(inst)); + return &RrPseudoColors(inst)[(r << (2*RrPseudoBPC(inst))) + + (g << (1*RrPseudoBPC(inst))) + + b]; +} + +static void swap_byte_order(XImage *im) +{ + gint x, y, di; + + di = 0; + for (y = 0; y < im->height; ++y) { + for (x = 0; x < im->height; ++x) { + gchar *c = &im->data[di + x * im->bits_per_pixel / 8]; + gchar t; + + switch (im->bits_per_pixel) { + case 32: + t = c[2]; + c[2] = c[3]; + c[3] = t; + case 16: + t = c[0]; + c[0] = c[1]; + c[1] = t; + case 8: + case 1: + break; + default: + g_error("Your bit depth (%i) is currently unhandled", + im->bits_per_pixel); + } + } + di += im->bytes_per_line; + } + + if (im->byte_order == LSBFirst) + im->byte_order = MSBFirst; + else + im->byte_order = LSBFirst; +} + +void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im) +{ + gint r, g, b; + gint x,y; + RrPixel32 *p32 = (RrPixel32 *) im->data; + RrPixel16 *p16 = (RrPixel16 *) im->data; + guchar *p8 = (guchar *)im->data; + + if (im->byte_order != LSBFirst) + swap_byte_order(im); + + switch (im->bits_per_pixel) { + case 32: + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + r = (p32[x] >> RrRedOffset(inst)) & 0xff; + g = (p32[x] >> RrGreenOffset(inst)) & 0xff; + b = (p32[x] >> RrBlueOffset(inst)) & 0xff; + data[x] = (r << RrDefaultRedOffset) + + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset) + + (0xff << RrDefaultAlphaOffset); + } + data += im->width; + p32 += im->bytes_per_line/4; + } + break; + case 16: + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + r = (p16[x] & RrRedMask(inst)) >> + RrRedOffset(inst) << + RrRedShift(inst); + g = (p16[x] & RrGreenMask(inst)) >> + RrGreenOffset(inst) << + RrGreenShift(inst); + b = (p16[x] & RrBlueMask(inst)) >> + RrBlueOffset(inst) << + RrBlueShift(inst); + data[x] = (r << RrDefaultRedOffset) + + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset) + + (0xff << RrDefaultAlphaOffset); + } + data += im->width; + p16 += im->bytes_per_line/2; + } + break; + case 8: + g_error("This image bit depth (%i) is currently unhandled", 8); + break; + case 1: + for (y = 0; y < im->height; y++) { + for (x = 0; x < im->width; x++) { + if (!(((p8[x / 8]) >> (x % 8)) & 0x1)) + data[x] = 0xff << RrDefaultAlphaOffset; /* black */ + else + data[x] = 0xffffffff; /* white */ + } + data += im->width; + p8 += im->bytes_per_line; + } + break; + default: + g_error("This image bit depth (%i) is currently unhandled", + im->bits_per_pixel); + } +} + +gint RrColorRed(const RrColor *c) +{ + return c->r; +} + +gint RrColorGreen(const RrColor *c) +{ + return c->g; +} + +gint RrColorBlue(const RrColor *c) +{ + return c->b; +} + +gulong RrColorPixel(const RrColor *c) +{ + return c->pixel; +} + +GC RrColorGC(RrColor *c) +{ + if (!c->gc) + RrColorAllocateGC(c); + return c->gc; +} diff --git a/obrender/color.h b/obrender/color.h new file mode 100644 index 0000000..26fa7af --- /dev/null +++ b/obrender/color.h @@ -0,0 +1,51 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + color.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __color_h +#define __color_h + +#include "render.h" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <glib.h> + +struct _RrColor { + const RrInstance *inst; + + gint r; + gint g; + gint b; + gulong pixel; + GC gc; + + gint key; + gint refcount; + +#ifdef DEBUG + gint id; +#endif +}; + +void RrColorAllocateGC(RrColor *in); +XColor *RrPickColor(const RrInstance *inst, gint r, gint g, gint b); +void RrReduceDepth(const RrInstance *inst, RrPixel32 *data, XImage *im); +void RrIncreaseDepth(const RrInstance *inst, RrPixel32 *data, XImage *im); + +#endif /* __color_h */ diff --git a/obrender/font.c b/obrender/font.c new file mode 100644 index 0000000..a22e23f --- /dev/null +++ b/obrender/font.c @@ -0,0 +1,382 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + font.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "font.h" +#include "color.h" +#include "mask.h" +#include "theme.h" +#include "geom.h" +#include "instance.h" +#include "gettext.h" + +#include <glib.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> + +static void measure_font(const RrInstance *inst, RrFont *f) +{ + PangoFontMetrics *metrics; + static PangoLanguage *lang = NULL; + + if (lang == NULL) { +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + lang = pango_language_get_default(); +#else + gchar *locale, *p; + /* get the default language from the locale + (based on gtk_get_default_language in gtkmain.c) */ + locale = g_strdup(setlocale(LC_CTYPE, NULL)); + if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */ + if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */ + lang = pango_language_from_string(locale); + g_free(locale); +#endif + } + + /* measure the ascent and descent */ + metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang); + f->ascent = pango_font_metrics_get_ascent(metrics); + f->descent = pango_font_metrics_get_descent(metrics); + pango_font_metrics_unref(metrics); + +} + +RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size, + RrFontWeight weight, RrFontSlant slant) +{ + RrFont *out; + PangoWeight pweight; + PangoStyle pstyle; + PangoAttrList *attrlist; + + out = g_slice_new(RrFont); + out->inst = inst; + out->ref = 1; + out->font_desc = pango_font_description_new(); + out->layout = pango_layout_new(inst->pango); + out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW); + out->shortcut_underline->start_index = 0; + out->shortcut_underline->end_index = 0; + + attrlist = pango_attr_list_new(); + /* shortcut_underline is owned by the attrlist */ + pango_attr_list_insert(attrlist, out->shortcut_underline); + /* the attributes are owned by the layout */ + pango_layout_set_attributes(out->layout, attrlist); + pango_attr_list_unref(attrlist); + + switch (weight) { + case RR_FONTWEIGHT_LIGHT: pweight = PANGO_WEIGHT_LIGHT; break; + case RR_FONTWEIGHT_NORMAL: pweight = PANGO_WEIGHT_NORMAL; break; + case RR_FONTWEIGHT_SEMIBOLD: pweight = PANGO_WEIGHT_SEMIBOLD; break; + case RR_FONTWEIGHT_BOLD: pweight = PANGO_WEIGHT_BOLD; break; + case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break; + default: g_assert_not_reached(); + } + + switch (slant) { + case RR_FONTSLANT_NORMAL: pstyle = PANGO_STYLE_NORMAL; break; + case RR_FONTSLANT_ITALIC: pstyle = PANGO_STYLE_ITALIC; break; + case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE; break; + default: g_assert_not_reached(); + } + + /* setup the font */ + pango_font_description_set_family(out->font_desc, name); + pango_font_description_set_weight(out->font_desc, pweight); + pango_font_description_set_style(out->font_desc, pstyle); + pango_font_description_set_size(out->font_desc, size * PANGO_SCALE); + + /* setup the layout */ + pango_layout_set_font_description(out->layout, out->font_desc); + pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR); + + /* get the ascent and descent */ + measure_font(inst, out); + + return out; +} + +RrFont *RrFontOpenDefault(const RrInstance *inst) +{ + return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize, + RrDefaultFontWeight, RrDefaultFontSlant); +} + +void RrFontRef(RrFont *f) +{ + ++f->ref; +} + +void RrFontClose(RrFont *f) +{ + if (f) { + if (--f->ref < 1) { + g_object_unref(f->layout); + pango_font_description_free(f->font_desc); + g_slice_free(RrFont, f); + } + } +} + +static void font_measure_full(const RrFont *f, const gchar *str, + gint *x, gint *y, gint shadow_x, gint shadow_y, + gboolean flow, gint maxwidth) +{ + PangoRectangle rect; + + pango_layout_set_text(f->layout, str, -1); + if (flow) { + pango_layout_set_single_paragraph_mode(f->layout, FALSE); + pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE); + pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE); + } + else { + /* single line mode */ + pango_layout_set_single_paragraph_mode(f->layout, TRUE); + pango_layout_set_width(f->layout, -1); + pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE); + } + + /* pango_layout_get_pixel_extents lies! this is the right way to get the + size of the text's area */ + pango_layout_get_extents(f->layout, NULL, &rect); +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + /* pass the logical rect as the ink rect, this is on purpose so we get the + full area for the text */ + pango_extents_to_pixels(&rect, NULL); +#else + rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE; + rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE; +#endif + *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */; + *y = rect.height + ABS(shadow_y); +} + +RrSize *RrFontMeasureString(const RrFont *f, const gchar *str, + gint shadow_x, gint shadow_y, + gboolean flow, gint maxwidth) +{ + RrSize *size; + + g_assert(!flow || maxwidth > 0); + + size = g_slice_new(RrSize); + font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y, + flow, maxwidth); + return size; +} + +gint RrFontHeight(const RrFont *f, gint shadow_y) +{ + return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y); +} + +static inline int font_calculate_baseline(RrFont *f, gint height) +{ +/* For my own reference: + * _________ + * ^space/2 ^height ^baseline + * v_________|_ | + * | ^ascent | _ _ + * | | | | |_ _____ _| |_ _ _ + * | | | | _/ -_) \ / _| || | + * | v_________v \__\___/_\_\\__|\_, | + * | ^descent |__/ + * __________|_v + * ^space/2 | + * V_________v + */ + return (((height * PANGO_SCALE) /* height of the space in pango units */ + - (f->ascent + f->descent)) /* minus space taken up by text */ + / 2 /* divided by two -> half of the empty space (this is the top + of the text) */ + + f->ascent) /* now move down to the baseline */ + / PANGO_SCALE; /* back to pixels */ +} + +void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) +{ + gint x,y,w; + XftColor c; + gint mw; + PangoRectangle rect; + PangoAttrList *attrlist; + PangoEllipsizeMode ell; + + g_assert(!t->flow || t->maxwidth > 0); + + y = area->y; + if (!t->flow) + /* center the text vertically + We do this centering based on the 'baseline' since different fonts + have different top edges. It looks bad when the whole string is + moved when 1 character from a non-default language is included in + the string */ + y += font_calculate_baseline(t->font, area->height); + + /* the +2 and -4 leave a small blank edge on the sides */ + x = area->x + 2; + w = area->width; + if (t->flow) w = MAX(w, t->maxwidth); + w -= 4; + /* h = area->height; */ + + if (t->flow) + ell = PANGO_ELLIPSIZE_NONE; + else { + switch (t->ellipsize) { + case RR_ELLIPSIZE_NONE: + ell = PANGO_ELLIPSIZE_NONE; + break; + case RR_ELLIPSIZE_START: + ell = PANGO_ELLIPSIZE_START; + break; + case RR_ELLIPSIZE_MIDDLE: + ell = PANGO_ELLIPSIZE_MIDDLE; + break; + case RR_ELLIPSIZE_END: + ell = PANGO_ELLIPSIZE_END; + break; + default: + g_assert_not_reached(); + } + } + + pango_layout_set_text(t->font->layout, t->string, -1); + pango_layout_set_width(t->font->layout, w * PANGO_SCALE); + pango_layout_set_ellipsize(t->font->layout, ell); + pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow); + + /* * * end of setting up the layout * * */ + + pango_layout_get_pixel_extents(t->font->layout, NULL, &rect); + mw = rect.width; + + /* pango_layout_set_alignment doesn't work with + pango_xft_render_layout_line */ + switch (t->justify) { + case RR_JUSTIFY_LEFT: + break; + case RR_JUSTIFY_RIGHT: + x += (w - mw); + break; + case RR_JUSTIFY_CENTER: + x += (w - mw) / 2; + break; + case RR_JUSTIFY_NUM_TYPES: + g_assert_not_reached(); + } + + if (t->shadow_offset_x || t->shadow_offset_y) { + /* From nvidia's readme (chapter 23): + + When rendering to a 32-bit window, keep in mind that the X RENDER + extension, used by most composite managers, expects "premultiplied + alpha" colors. This means that if your color has components (r,g,b) + and alpha value a, then you must render (a*r, a*g, a*b, a) into the + target window. + */ + c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) * + t->shadow_alpha / 255; + c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) * + t->shadow_alpha / 255; + c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) * + t->shadow_alpha / 255; + c.color.alpha = 0xffff * t->shadow_alpha / 255; + c.pixel = t->shadow_color->pixel; + + /* see below... */ + if (!t->flow) { + pango_xft_render_layout_line + (d, &c, +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + pango_layout_get_line_readonly(t->font->layout, 0), +#else + pango_layout_get_line(t->font->layout, 0), +#endif + (x + t->shadow_offset_x) * PANGO_SCALE, + (y + t->shadow_offset_y) * PANGO_SCALE); + } + else { + pango_xft_render_layout(d, &c, t->font->layout, + (x + t->shadow_offset_x) * PANGO_SCALE, + (y + t->shadow_offset_y) * PANGO_SCALE); + } + } + + c.color.red = t->color->r | t->color->r << 8; + c.color.green = t->color->g | t->color->g << 8; + c.color.blue = t->color->b | t->color->b << 8; + c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */ + c.pixel = t->color->pixel; + + if (t->shortcut) { + const gchar *s = t->string + t->shortcut_pos; + + t->font->shortcut_underline->start_index = t->shortcut_pos; + t->font->shortcut_underline->end_index = t->shortcut_pos + + (g_utf8_next_char(s) - s); + + /* the attributes are owned by the layout. + re-add the attributes to the layout after changing the + start and end index */ + attrlist = pango_layout_get_attributes(t->font->layout); + pango_attr_list_ref(attrlist); + pango_layout_set_attributes(t->font->layout, attrlist); + pango_attr_list_unref(attrlist); + } + + /* layout_line() uses y to specify the baseline + The line doesn't need to be freed, it's a part of the layout */ + if (!t->flow) { + pango_xft_render_layout_line + (d, &c, +#if PANGO_VERSION_MAJOR > 1 || \ + (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16) + pango_layout_get_line_readonly(t->font->layout, 0), +#else + pango_layout_get_line(t->font->layout, 0), +#endif + x * PANGO_SCALE, + y * PANGO_SCALE); + } + else { + pango_xft_render_layout(d, &c, t->font->layout, + x * PANGO_SCALE, + y * PANGO_SCALE); + } + + if (t->shortcut) { + t->font->shortcut_underline->start_index = 0; + t->font->shortcut_underline->end_index = 0; + /* the attributes are owned by the layout. + re-add the attributes to the layout after changing the + start and end index */ + attrlist = pango_layout_get_attributes(t->font->layout); + pango_attr_list_ref(attrlist); + pango_layout_set_attributes(t->font->layout, attrlist); + pango_attr_list_unref(attrlist); + } +} diff --git a/obrender/font.h b/obrender/font.h new file mode 100644 index 0000000..07d648d --- /dev/null +++ b/obrender/font.h @@ -0,0 +1,42 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + font.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __font_h +#define __font_h +#include "render.h" +#include "geom.h" +#include <pango/pango.h> + +struct _RrFont { + const RrInstance *inst; + gint ref; + PangoFontDescription *font_desc; + PangoLayout *layout; /*!< Used for measuring and rendering strings */ + PangoAttribute *shortcut_underline; /*< For underlining the shortcut key */ + gint ascent; /*!< The font's ascent in pango-units */ + gint descent; /*!< The font's descent in pango-units */ +}; + +void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *position); + +/*! Increment the references for this font, RrFontClose will decrement until 0 + and then really close it */ +void RrFontRef(RrFont *f); + +#endif /* __font_h */ diff --git a/obrender/geom.h b/obrender/geom.h new file mode 100644 index 0000000..4d81e4b --- /dev/null +++ b/obrender/geom.h @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + geom.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __render_geom_h +#define __render_geom_h + +typedef struct { + int width; + int height; +} RrSize; + +typedef struct { + int x; + int y; + int width; + int height; +} RrRect; + +#define RECT_SET(r, nx, ny, w, h) \ + (r).x = (nx), (r).y = (ny), (r).width = (w), (r).height = (h) + +#endif diff --git a/obrender/gradient.c b/obrender/gradient.c new file mode 100644 index 0000000..60a0a55 --- /dev/null +++ b/obrender/gradient.c @@ -0,0 +1,836 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + gradient.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2008 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "gradient.h" +#include "color.h" +#include <glib.h> +#include <string.h> + +static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y, + gboolean raised); +static void gradient_parentrelative(RrAppearance *a, gint w, gint h); +static void gradient_solid(RrAppearance *l, gint w, gint h); +static void gradient_splitvertical(RrAppearance *a, gint w, gint h); +static void gradient_vertical(RrSurface *sf, gint w, gint h); +static void gradient_horizontal(RrSurface *sf, gint w, gint h); +static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h); +static void gradient_diagonal(RrSurface *sf, gint w, gint h); +static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h); +static void gradient_pyramid(RrSurface *sf, gint inw, gint inh); + +void RrRender(RrAppearance *a, gint w, gint h) +{ + RrPixel32 *data = a->surface.pixel_data; + RrPixel32 current; + guint r,g,b; + register gint off, x; + + switch (a->surface.grad) { + case RR_SURFACE_PARENTREL: + gradient_parentrelative(a, w, h); + break; + case RR_SURFACE_SOLID: + gradient_solid(a, w, h); + break; + case RR_SURFACE_SPLIT_VERTICAL: + gradient_splitvertical(a, w, h); + break; + case RR_SURFACE_VERTICAL: + gradient_vertical(&a->surface, w, h); + break; + case RR_SURFACE_HORIZONTAL: + gradient_horizontal(&a->surface, w, h); + break; + case RR_SURFACE_MIRROR_HORIZONTAL: + gradient_mirrorhorizontal(&a->surface, w, h); + break; + case RR_SURFACE_DIAGONAL: + gradient_diagonal(&a->surface, w, h); + break; + case RR_SURFACE_CROSS_DIAGONAL: + gradient_crossdiagonal(&a->surface, w, h); + break; + case RR_SURFACE_PYRAMID: + gradient_pyramid(&a->surface, w, h); + break; + default: + g_assert_not_reached(); /* unhandled gradient */ + return; + } + + if (a->surface.interlaced) { + gint i; + RrPixel32 *p; + + r = a->surface.interlace_color->r; + g = a->surface.interlace_color->g; + b = a->surface.interlace_color->b; + current = (r << RrDefaultRedOffset) + + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset); + p = data; + for (i = 0; i < h; i += 2, p += w) + for (x = 0; x < w; ++x, ++p) + *p = current; + } + + if (a->surface.relief == RR_RELIEF_FLAT && a->surface.border) { + r = a->surface.border_color->r; + g = a->surface.border_color->g; + b = a->surface.border_color->b; + current = (r << RrDefaultRedOffset) + + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset); + for (off = 0, x = 0; x < w; ++x, off++) { + *(data + off) = current; + *(data + off + ((h-1) * w)) = current; + } + for (off = 0, x = 0; x < h; ++x, off++) { + *(data + (off * w)) = current; + *(data + (off * w) + w - 1) = current; + } + } + + if (a->surface.relief != RR_RELIEF_FLAT) { + if (a->surface.bevel == RR_BEVEL_1) { + for (off = 1, x = 1; x < w - 1; ++x, off++) + highlight(&a->surface, data + off, + data + off + (h-1) * w, + a->surface.relief==RR_RELIEF_RAISED); + for (off = 0, x = 0; x < h; ++x, off++) + highlight(&a->surface, data + off * w, + data + off * w + w - 1, + a->surface.relief==RR_RELIEF_RAISED); + } + + if (a->surface.bevel == RR_BEVEL_2) { + for (off = 2, x = 2; x < w - 2; ++x, off++) + highlight(&a->surface, data + off + w, + data + off + (h-2) * w, + a->surface.relief==RR_RELIEF_RAISED); + for (off = 1, x = 1; x < h-1; ++x, off++) + highlight(&a->surface, data + off * w + 1, + data + off * w + w - 2, + a->surface.relief==RR_RELIEF_RAISED); + } + } +} + +static void highlight(RrSurface *s, RrPixel32 *x, RrPixel32 *y, gboolean raised) +{ + register gint r, g, b; + + RrPixel32 *up, *down; + if (raised) { + up = x; + down = y; + } else { + up = y; + down = x; + } + + r = (*up >> RrDefaultRedOffset) & 0xFF; + r += (r * s->bevel_light_adjust) >> 8; + g = (*up >> RrDefaultGreenOffset) & 0xFF; + g += (g * s->bevel_light_adjust) >> 8; + b = (*up >> RrDefaultBlueOffset) & 0xFF; + b += (b * s->bevel_light_adjust) >> 8; + if (r > 0xFF) r = 0xFF; + if (g > 0xFF) g = 0xFF; + if (b > 0xFF) b = 0xFF; + *up = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset); + + r = (*down >> RrDefaultRedOffset) & 0xFF; + r -= (r * s->bevel_dark_adjust) >> 8; + g = (*down >> RrDefaultGreenOffset) & 0xFF; + g -= (g * s->bevel_dark_adjust) >> 8; + b = (*down >> RrDefaultBlueOffset) & 0xFF; + b -= (b * s->bevel_dark_adjust) >> 8; + *down = (r << RrDefaultRedOffset) + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset); +} + +static void create_bevel_colors(RrAppearance *l) +{ + register gint r, g, b; + + /* light color */ + r = l->surface.primary->r; + r += (r * l->surface.bevel_light_adjust) >> 8; + g = l->surface.primary->g; + g += (g * l->surface.bevel_light_adjust) >> 8; + b = l->surface.primary->b; + b += (b * l->surface.bevel_light_adjust) >> 8; + if (r > 0xFF) r = 0xFF; + if (g > 0xFF) g = 0xFF; + if (b > 0xFF) b = 0xFF; + g_assert(!l->surface.bevel_light); + l->surface.bevel_light = RrColorNew(l->inst, r, g, b); + + /* dark color */ + r = l->surface.primary->r; + r -= (r * l->surface.bevel_dark_adjust) >> 8; + g = l->surface.primary->g; + g -= (g * l->surface.bevel_dark_adjust) >> 8; + b = l->surface.primary->b; + b -= (b * l->surface.bevel_dark_adjust) >> 8; + g_assert(!l->surface.bevel_dark); + l->surface.bevel_dark = RrColorNew(l->inst, r, g, b); +} + +/*! Repeat the first pixel over the entire block of memory + @param start The block of memory. start[0] will be copied + to the rest of the block. + @param w The width of the block of memory (including the already-set first + element +*/ +static inline void repeat_pixel(RrPixel32 *start, gint w) +{ + register gint x; + RrPixel32 *dest; + + dest = start + 1; + + /* for really small things, just copy ourselves */ + if (w < 8) { + for (x = w-1; x > 0; --x) + *(dest++) = *start; + } + + /* for >= 8, then use O(log n) memcpy's... */ + else { + gchar *cdest; + gint lenbytes; + + /* copy the first 3 * 32 bits (3 words) ourselves - then we have + 3 + the original 1 = 4 words to make copies of at a time + + this is faster than doing memcpy for 1 or 2 words at a time + */ + for (x = 3; x > 0; --x) + *(dest++) = *start; + + /* cdest is a pointer to the pixel data that is typed char* so that + adding 1 to its position moves it only one byte + + lenbytes is the amount of bytes that we will be copying each + iteration. this doubles each time through the loop. + + x is the number of bytes left to copy into. lenbytes will alwaysa + be bounded by x + + this loop will run O(log n) times (n is the number of bytes we + need to copy into), since the size of the copy is doubled each + iteration. it seems that gcc does some nice optimizations to make + this memcpy very fast on hardware with support for vector operations + such as mmx or see. here is an idea of the kind of speed up we are + getting by doing this (splitvertical3 switches from doing + "*(data++) = color" n times to doing this memcpy thing log n times: + + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 49.44 0.88 0.88 1063 0.83 0.83 splitvertical1 + 47.19 1.72 0.84 1063 0.79 0.79 splitvertical2 + 2.81 1.77 0.05 1063 0.05 0.05 splitvertical3 + */ + cdest = (gchar*)dest; + lenbytes = 4 * sizeof(RrPixel32); + for (x = (w - 4) * sizeof(RrPixel32); x > 0;) { + memcpy(cdest, start, lenbytes); + x -= lenbytes; + cdest += lenbytes; + lenbytes <<= 1; + if (lenbytes > x) + lenbytes = x; + } + } +} + +static void gradient_parentrelative(RrAppearance *a, gint w, gint h) +{ + RrPixel32 *source, *dest; + gint sw, sh, partial_w, partial_h; + register gint i; + + g_assert (a->surface.parent); + g_assert (a->surface.parent->w); + + sw = a->surface.parent->w; + sh = a->surface.parent->h; + + /* This is a little hack. When a texture is parentrelative, and the same + area as the parent, and has a bevel, it will draw its bevel on top + of the parent's, amplifying it. So instead, rerender the child with + the parent's settings, but the child's bevel and interlace */ + if (a->surface.relief != RR_RELIEF_FLAT && + (a->surface.parent->surface.relief != RR_RELIEF_FLAT || + a->surface.parent->surface.border) && + !a->surface.parentx && !a->surface.parenty && + sw == w && sh == h) + { + RrSurface old = a->surface; + a->surface = a->surface.parent->surface; + + /* turn these off for the parent */ + a->surface.relief = RR_RELIEF_FLAT; + a->surface.border = FALSE; + + a->surface.pixel_data = old.pixel_data; + + RrRender(a, w, h); + a->surface = old; + } else { + source = (a->surface.parent->surface.pixel_data + + a->surface.parentx + sw * a->surface.parenty); + dest = a->surface.pixel_data; + + if (a->surface.parentx + w > sw) { + partial_w = sw - a->surface.parentx; + } else partial_w = w; + + if (a->surface.parenty + h > sh) { + partial_h = sh - a->surface.parenty; + } else partial_h = h; + + for (i = 0; i < partial_h; i++, source += sw, dest += w) { + memcpy(dest, source, partial_w * sizeof(RrPixel32)); + } + } +} + +static void gradient_solid(RrAppearance *l, gint w, gint h) +{ + register gint i; + RrPixel32 pix; + RrPixel32 *data = l->surface.pixel_data; + RrSurface *sp = &l->surface; + gint left = 0, top = 0, right = w - 1, bottom = h - 1; + + pix = (sp->primary->r << RrDefaultRedOffset) + + (sp->primary->g << RrDefaultGreenOffset) + + (sp->primary->b << RrDefaultBlueOffset); + + for (i = 0; i < w * h; i++) + *data++ = pix; + + if (sp->interlaced) + return; + + XFillRectangle(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->primary), + 0, 0, w, h); + + switch (sp->relief) { + case RR_RELIEF_RAISED: + if (!sp->bevel_dark) + create_bevel_colors(l); + + switch (sp->bevel) { + case RR_BEVEL_1: + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left, bottom, right, bottom); + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + right, bottom, right, top); + + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left, top, right, top); + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left, bottom, left, top); + break; + case RR_BEVEL_2: + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left + 2, bottom - 1, right - 2, bottom - 1); + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + right - 1, bottom - 1, right - 1, top + 1); + + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left + 2, top + 1, right - 2, top + 1); + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left + 1, bottom - 1, left + 1, top + 1); + break; + default: + g_assert_not_reached(); /* unhandled BevelType */ + } + break; + case RR_RELIEF_SUNKEN: + if (!sp->bevel_dark) + create_bevel_colors(l); + + switch (sp->bevel) { + case RR_BEVEL_1: + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left, bottom, right, bottom); + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + right, bottom, right, top); + + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left, top, right, top); + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left, bottom, left, top); + break; + case RR_BEVEL_2: + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + left + 2, bottom - 1, right - 2, bottom - 1); + XDrawLine(RrDisplay(l->inst), l->pixmap,RrColorGC(sp->bevel_light), + right - 1, bottom - 1, right - 1, top + 1); + + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left + 2, top + 1, right - 2, top + 1); + XDrawLine(RrDisplay(l->inst), l->pixmap, RrColorGC(sp->bevel_dark), + left + 1, bottom - 1, left + 1, top + 1); + break; + default: + g_assert_not_reached(); /* unhandled BevelType */ + } + break; + case RR_RELIEF_FLAT: + if (sp->border) { + XDrawRectangle(RrDisplay(l->inst), l->pixmap, + RrColorGC(sp->border_color), + left, top, right, bottom); + } + break; + default: + g_assert_not_reached(); /* unhandled ReliefType */ + } +} + +/* * * * * * * * * * * * * * GRADIENT MAGIC WOOT * * * * * * * * * * * * * * */ + +#define VARS(x) \ + register gint len##x; \ + guint color##x[3]; \ + gint cdelta##x[3], error##x[3] = { 0, 0, 0 }, inc##x[3]; \ + gboolean bigslope##x[3] /* color slope > 1 */ + +#define SETUP(x, from, to, w) \ + len##x = w; \ + \ + color##x[0] = from->r; \ + color##x[1] = from->g; \ + color##x[2] = from->b; \ + \ + cdelta##x[0] = to->r - from->r; \ + cdelta##x[1] = to->g - from->g; \ + cdelta##x[2] = to->b - from->b; \ + \ + if (cdelta##x[0] < 0) { \ + cdelta##x[0] = -cdelta##x[0]; \ + inc##x[0] = -1; \ + } else \ + inc##x[0] = 1; \ + if (cdelta##x[1] < 0) { \ + cdelta##x[1] = -cdelta##x[1]; \ + inc##x[1] = -1; \ + } else \ + inc##x[1] = 1; \ + if (cdelta##x[2] < 0) { \ + cdelta##x[2] = -cdelta##x[2]; \ + inc##x[2] = -1; \ + } else \ + inc##x[2] = 1; \ + bigslope##x[0] = cdelta##x[0] > w;\ + bigslope##x[1] = cdelta##x[1] > w;\ + bigslope##x[2] = cdelta##x[2] > w + +#define COLOR_RR(x, c) \ + c->r = color##x[0]; \ + c->g = color##x[1]; \ + c->b = color##x[2] + +#define COLOR(x) \ + ((color##x[0] << RrDefaultRedOffset) + \ + (color##x[1] << RrDefaultGreenOffset) + \ + (color##x[2] << RrDefaultBlueOffset)) + +#define INCREMENT(x, i) \ + (inc##x[i]) + +#define NEXT(x) \ +{ \ + register gint i; \ + for (i = 2; i >= 0; --i) { \ + if (!cdelta##x[i]) continue; \ + \ + if (!bigslope##x[i]) { \ + /* Y (color) is dependant on X */ \ + error##x[i] += cdelta##x[i]; \ + if ((error##x[i] << 1) >= len##x) { \ + color##x[i] += INCREMENT(x, i); \ + error##x[i] -= len##x; \ + } \ + } else { \ + /* X is dependant on Y (color) */ \ + while (1) { \ + color##x[i] += INCREMENT(x, i); \ + error##x[i] += len##x; \ + if ((error##x[i] << 1) >= cdelta##x[i]) { \ + error##x[i] -= cdelta##x[i]; \ + break; \ + } \ + } \ + } \ + } \ +} + +static void gradient_splitvertical(RrAppearance *a, gint w, gint h) +{ + register gint y1, y2, y3; + RrSurface *sf = &a->surface; + RrPixel32 *data; + register gint y1sz, y2sz, y3sz; + + VARS(y1); + VARS(y2); + VARS(y3); + + /* if h <= 5, then a 0 or 1px middle gradient. + if h > 5, then always a 1px middle gradient. + */ + if (h <= 5) { + y1sz = MAX(h/2, 0); + y2sz = (h < 3) ? 0 : (h & 1); + y3sz = MAX(h/2, 1); + } + else { + y1sz = h/2 - (1 - (h & 1)); + y2sz = 1; + y3sz = h/2; + } + + SETUP(y1, sf->split_primary, sf->primary, y1sz); + if (y2sz) { + /* setup to get the colors _in between_ these other 2 */ + SETUP(y2, sf->primary, sf->secondary, y2sz + 2); + NEXT(y2); /* skip the first one, its the same as the last of y1 */ + } + SETUP(y3, sf->secondary, sf->split_secondary, y3sz); + + /* find the color for the first pixel of each row first */ + data = sf->pixel_data; + + for (y1 = y1sz-1; y1 > 0; --y1) { + *data = COLOR(y1); + data += w; + NEXT(y1); + } + *data = COLOR(y1); + data += w; + if (y2sz) { + for (y2 = y2sz-1; y2 > 0; --y2) { + *data = COLOR(y2); + data += w; + NEXT(y2); + } + *data = COLOR(y2); + data += w; + } + for (y3 = y3sz-1; y3 > 0; --y3) { + *data = COLOR(y3); + data += w; + NEXT(y3); + } + *data = COLOR(y3); + + /* copy the first pixels into the whole rows */ + data = sf->pixel_data; + for (y1 = h; y1 > 0; --y1) { + repeat_pixel(data, w); + data += w; + } +} + +static void gradient_horizontal(RrSurface *sf, gint w, gint h) +{ + register gint x, y, cpbytes; + RrPixel32 *data = sf->pixel_data, *datav; + gchar *datac; + + VARS(x); + SETUP(x, sf->primary, sf->secondary, w); + + /* set the color values for the first row */ + datav = data; + for (x = w - 1; x > 0; --x) { /* 0 -> w - 1 */ + *datav = COLOR(x); + ++datav; + NEXT(x); + } + *datav = COLOR(x); + ++datav; + + /* copy the first row to the rest in O(logn) copies */ + datac = (gchar*)datav; + cpbytes = 1 * w * sizeof(RrPixel32); + for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) { + memcpy(datac, data, cpbytes); + y -= cpbytes; + datac += cpbytes; + cpbytes <<= 1; + if (cpbytes > y) + cpbytes = y; + } +} + +static void gradient_mirrorhorizontal(RrSurface *sf, gint w, gint h) +{ + register gint x, y, half1, half2, cpbytes; + RrPixel32 *data = sf->pixel_data, *datav; + gchar *datac; + + VARS(x); + + half1 = (w + 1) / 2; + half2 = w / 2; + + /* set the color values for the first row */ + + SETUP(x, sf->primary, sf->secondary, half1); + datav = data; + for (x = half1 - 1; x > 0; --x) { /* 0 -> half1 - 1 */ + *datav = COLOR(x); + ++datav; + NEXT(x); + } + *datav = COLOR(x); + ++datav; + + if (half2 > 0) { + SETUP(x, sf->secondary, sf->primary, half2); + for (x = half2 - 1; x > 0; --x) { /* 0 -> half2 - 1 */ + *datav = COLOR(x); + ++datav; + NEXT(x); + } + *datav = COLOR(x); + ++datav; + } + + /* copy the first row to the rest in O(logn) copies */ + datac = (gchar*)datav; + cpbytes = 1 * w * sizeof(RrPixel32); + for (y = (h - 1) * w * sizeof(RrPixel32); y > 0;) { + memcpy(datac, data, cpbytes); + y -= cpbytes; + datac += cpbytes; + cpbytes <<= 1; + if (cpbytes > y) + cpbytes = y; + } +} + +static void gradient_vertical(RrSurface *sf, gint w, gint h) +{ + register gint y; + RrPixel32 *data; + + VARS(y); + SETUP(y, sf->primary, sf->secondary, h); + + /* find the color for the first pixel of each row first */ + data = sf->pixel_data; + + for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */ + *data = COLOR(y); + data += w; + NEXT(y); + } + *data = COLOR(y); + + /* copy the first pixels into the whole rows */ + data = sf->pixel_data; + for (y = h; y > 0; --y) { + repeat_pixel(data, w); + data += w; + } +} + +static void gradient_diagonal(RrSurface *sf, gint w, gint h) +{ + register gint x, y; + RrPixel32 *data = sf->pixel_data; + RrColor left, right; + RrColor extracorner; + + VARS(lefty); + VARS(righty); + VARS(x); + + extracorner.r = (sf->primary->r + sf->secondary->r) / 2; + extracorner.g = (sf->primary->g + sf->secondary->g) / 2; + extracorner.b = (sf->primary->b + sf->secondary->b) / 2; + + SETUP(lefty, sf->primary, (&extracorner), h); + SETUP(righty, (&extracorner), sf->secondary, h); + + for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */ + COLOR_RR(lefty, (&left)); + COLOR_RR(righty, (&right)); + + SETUP(x, (&left), (&right), w); + + for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */ + *(data++) = COLOR(x); + + NEXT(x); + } + *(data++) = COLOR(x); + + NEXT(lefty); + NEXT(righty); + } + COLOR_RR(lefty, (&left)); + COLOR_RR(righty, (&right)); + + SETUP(x, (&left), (&right), w); + + for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */ + *(data++) = COLOR(x); + + NEXT(x); + } + *data = COLOR(x); +} + +static void gradient_crossdiagonal(RrSurface *sf, gint w, gint h) +{ + register gint x, y; + RrPixel32 *data = sf->pixel_data; + RrColor left, right; + RrColor extracorner; + + VARS(lefty); + VARS(righty); + VARS(x); + + extracorner.r = (sf->primary->r + sf->secondary->r) / 2; + extracorner.g = (sf->primary->g + sf->secondary->g) / 2; + extracorner.b = (sf->primary->b + sf->secondary->b) / 2; + + SETUP(lefty, (&extracorner), sf->secondary, h); + SETUP(righty, sf->primary, (&extracorner), h); + + for (y = h - 1; y > 0; --y) { /* 0 -> h-1 */ + COLOR_RR(lefty, (&left)); + COLOR_RR(righty, (&right)); + + SETUP(x, (&left), (&right), w); + + for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */ + *(data++) = COLOR(x); + + NEXT(x); + } + *(data++) = COLOR(x); + + NEXT(lefty); + NEXT(righty); + } + COLOR_RR(lefty, (&left)); + COLOR_RR(righty, (&right)); + + SETUP(x, (&left), (&right), w); + + for (x = w - 1; x > 0; --x) { /* 0 -> w-1 */ + *(data++) = COLOR(x); + + NEXT(x); + } + *data = COLOR(x); +} + +static void gradient_pyramid(RrSurface *sf, gint w, gint h) +{ + RrPixel32 *ldata, *rdata; + RrPixel32 *cp; + RrColor left, right; + RrColor extracorner; + register gint x, y, halfw, halfh, midx, midy; + + VARS(lefty); + VARS(righty); + VARS(x); + + extracorner.r = (sf->primary->r + sf->secondary->r) / 2; + extracorner.g = (sf->primary->g + sf->secondary->g) / 2; + extracorner.b = (sf->primary->b + sf->secondary->b) / 2; + + halfw = w >> 1; + halfh = h >> 1; + midx = w - halfw - halfw; /* 0 or 1, depending if w is even or odd */ + midy = h - halfh - halfh; /* 0 or 1, depending if h is even or odd */ + + SETUP(lefty, sf->primary, (&extracorner), halfh + midy); + SETUP(righty, (&extracorner), sf->secondary, halfh + midy); + + /* draw the top half + + it is faster to draw both top quarters together than to draw one and + then copy it over to the other side. + */ + + ldata = sf->pixel_data; + rdata = ldata + w - 1; + for (y = halfh + midy; y > 0; --y) { /* 0 -> (h+1)/2 */ + RrPixel32 c; + + COLOR_RR(lefty, (&left)); + COLOR_RR(righty, (&right)); + + SETUP(x, (&left), (&right), halfw + midx); + + for (x = halfw + midx - 1; x > 0; --x) { /* 0 -> (w+1)/2 */ + c = COLOR(x); + *(ldata++) = *(rdata--) = c; + + NEXT(x); + } + c = COLOR(x); + *ldata = *rdata = c; + ldata += halfw + 1; + rdata += halfw - 1 + midx + w; + + NEXT(lefty); + NEXT(righty); + } + + /* copy the top half into the bottom half, mirroring it, so we can only + copy one row at a time + + it is faster, to move the writing pointer forward, and the reading + pointer backward + + this is the current code, moving the write pointer forward and read + pointer backward + 41.78 4.26 1.78 504 3.53 3.53 gradient_pyramid2 + this is the opposite, moving the read pointer forward and the write + pointer backward + 42.27 4.40 1.86 504 3.69 3.69 gradient_pyramid2 + + */ + ldata = sf->pixel_data + (halfh - 1) * w; + cp = ldata + (midy + 1) * w; + for (y = halfh; y > 0; --y) { + memcpy(cp, ldata, w * sizeof(RrPixel32)); + ldata -= w; + cp += w; + } +} diff --git a/obrender/gradient.h b/obrender/gradient.h new file mode 100644 index 0000000..8613f0c --- /dev/null +++ b/obrender/gradient.h @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + gradient.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __gradient_h +#define __gradient_h + +#include "render.h" + +void RrRender(RrAppearance *a, gint w, gint h); + +#endif /* __gradient_h */ diff --git a/obrender/icon.h b/obrender/icon.h new file mode 100644 index 0000000..f0b2d26 --- /dev/null +++ b/obrender/icon.h @@ -0,0 +1,422 @@ +/* GIMP RGBA C-Source image dump (icon.h) */ + +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + icon.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#define OB_DEFAULT_ICON_WIDTH (48) +#define OB_DEFAULT_ICON_HEIGHT (48) +#define OB_DEFAULT_ICON_BYTES_PER_PIXEL (4) /* 3:RGB, 4:RGBA */ +#define OB_DEFAULT_ICON_COMMENT \ + "To recreate this file, save an image as \"C-Source\" in The Gimp. Use \"ob_default_icon\" as the Prefixed Name. Enable Glib Types. Enable Save Alpha Channel. Enable Use Macros instead of Struct." +#define OB_DEFAULT_ICON_PIXEL_DATA ((guint8*) OB_DEFAULT_ICON_pixel_data) +static const guint8 OB_DEFAULT_ICON_pixel_data[48 * 48 * 4 + 1] = +("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40J\207\15\40J\207\23\40J\207" + "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J" + "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23" + "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207" + "\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J" + "\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23" + "\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207\23\40J\207" + "\23\40J\207\15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'P\213\267'Q\214\275'Q\214\275" + "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214" + "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275" + "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215\275'Q\214\275'Q\214" + "\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275" + "'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275'Q\214\275(R\215" + "\275(R\215\275(R\215\275&P\213\267\40J\207\20\0\0\0\0\0\0\0\0\40J\207+Y{\252" + "\377\216\253\320\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243" + "\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204" + "\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377" + "\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314" + "\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243\314\377\204\243" + "\314\377\204\243\314\377\203\243\314\377\203\243\313\377\203\243\313\377\203" + "\243\313\377\203\242\313\377\202\242\313\377\202\242\313\377\202\241\313\377" + "\201\241\312\377\201\241\312\377\201\240\312\377\201\240\312\377\200\240\312" + "\377\200\240\312\377\200\240\312\377\200\240\312\377\211\247\316\377Jn\241" + "\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Wy\251\377]\207\275\377>o\260\377>o" + "\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260" + "\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377" + ">o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260\377>o\260" + "\377>o\260\377>o\260\377>o\260\377>o\260\377=o\260\377=o\260\377=o\260\377" + "=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260\377=o\260" + "\377=o\260\377_\210\275\377Hm\241\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Tw" + "\251\377]\207\276\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r" + "\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263" + "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377" + "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263" + "\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377" + "?r\263\377?r\263\377?r\263\377?r\263\377?r\263\377]\210\277\377Fl\241\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Rv\250\377\\\210\300\377At\265\377At\265" + "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377" + "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265" + "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377" + "At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265" + "\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377At\265\377" + "At\265\377\\\210\300\377Dj\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ot\247" + "\377\\\211\302\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271" + "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377" + "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271" + "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377" + "Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377Dw\271" + "\377Dw\271\377Dw\271\377Dw\271\377Dw\271\377[\211\302\377Bi\240\377\40J\207" + "+\0\0\0\0\0\0\0\0\40J\207+Ls\247\377\\\211\303\377Fy\273\377Fy\273\377Fy\273" + "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377" + "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273" + "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377" + "Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273" + "\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377Fy\273\377" + "\\\211\303\377Ah\240\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Jq\246\377\\\212" + "\305\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277" + "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377" + "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277" + "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377" + "I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277" + "\377I}\277\377I}\277\377I}\277\377[\212\305\377?g\237\377\40J\207+\0\0\0\0" + "\0\0\0\0\40J\207+Jq\246\377\\\212\305\377I}\277\377I}\277\377I}\277\377I}" + "\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277" + "\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377I}\277\377" + "I}\277\377I}\277\377I}\277\377J~\300\377K\177\301\377K\177\301\377K\177\301" + "\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177" + "\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377K\177\301\377" + "K\177\301\377K\177\301\377K\177\301\377K\177\301\377Z\212\307\377=f\237\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ip\247\377\\\213\307\377J\177\301\377J\177" + "\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377" + "J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301" + "\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177\301\377J\177" + "\301\377J\177\301\377J\177\301\377K\200\302\377K\200\302\377K\200\302\377" + "K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302" + "\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377K\200" + "\302\377K\200\302\377K\200\302\377K\200\302\377K\200\302\377Z\212\307\377" + "=f\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+Ho\247\377e\223\314\377Z\213\310" + "\377Z\213\310\377Z\213\310\377Z\213\310\377Z\213\307\377Z\213\307\377Z\213" + "\307\377Z\213\307\377Z\213\307\377Y\212\307\377Y\212\307\377Y\212\307\377" + "Y\212\307\377Y\212\307\377X\212\307\377X\212\307\377X\212\307\377X\211\307" + "\377X\212\307\377X\212\307\377X\211\307\377X\211\307\377X\211\307\377X\211" + "\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377X\211\307\377" + "X\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307\377W\211\307" + "\377W\211\307\377V\211\307\377V\211\307\377V\210\307\377V\210\307\377`\217" + "\312\377<e\237\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+<b\231\377i\212\267\377" + "i\211\266\377i\211\266\377i\211\266\377i\211\266\377i\211\266\377h\211\266" + "\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211\266\377h\211" + "\266\377g\211\266\377g\211\266\377g\211\266\377g\211\266\377g\210\266\377" + "g\210\266\377g\211\266\377g\211\266\377g\210\266\377g\210\266\377g\210\265" + "\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210\265\377g\210" + "\265\377g\210\265\377g\210\265\377f\210\265\377f\210\264\377f\210\264\377" + "f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\210\264\377f\207\264" + "\377f\207\264\3778^\226\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+b\177\251\377" + "\340\344\351\377\337\342\350\377\337\342\350\377\336\342\350\377\336\342\347" + "\377\336\342\347\377\336\342\347\377\336\342\347\377\335\341\347\377\335\341" + "\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335\341\346\377\335" + "\341\346\377\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377" + "\335\341\346\377\335\341\346\377\334\340\346\377\334\340\346\377\334\340\345" + "\377\334\340\345\377\334\340\345\377\333\337\345\377\333\337\345\377\333\337" + "\344\377\333\337\344\377\333\337\344\377\333\336\344\377\332\336\344\377\332" + "\336\344\377\332\336\344\377\332\336\343\377\332\336\343\377\332\336\343\377" + "\332\336\343\377\332\336\343\377\332\336\343\377\333\337\345\377a}\247\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\355\355\355\377\351\351\351" + "\377\351\351\352\377\351\351\351\377\351\351\351\377\351\351\351\377\351\351" + "\350\377\351\350\350\377\351\351\351\377\350\350\350\377\350\350\350\377\350" + "\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377\350\350\350\377" + "\350\347\350\377\350\347\347\377\347\347\347\377\347\347\347\377\347\347\347" + "\377\347\347\347\377\347\347\347\377\347\347\347\377\346\347\347\377\347\346" + "\347\377\347\346\347\377\346\346\347\377\346\346\346\377\346\346\346\377\346" + "\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377" + "\346\345\345\377\345\345\346\377\345\346\345\377\345\346\345\377\345\346\345" + "\377\345\345\345\377\351\351\351\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\203\253\377\354\354\354\377\350\350\350\377\347\350\350\377\347" + "\347\350\377\347\347\350\377\347\347\347\377\347\347\347\377\347\347\347\377" + "\347\347\347\377\347\346\347\377\347\346\346\377\347\346\346\377\347\347\347" + "\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346\346\377\346\346" + "\346\377\346\346\346\377\345\345\346\377\345\345\345\377\345\345\346\377\345" + "\346\346\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377" + "\345\345\345\377\345\344\344\377\345\345\344\377\345\345\345\377\345\344\344" + "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344" + "\344\377\343\344\344\377\344\344\344\377\344\344\343\377\344\343\343\377\350" + "\350\350\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\353" + "\352\352\377\346\346\346\377\346\345\345\377\345\346\346\377\345\346\345\377" + "\345\345\345\377\345\345\345\377\345\345\345\377\345\345\346\377\345\345\345" + "\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345\345\377\345\345" + "\345\377\344\345\345\377\344\344\344\377\344\345\344\377\344\344\344\377\344" + "\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\343\344\377" + "\343\343\343\377\344\343\343\377\343\343\343\377\343\343\343\377\343\343\343" + "\377\343\343\343\377\343\343\343\377\342\343\343\377\342\343\343\377\343\342" + "\343\377\343\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342" + "\342\342\377\342\342\342\377\342\342\342\377\346\346\346\377g\202\252\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\351\351\351\377\345\344\344" + "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\343\344" + "\344\377\343\344\344\377\343\343\344\377\343\343\343\377\343\343\343\377\343" + "\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377" + "\343\343\343\377\343\343\343\377\343\342\342\377\342\342\342\377\342\342\342" + "\377\342\342\342\377\342\342\342\377\342\341\342\377\342\342\342\377\342\342" + "\341\377\342\341\342\377\341\341\342\377\341\341\341\377\341\341\341\377\341" + "\341\341\377\341\341\341\377\341\341\341\377\341\340\341\377\341\341\341\377" + "\340\341\341\377\341\341\341\377\340\340\340\377\340\340\340\377\340\340\340" + "\377\340\340\340\377\346\345\346\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\203\253\377\350\350\350\377\342\342\343\377\342\342\342\377\342" + "\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377" + "\342\342\342\377\342\342\341\377\342\341\342\377\341\341\341\377\341\341\341" + "\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341" + "\341\377\340\341\341\377\341\340\340\377\341\340\340\377\340\340\340\377\340" + "\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377\340\340\340\377" + "\340\340\337\377\340\340\340\377\337\337\337\377\340\340\337\377\337\337\337" + "\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337" + "\337\377\337\337\337\377\337\337\336\377\336\337\336\377\336\336\336\377\344" + "\344\344\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\347" + "\347\347\377\341\341\341\377\341\341\341\377\341\341\341\377\341\341\341\377" + "\341\341\341\377\340\341\340\377\340\340\340\377\340\340\340\377\340\340\337" + "\377\340\340\340\377\340\340\340\377\340\340\337\377\337\340\337\377\337\340" + "\337\377\337\337\340\377\337\337\337\377\337\337\337\377\337\337\337\377\337" + "\337\337\377\337\337\337\377\337\337\337\377\336\337\337\377\336\337\336\377" + "\337\336\336\377\336\336\336\377\336\336\336\377\336\336\336\377\336\336\336" + "\377\336\336\336\377\336\336\336\377\336\336\335\377\336\335\335\377\336\335" + "\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335" + "\335\335\377\335\335\335\377\335\335\335\377\343\343\343\377g\202\252\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\345\346\346\377\337\337\340" + "\377\340\337\337\377\337\337\337\377\337\337\337\377\337\337\337\377\337\337" + "\337\377\337\337\336\377\337\336\336\377\336\336\337\377\336\336\337\377\336" + "\336\336\377\336\336\336\377\336\336\336\377\336\336\335\377\336\336\336\377" + "\336\335\336\377\336\336\336\377\335\335\336\377\335\335\335\377\335\335\335" + "\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377\335\335" + "\334\377\334\335\335\377\334\334\334\377\334\334\334\377\334\334\334\377\334" + "\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377" + "\334\333\334\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333" + "\377\333\333\333\377\341\341\342\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\203\253\377\345\345\345\377\336\335\336\377\336\335\335\377\335" + "\335\335\377\336\335\335\377\335\335\335\377\335\335\335\377\335\335\335\377" + "\335\334\335\377\335\335\335\377\335\335\335\377\334\335\334\377\334\334\335" + "\377\334\335\334\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334" + "\334\377\334\334\334\377\334\334\334\377\334\333\334\377\334\333\333\377\333" + "\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377\333\333\333\377" + "\333\332\333\377\333\333\333\377\333\333\333\377\332\333\333\377\332\333\332" + "\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332" + "\332\377\332\332\332\377\331\332\331\377\331\332\331\377\331\331\332\377\341" + "\341\341\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\343" + "\343\344\377\334\334\334\377\334\333\334\377\334\334\334\377\333\333\334\377" + "\333\333\333\377\333\334\333\377\333\333\333\377\333\333\333\377\333\333\333" + "\377\333\333\333\377\333\333\333\377\333\332\333\377\333\332\333\377\333\332" + "\332\377\332\332\333\377\332\332\333\377\332\332\332\377\333\332\332\377\332" + "\332\332\377\332\332\332\377\331\332\332\377\331\332\331\377\331\332\331\377" + "\331\332\331\377\331\331\331\377\331\331\331\377\332\331\331\377\331\331\331" + "\377\331\331\331\377\331\331\331\377\330\330\331\377\330\330\330\377\331\330" + "\330\377\330\330\330\377\330\330\330\377\331\330\330\377\330\330\330\377\330" + "\330\330\377\330\330\330\377\330\330\330\377\337\337\337\377f\202\252\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\342\342\343\377\332\332\332" + "\377\332\332\332\377\332\332\332\377\332\332\332\377\331\332\332\377\331\332" + "\332\377\332\332\331\377\332\331\331\377\331\331\331\377\331\331\331\377\331" + "\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377" + "\331\331\331\377\330\330\330\377\331\330\331\377\330\331\330\377\330\330\330" + "\377\330\330\330\377\330\330\330\377\330\330\327\377\327\330\330\377\330\330" + "\327\377\327\330\330\377\330\330\327\377\327\327\327\377\327\327\327\377\327" + "\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377\327\327\327\377" + "\326\327\327\377\327\326\326\377\326\327\326\377\326\326\326\377\326\326\326" + "\377\326\326\326\377\336\336\337\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+g\203\253\377\341\341\341\377\331\331\331\377\330\331\331\377\330" + "\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377\330\330\330\377" + "\327\330\330\377\330\330\327\377\330\330\330\377\330\327\330\377\327\327\327" + "\377\327\330\327\377\327\330\327\377\327\327\327\377\327\327\327\377\327\327" + "\327\377\327\327\327\377\326\327\326\377\326\327\326\377\326\326\326\377\326" + "\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377\326\326\326\377" + "\326\326\326\377\325\325\326\377\325\326\326\377\326\325\325\377\325\325\325" + "\377\325\326\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325" + "\325\377\325\325\325\377\325\324\325\377\324\324\324\377\324\324\324\377\335" + "\335\335\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\340" + "\340\340\377\327\327\327\377\327\327\327\377\326\327\327\377\327\326\327\377" + "\326\326\326\377\326\326\327\377\326\326\326\377\326\326\326\377\326\326\326" + "\377\326\326\326\377\326\326\326\377\326\326\325\377\325\326\325\377\325\326" + "\326\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325" + "\325\325\377\325\325\325\377\324\325\325\377\325\325\325\377\324\325\325\377" + "\324\324\324\377\325\324\324\377\324\324\324\377\324\324\324\377\324\324\324" + "\377\323\324\324\377\324\323\324\377\323\324\324\377\323\324\324\377\323\324" + "\323\377\323\323\324\377\323\323\323\377\323\323\323\377\323\323\323\377\323" + "\323\323\377\323\323\323\377\323\323\323\377\334\334\334\377f\202\252\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\337\336\337\377\325\325\325" + "\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325\325\377\325\325" + "\325\377\325\325\324\377\325\325\325\377\324\325\324\377\324\324\324\377\324" + "\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377\324\324\324\377" + "\324\324\324\377\324\323\323\377\323\323\323\377\324\323\323\377\323\323\323" + "\377\323\323\323\377\324\323\323\377\323\323\323\377\323\323\323\377\323\322" + "\323\377\323\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\322" + "\322\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\321\377" + "\321\321\322\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321" + "\377\321\321\321\377\334\333\334\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\203\253\377\336\336\336\377\323\324\323\377\324\323\324\377\324" + "\323\323\377\323\324\323\377\323\323\323\377\323\323\323\377\323\323\323\377" + "\323\323\323\377\323\323\323\377\323\323\323\377\322\323\323\377\323\322\323" + "\377\322\322\323\377\322\322\322\377\322\322\322\377\322\322\322\377\322\322" + "\322\377\322\322\322\377\322\322\322\377\322\322\322\377\321\321\321\377\322" + "\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377\321\321\321\377" + "\321\321\320\377\321\321\321\377\321\321\321\377\321\321\321\377\320\320\320" + "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\320\377\317\320" + "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\317\317\377\332" + "\332\332\377g\202\252\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\335" + "\335\335\377\322\322\322\377\322\322\322\377\322\322\322\377\321\322\322\377" + "\321\321\321\377\321\321\321\377\322\321\321\377\321\321\321\377\321\321\321" + "\377\321\321\321\377\321\321\321\377\321\320\321\377\320\321\320\377\320\320" + "\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320" + "\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\317\320\320\377" + "\317\320\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317" + "\377\317\317\317\377\316\317\317\377\317\317\317\377\317\316\316\377\316\316" + "\316\377\317\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316" + "\316\316\377\316\316\316\377\316\316\316\377\331\331\331\377f\202\252\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\334\334\334\377\320\320\320" + "\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320\320\377\320\320" + "\320\377\320\320\317\377\317\317\320\377\320\317\317\377\317\317\317\377\317" + "\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377\317\317\317\377" + "\317\316\317\377\317\317\316\377\316\317\317\377\316\316\316\377\316\316\316" + "\377\316\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\316\315" + "\315\377\316\316\315\377\315\316\315\377\315\316\316\377\315\315\315\377\315" + "\315\315\377\315\315\315\377\315\315\315\377\315\314\315\377\315\315\315\377" + "\315\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314" + "\377\314\314\314\377\331\330\331\377f\202\252\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\203\253\377\333\333\333\377\317\317\316\377\316\317\316\377\317" + "\316\316\377\317\316\316\377\316\317\316\377\316\316\316\377\316\316\316\377" + "\316\316\316\377\316\316\316\377\316\315\316\377\315\315\315\377\315\315\316" + "\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315\315\377\315\315" + "\315\377\315\315\315\377\315\315\314\377\315\314\314\377\314\314\314\377\314" + "\315\314\377\314\314\314\377\314\314\314\377\314\314\314\377\314\314\314\377" + "\314\314\314\377\314\314\313\377\313\313\313\377\314\314\313\377\313\313\313" + "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313" + "\313\377\312\313\313\377\312\312\313\377\312\312\313\377\312\312\313\377\327" + "\327\327\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\203\253\377\332" + "\332\332\377\315\315\315\377\314\315\315\377\315\315\315\377\315\315\315\377" + "\314\315\315\377\314\314\315\377\314\314\314\377\314\314\314\377\314\314\314" + "\377\314\314\314\377\314\314\314\377\314\314\314\377\313\314\314\377\313\314" + "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313" + "\313\313\377\313\313\313\377\313\312\313\377\313\313\313\377\312\313\313\377" + "\313\313\313\377\312\312\312\377\312\312\312\377\312\312\312\377\312\312\312" + "\377\312\312\312\377\312\312\312\377\312\312\311\377\312\311\311\377\312\312" + "\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311" + "\311\311\377\311\311\311\377\311\311\311\377\327\326\327\377g\203\253\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+g\203\253\377\331\331\331\377\313\313\313" + "\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313" + "\313\377\312\312\313\377\312\313\313\377\312\312\312\377\312\312\312\377\312" + "\312\312\377\312\312\312\377\312\312\312\377\312\312\312\377\311\311\311\377" + "\312\312\312\377\311\311\311\377\311\311\311\377\311\312\311\377\311\311\311" + "\377\311\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\311\310" + "\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310" + "\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377" + "\307\307\310\377\310\307\310\377\307\307\307\377\307\307\307\377\307\307\307" + "\377\307\307\307\377\325\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\207+h\204\254\377\330\330\331\377\312\312\311\377\312\312\311\377\312" + "\311\311\377\311\311\311\377\311\311\311\377\311\311\311\377\310\311\311\377" + "\311\311\311\377\311\311\311\377\311\310\311\377\311\310\310\377\311\311\311" + "\377\310\310\311\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310" + "\310\377\310\310\310\377\307\310\307\377\310\310\310\377\307\307\307\377\307" + "\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307\377" + "\307\307\307\377\307\307\307\377\306\306\307\377\306\307\307\377\306\306\306" + "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306" + "\306\377\305\306\306\377\305\306\305\377\305\306\306\377\305\306\306\377\325" + "\325\325\377g\203\253\377\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\327" + "\327\327\377\310\310\310\377\310\310\310\377\310\310\310\377\310\307\307\377" + "\307\307\310\377\307\307\307\377\307\307\307\377\307\307\307\377\307\307\307" + "\377\307\307\307\377\307\307\307\377\307\307\307\377\307\306\306\377\307\306" + "\306\377\306\307\306\377\307\306\306\377\306\306\306\377\306\306\306\377\306" + "\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\305\305\305\377" + "\305\305\306\377\306\305\305\377\305\305\305\377\305\305\306\377\305\305\305" + "\377\305\305\305\377\305\305\305\377\305\304\305\377\304\304\304\377\304\304" + "\305\377\304\304\305\377\304\304\304\377\304\304\304\377\304\304\304\377\304" + "\304\304\377\304\304\304\377\304\304\304\377\324\324\324\377g\203\253\377" + "\40J\207+\0\0\0\0\0\0\0\0\40J\207+h\204\254\377\326\326\326\377\307\306\306" + "\377\306\306\306\377\306\306\306\377\306\306\306\377\306\306\306\377\306\305" + "\306\377\306\306\305\377\305\305\306\377\305\306\306\377\305\305\305\377\305" + "\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377" + "\305\304\305\377\305\305\305\377\304\304\305\377\304\304\304\377\304\304\304" + "\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304\304\377\304\304" + "\303\377\304\303\304\377\303\303\304\377\303\303\304\377\303\303\303\377\303" + "\303\303\377\303\302\303\377\303\303\303\377\303\303\303\377\303\302\303\377" + "\303\302\303\377\303\302\303\377\302\302\302\377\302\302\302\377\302\302\302" + "\377\302\302\302\377\323\323\323\377h\204\254\377\40J\207+\0\0\0\0\0\0\0\0" + "\40J\206+h\204\254\377\325\325\325\377\304\305\305\377\305\305\305\377\305" + "\304\304\377\304\304\305\377\304\305\305\377\304\304\304\377\305\304\305\377" + "\304\305\304\377\304\304\304\377\304\304\304\377\304\304\303\377\304\304\304" + "\377\304\304\303\377\303\303\304\377\303\303\303\377\304\303\303\377\303\303" + "\303\377\303\303\303\377\303\303\303\377\303\302\303\377\303\302\303\377\302" + "\303\303\377\302\302\303\377\302\303\303\377\302\303\302\377\302\302\302\377" + "\302\302\302\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301" + "\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301" + "\301\377\301\301\301\377\301\301\301\377\301\301\301\377\301\301\301\377\322" + "\322\322\377g\203\253\377\37H\204,\0\0\0\0\0\0\0\1\35Cy0f\202\252\377\350" + "\350\350\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377" + "\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344" + "\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344\344\377\344\344" + "\344\377\344\344\344\377\344\344\344\377\343\343\343\377\343\343\343\377\344" + "\344\344\377\344\343\344\377\343\343\343\377\343\343\343\377\343\343\343\377" + "\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\342\342\342" + "\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342" + "\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342\342\342\377\342" + "\342\342\377\342\342\342\377\342\342\342\377\347\347\347\377f\202\252\377" + "\33=p3\0\0\0\5\0\0\0\14\27""5`<+T\216\377<d\233\377<d\233\377<d\233\377<d" + "\233\377<d\233\377<d\233\377<c\233\377:b\232\377-U\217\377<c\233\377;c\233" + "\377:c\233\377:c\233\377:c\233\377:c\233\377:c\233\377:c\232\377:c\232\377" + ":b\232\377:c\232\377:c\232\377:b\232\377:b\232\377:b\232\3779b\232\3779b\232" + "\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3779b\232\3778b\232\377" + "+U\217\3778a\231\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232\3778a\232" + "\3777`\231\377)R\216\377\25""1YA\0\0\0\23\0\0\0\25\24.UD4_\234\377R\202\277" + "\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202\277\377R\202" + "\277\377N\177\273\3771^\232\377R\202\277\377Q\202\277\377Q\202\277\377Q\202" + "\277\377Q\202\277\377Q\202\277\377Q\202\277\377Q\201\276\377Q\201\276\377" + "P\201\276\377Q\201\276\377Q\201\276\377P\201\276\377P\201\276\377P\201\276" + "\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201\276\377P\201" + "\276\377O\200\276\377O\200\276\377O\200\276\3770]\232\377N\177\274\377O\200" + "\276\377O\200\276\377O\200\276\377N\200\276\377N\200\276\377N\200\276\377" + "N\177\275\3771]\233\377\22+OI\0\0\0\34\0\0\0\33\20%C@*S\214\377<b\227\377" + "<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377;_\226\377-T\216" + "\377;`\226\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377<a\227\377" + "<a\227\377<a\227\377<a\227\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226" + "\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377;`\226\377" + ";`\226\377;`\226\377-T\215\377;`\226\377;`\226\377;`\226\377;`\226\377;a\226" + "\377;a\226\377;a\226\377<a\227\377*R\214\377\17!=G\0\0\0#\0\0\0\33\3\7\14" + ")\31""6bw\30""4\\}\26""0W\205\25.S\214\25.R\215\25.R\215\25.R\215\25.R\215" + "\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25" + ".R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25." + "R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R" + "\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25.R\215\25-Q\217" + "\26/T\212\27""2Y\202\30""4^{\2\5\12""1\0\0\0#\0\0\0\25\0\0\0\37\0\0\0+\0\0" + "\0""4\0\0\0=\0\0\0C\0\0\0D\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0" + "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0" + "\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0" + "C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0C\0\0\0D\0\0\0F\0\0\0B\0\0\0:\0" + "\0\0""2\0\0\0&\0\0\0\35\0\0\0\15\0\0\0\26\0\0\0\40\0\0\0'\0\0\0/\0\0\0""3" + "\0\0\0""3\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0" + "\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0" + "\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0" + """2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2\0\0\0""2" + "\0\0\0""2\0\0\0""2\0\0\0""3\0\0\0""5\0\0\0""2\0\0\0,\0\0\0%\0\0\0\34\0\0\0" + "\23\0\0\0\1\0\0\0\7\0\0\0\16\0\0\0\25\0\0\0\32\0\0\0\35\0\0\0\35\0\0\0\34" + "\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0" + "\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0" + "\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0" + "\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34\0\0\0\34" + "\0\0\0\35\0\0\0\36\0\0\0\35\0\0\0\30\0\0\0\23\0\0\0\12\0\0\0\3\0\0\0\0\0\0" + "\0\0\0\0\0\2\0\0\0\5\0\0\0\10\0\0\0\13\0\0\0\14\0\0\0\13\0\0\0\13\0\0\0\13" + "\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0" + "\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0" + "\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0" + "\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\13\0\0\0\14\0\0\0\14" + "\0\0\0\12\0\0\0\6\0\0\0\3\0\0\0\0\0\0\0\0"); + diff --git a/obrender/image.c b/obrender/image.c new file mode 100644 index 0000000..196d9d1 --- /dev/null +++ b/obrender/image.c @@ -0,0 +1,868 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + image.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "geom.h" +#include "image.h" +#include "color.h" +#include "imagecache.h" +#ifdef USE_IMLIB2 +#include <Imlib2.h> +#endif + +#include <glib.h> + +#define FRACTION 12 +#define FLOOR(i) ((i) & (~0UL << FRACTION)) +#define AVERAGE(a, b) (((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b))) + +/************************************************************************ + RrImagePic functions. + + RrImagePics are pictures that are grouped together into RrImageSets. Each + RrImagePic in the set has the same logical image inside it, but they are + of different sizes. An RrImagePic can be an original (which comes from some + outside source, such as an image file), or resized from some other RrImagePic + to meet the needs of the user. +**************************************************************************/ + + +/*! Set up an RrImagePic. + This does _not_ make a copy of the data. So the value of data must be + owned by the caller of this function, and not freed afterward. + This function does not allocate an RrImagePic, and can be used for setting + up a temporary RrImagePic on the stack. Such an object would then also + not be freed with RrImagePicFree. +*/ +static void RrImagePicInit(RrImagePic *pic, gint w, gint h, RrPixel32 *data) +{ + gint i; + + pic->width = w; + pic->height = h; + pic->data = data; + pic->sum = 0; + for (i = w*h; i > 0; --i) + pic->sum += *(data++); +} + +/*! Create a new RrImagePic from some picture data. + This makes a duplicate of the data. +*/ +static RrImagePic* RrImagePicNew(gint w, gint h, RrPixel32 *data) +{ + RrImagePic *pic; + + pic = g_slice_new(RrImagePic); + RrImagePicInit(pic, w, h, g_memdup(data, w*h*sizeof(RrPixel32))); + return pic; +} + + +/*! Destroy an RrImagePic. + This frees the RrImagePic object and everything inside it. +*/ +static void RrImagePicFree(RrImagePic *pic) +{ + if (pic) { + g_free(pic->data); + g_slice_free(RrImagePic, pic); + } +} + +/************************************************************************ + RrImageSet functions. + + RrImageSets hold a group of pictures, each of which represent the same logical + image, but are physically different sizes. + At any time, it may be discovered that two different RrImageSets are actually + holding the same logical image. At that time, they would be merged. + An RrImageSet holds both original images which come from an outside source, + and resized images, which are generated when requests for a specific size are + made, and kept around in case they are request again. There is a maximum + number of resized images that an RrImageSet will keep around, however. + + Each RrImage points to a single RrImageSet, which keeps track of which + RrImages point to it. If two RrImageSets are merged, then the RrImages which + pointed to the two RrImageSets will all point at the resulting merged set. +**************************************************************************/ + + +/*! Free an RrImageSet and the stuff inside it. + This should only occur when there are no more RrImages pointing to the set. +*/ +static void RrImageSetFree(RrImageSet *self) +{ + GSList *it; + gint i; + + if (self) { + g_assert(self->images == NULL); + + /* remove all names associated with this RrImageSet */ + for (it = self->names; it; it = g_slist_next(it)) { + g_hash_table_remove(self->cache->name_table, it->data); + g_free(it->data); + } + g_slist_free(self->names); + + /* destroy the RrImagePic objects stored in the RrImageSet. they will + be keys in the cache to RrImageSet objects, so remove them from + the cache's pic_table as well. */ + for (i = 0; i < self->n_original; ++i) { + g_hash_table_remove(self->cache->pic_table, self->original[i]); + RrImagePicFree(self->original[i]); + } + g_free(self->original); + for (i = 0; i < self->n_resized; ++i) { + g_hash_table_remove(self->cache->pic_table, self->resized[i]); + RrImagePicFree(self->resized[i]); + } + g_free(self->resized); + + g_slice_free(RrImageSet, self); + } +} + +/*! Remove a picture from an RrImageSet as a given position. + @param set The RrImageSet to remove the picture from. + @param i The index of the picture in the RrImageSet in the list of + originals (if @original is TRUE), or in the list of resized pictures (if + @original is FALSE). + @param original TRUE if the picture is an original, FALSE if it is a resized + version of another picture in the RrImageSet. + */ +static void RrImageSetRemovePictureAt(RrImageSet *self, gint i, + gboolean original) +{ + RrImagePic ***list; + gint *len; + + if (original) { + list = &self->original; + len = &self->n_original; + } + else { + list = &self->resized; + len = &self->n_resized; + } + + g_assert(i >= 0 && i < *len); + + /* remove the picture data as a key in the cache */ + g_hash_table_remove(self->cache->pic_table, (*list)[i]); + + /* free the picture being removed */ + RrImagePicFree((*list)[i]); + + /* copy the elements after the removed one in the array forward one space + and shrink the array down one size */ + for (i = i+1; i < *len; ++i) + (*list)[i-1] = (*list)[i]; + --(*len); + *list = g_renew(RrImagePic*, *list, *len); +} + +/*! Add an RrImagePic to an RrImageSet. + The RrImagePic should _not_ exist in the image cache already. + Pictures are added to the front of the list, to maintain the ordering of + newest to oldest. +*/ +static void RrImageSetAddPicture(RrImageSet *self, RrImagePic *pic, + gboolean original) +{ + gint i; + RrImagePic ***list; + gint *len; + + g_assert(pic->width > 0 && pic->height > 0); + g_assert(g_hash_table_lookup(self->cache->pic_table, pic) == NULL); + + /* choose which list in the RrImageSet to add the new picture to. */ + if (original) { + /* remove the resized picture of the same size if one exists */ + for (i = 0; i < self->n_resized; ++i) + if (self->resized[i]->width == pic->width || + self->resized[i]->height == pic->height) + { + RrImageSetRemovePictureAt(self, i, FALSE); + break; + } + + list = &self->original; + len = &self->n_original; + } + else { + list = &self->resized; + len = &self->n_resized; + } + + /* grow the list by one spot, shift everything down one, and insert the new + picture at the front of the list */ + *list = g_renew(RrImagePic*, *list, ++*len); + for (i = *len-1; i > 0; --i) + (*list)[i] = (*list)[i-1]; + (*list)[0] = pic; + + /* add the picture as a key to point to this image in the cache */ + g_hash_table_insert(self->cache->pic_table, (*list)[0], self); + +/* +#ifdef DEBUG + g_debug("Adding %s picture to the cache:\n " + "Image 0x%lx, w %d h %d Hash %u", + (*list == self->original ? "ORIGINAL" : "RESIZED"), + (gulong)self, pic->width, pic->height, RrImagePicHash(pic)); +#endif +*/ +} + +/*! Merges two image sets, destroying one, and returning the other. */ +RrImageSet* RrImageSetMergeSets(RrImageSet *b, RrImageSet *a) +{ + gint a_i, b_i, merged_i; + RrImagePic **original, **resized; + gint n_original, n_resized, tmp; + GSList *it; + + const gint max_resized = a->cache->max_resized_saved; + + if (!a) + return b; + if (!b) + return a; + if (a == b) + return b; + + /* the original and resized picture lists in an RrImageSet are kept ordered + as newest to oldest. we don't have timestamps for them, so we cannot + preserve this in the merged RrImageSet exactly. a decent approximation, + i think, is to add them in alternating order (one from a, one from b, + repeat). this way, the newest from each will be near the front at + least, and in the resized list, when we drop an old picture, we will + not always only drop from a or b only, but from each of them equally (or + from whichever has more resized pictures. + */ + + g_assert(b->cache == a->cache); + + a_i = b_i = merged_i = 0; + n_original = a->n_original + b->n_original; + original = g_new(RrImagePic*, n_original); + while (merged_i < n_original) { + if (a_i < a->n_original) + original[merged_i++] = a->original[a_i++]; + if (b_i < b->n_original) + original[merged_i++] = b->original[b_i++]; + } + + a_i = b_i = merged_i = 0; + n_resized = MIN(max_resized, a->n_resized + b->n_resized); + resized = g_new(RrImagePic*, n_resized); + while (merged_i < n_resized) { + if (a_i < a->n_resized) + resized[merged_i++] = a->resized[a_i++]; + if (b_i < b->n_resized && merged_i < n_resized) + resized[merged_i++] = b->resized[b_i++]; + } + + /* if there are any RrImagePic objects left over in a->resized or + b->resized, they need to be disposed of, and removed from the cache. + + updates the size of the list, as we want to remember which pointers + were merged from which list (and don't want to remember the ones we + did not merge and have freed). + */ + tmp = a_i; + for (; a_i < a->n_resized; ++a_i) { + g_hash_table_remove(a->cache->pic_table, a->resized[a_i]); + RrImagePicFree(a->resized[a_i]); + } + a->n_resized = tmp; + + tmp = b_i; + for (; b_i < b->n_resized; ++b_i) { + g_hash_table_remove(a->cache->pic_table, b->resized[b_i]); + RrImagePicFree(b->resized[b_i]); + } + b->n_resized = tmp; + + /* we will use the a object as the merge destination, so things in b will + be moving. + + the cache's name_table will point to b for all the names in b->names, + so these need to be updated to point at a instead. + also, the cache's pic_table will point to b for all the pictures in b, + so these need to be updated to point at a as well. + + any RrImage objects that were using b should now use a instead. + + the names and images will be all moved into a, and the merged picture + lists will be placed in a. the pictures in a and b are moved to new + arrays, so the arrays in a and b need to be freed explicitly (the + RrImageSetFree function would free the picture data too which we do not + want here). then b can be freed. + */ + + for (it = b->names; it; it = g_slist_next(it)) + g_hash_table_insert(a->cache->name_table, it->data, a); + for (b_i = 0; b_i < b->n_original; ++b_i) + g_hash_table_insert(a->cache->pic_table, b->original[b_i], a); + for (b_i = 0; b_i < b->n_resized; ++b_i) + g_hash_table_insert(a->cache->pic_table, b->resized[b_i], a); + + for (it = b->images; it; it = g_slist_next(it)) + ((RrImage*)it->data)->set = a; + + a->images = g_slist_concat(a->images, b->images); + b->images = NULL; + a->names = g_slist_concat(a->names, b->names); + b->names = NULL; + + a->n_original = a->n_resized = 0; + g_free(a->original); + g_free(a->resized); + a->original = a->resized = NULL; + b->n_original = b->n_resized = 0; + g_free(b->original); + g_free(b->resized); + b->original = b->resized = NULL; + + a->n_original = n_original; + a->original = original; + a->n_resized = n_resized; + a->resized = resized; + + RrImageSetFree(b); + + return a; +} + +static void RrImageSetAddName(RrImageSet *set, const gchar *name) +{ + gchar *n; + + n = g_strdup(name); + set->names = g_slist_prepend(set->names, n); + + /* add the new name to the hash table */ + g_assert(g_hash_table_lookup(set->cache->name_table, n) == NULL); + g_hash_table_insert(set->cache->name_table, n, set); +} + + +/************************************************************************ + RrImage functions. +**************************************************************************/ + + +void RrImageRef(RrImage *self) +{ + ++self->ref; +} + +void RrImageUnref(RrImage *self) +{ + if (self && --self->ref == 0) { + RrImageSet *set; +/* +#ifdef DEBUG + g_debug("Refcount to 0, removing ALL pictures from the cache:\n " + "Image 0x%lx", (gulong)self); +#endif +*/ + if (self->destroy_func) + self->destroy_func(self, self->destroy_data); + + set = self->set; + set->images = g_slist_remove(set->images, self); + + /* free the set as well if there are no images pointing to it */ + if (!set->images) + RrImageSetFree(set); + g_slice_free(RrImage, self); + } +} + +/*! Set function that will be called just before RrImage is destroyed. */ +void RrImageSetDestroyFunc(RrImage *self, RrImageDestroyFunc func, + gpointer data) +{ + self->destroy_func = func; + self->destroy_data = data; +} + +void RrImageAddFromData(RrImage *self, RrPixel32 *data, gint w, gint h) +{ + RrImagePic pic, *ppic; + RrImageSet *set; + + g_return_if_fail(self != NULL); + g_return_if_fail(data != NULL); + g_return_if_fail(w > 0 && h > 0); + + RrImagePicInit(&pic, w, h, data); + set = g_hash_table_lookup(self->set->cache->pic_table, &pic); + if (set) + self->set = RrImageSetMergeSets(self->set, set); + else { + ppic = RrImagePicNew(w, h, data); + RrImageSetAddPicture(self->set, ppic, TRUE); + } +} + +RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data, + gint w, gint h) +{ + RrImagePic pic, *ppic; + RrImage *self; + RrImageSet *set; + + g_return_val_if_fail(cache != NULL, NULL); + g_return_val_if_fail(data != NULL, NULL); + g_return_val_if_fail(w > 0 && h > 0, NULL); + + /* finds a picture in the cache, if it is already in there, and use the + RrImageSet the picture lives in. */ + RrImagePicInit(&pic, w, h, data); + set = g_hash_table_lookup(cache->pic_table, &pic); + if (set) { + self = set->images->data; /* just grab any RrImage from the list */ + RrImageRef(self); + return self; + } + + /* the image does not exist in any RrImageSet in the cache, so make + a new RrImageSet, and a new RrImage that points to it, and place the + new image inside the new RrImageSet */ + + self = g_slice_new0(RrImage); + self->ref = 1; + self->set = g_slice_new0(RrImageSet); + self->set->cache = cache; + self->set->images = g_slist_append(self->set->images, self); + + ppic = RrImagePicNew(w, h, data); + RrImageSetAddPicture(self->set, ppic, TRUE); + + return self; +} + +RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name) +{ +#ifndef USE_IMLIB2 + return NULL; +#else + RrImage *self; + RrImageSet *set; + Imlib_Image img; + gint w, h; + RrPixel32 *data; + gchar *path; + + g_return_val_if_fail(cache != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + set = g_hash_table_lookup(cache->name_table, name); + if (set) { + self = set->images->data; + RrImageRef(self); + return self; + } + + /* XXX find the path via freedesktop icon spec (use obt) ! */ + path = g_strdup(name); + + if (!(img = imlib_load_image(path))) + g_message("Cannot load image \"%s\" from file \"%s\"", name, path); + g_free(path); + + if (!img) + return NULL; + + /* Get data and dimensions of the image. + + WARNING: This stuff is NOT threadsafe !! + */ + imlib_context_set_image(img); + data = imlib_image_get_data_for_reading_only(); + w = imlib_image_get_width(); + h = imlib_image_get_height(); + + /* get an RrImage that contains an RrImageSet with this picture in it. + the RrImage might be new, or reused if the picture was already in the + cache. + + either way, we get back an RrImageSet (via the RrImage), and we must add + the name to that RrImageSet. because of the check above, we know that + there is no RrImageSet in the cache which already has the given name + asosciated with it. + */ + + self = RrImageNewFromData(cache, data, w, h); + RrImageSetAddName(self->set, name); + + imlib_free_image(); + return self; +#endif +} + +/************************************************************************ + Image drawing and resizing operations. +**************************************************************************/ + +/*! Given a picture in RGBA format, of a specified size, resize it to the new + requested size (but keep its aspect ratio). If the image does not need to + be resized (it is already the right size) then this returns NULL. Otherwise + it returns a newly allocated RrImagePic with the resized picture inside it + @return Returns a newly allocated RrImagePic object with a new version of the + image in the requested size (keeping aspect ratio). +*/ +static RrImagePic* ResizeImage(RrPixel32 *src, + gulong srcW, gulong srcH, + gulong dstW, gulong dstH) +{ + RrPixel32 *dst, *dststart; + RrImagePic *pic; + gulong dstX, dstY, srcX, srcY; + gulong srcX1, srcX2, srcY1, srcY2; + gulong ratioX, ratioY; + gulong aspectW, aspectH; + + g_assert(srcW > 0); + g_assert(srcH > 0); + g_assert(dstW > 0); + g_assert(dstH > 0); + + /* keep the aspect ratio */ + aspectW = dstW; + aspectH = (gint)(dstW * ((gdouble)srcH / srcW)); + if (aspectH > dstH) { + aspectH = dstH; + aspectW = (gint)(dstH * ((gdouble)srcW / srcH)); + } + dstW = aspectW ? aspectW : 1; + dstH = aspectH ? aspectH : 1; + + if (srcW == dstW && srcH == dstH) + return NULL; /* no scaling needed! */ + + dststart = dst = g_new(RrPixel32, dstW * dstH); + + ratioX = (srcW << FRACTION) / dstW; + ratioY = (srcH << FRACTION) / dstH; + + srcY2 = 0; + for (dstY = 0; dstY < dstH; dstY++) { + srcY1 = srcY2; + srcY2 += ratioY; + + srcX2 = 0; + for (dstX = 0; dstX < dstW; dstX++) { + gulong red = 0, green = 0, blue = 0, alpha = 0; + gulong portionX, portionY, portionXY, sumXY = 0; + RrPixel32 pixel; + + srcX1 = srcX2; + srcX2 += ratioX; + + for (srcY = srcY1; srcY < srcY2; srcY += (1UL << FRACTION)) { + if (srcY == srcY1) { + srcY = FLOOR(srcY); + portionY = (1UL << FRACTION) - (srcY1 - srcY); + if (portionY > srcY2 - srcY1) + portionY = srcY2 - srcY1; + } + else if (srcY == FLOOR(srcY2)) + portionY = srcY2 - srcY; + else + portionY = (1UL << FRACTION); + + for (srcX = srcX1; srcX < srcX2; srcX += (1UL << FRACTION)) { + if (srcX == srcX1) { + srcX = FLOOR(srcX); + portionX = (1UL << FRACTION) - (srcX1 - srcX); + if (portionX > srcX2 - srcX1) + portionX = srcX2 - srcX1; + } + else if (srcX == FLOOR(srcX2)) + portionX = srcX2 - srcX; + else + portionX = (1UL << FRACTION); + + portionXY = (portionX * portionY) >> FRACTION; + sumXY += portionXY; + + pixel = *(src + (srcY >> FRACTION) * srcW + + (srcX >> FRACTION)); + red += ((pixel >> RrDefaultRedOffset) & 0xFF) + * portionXY; + green += ((pixel >> RrDefaultGreenOffset) & 0xFF) + * portionXY; + blue += ((pixel >> RrDefaultBlueOffset) & 0xFF) + * portionXY; + alpha += ((pixel >> RrDefaultAlphaOffset) & 0xFF) + * portionXY; + } + } + + g_assert(sumXY != 0); + red /= sumXY; + green /= sumXY; + blue /= sumXY; + alpha /= sumXY; + + *dst++ = (red << RrDefaultRedOffset) | + (green << RrDefaultGreenOffset) | + (blue << RrDefaultBlueOffset) | + (alpha << RrDefaultAlphaOffset); + } + } + + pic = g_slice_new(RrImagePic); + RrImagePicInit(pic, dstW, dstH, dststart); + + return pic; +} + +/*! This draws an RGBA picture into the target, within the rectangle specified + by the area parameter. If the area's size differs from the source's then it + will be centered within the rectangle */ +void DrawRGBA(RrPixel32 *target, gint target_w, gint target_h, + RrPixel32 *source, gint source_w, gint source_h, + gint alpha, RrRect *area) +{ + RrPixel32 *dest; + gint col, num_pixels; + gint dw, dh; + + g_assert(source_w <= area->width && source_h <= area->height); + g_assert(area->x + area->width <= target_w); + g_assert(area->y + area->height <= target_h); + + /* keep the aspect ratio */ + dw = area->width; + dh = (gint)(dw * ((gdouble)source_h / source_w)); + if (dh > area->height) { + dh = area->height; + dw = (gint)(dh * ((gdouble)source_w / source_h)); + } + + /* copy source -> dest, and apply the alpha channel. + center the image if it is smaller than the area */ + col = 0; + num_pixels = dw * dh; + dest = target + area->x + (area->width - dw) / 2 + + (target_w * (area->y + (area->height - dh) / 2)); + while (num_pixels-- > 0) { + guchar a, r, g, b, bgr, bgg, bgb; + + /* apply the rgba's opacity as well */ + a = ((*source >> RrDefaultAlphaOffset) * alpha) >> 8; + r = *source >> RrDefaultRedOffset; + g = *source >> RrDefaultGreenOffset; + b = *source >> RrDefaultBlueOffset; + + /* background color */ + bgr = *dest >> RrDefaultRedOffset; + bgg = *dest >> RrDefaultGreenOffset; + bgb = *dest >> RrDefaultBlueOffset; + + r = bgr + (((r - bgr) * a) >> 8); + g = bgg + (((g - bgg) * a) >> 8); + b = bgb + (((b - bgb) * a) >> 8); + + *dest = ((r << RrDefaultRedOffset) | + (g << RrDefaultGreenOffset) | + (b << RrDefaultBlueOffset)); + + dest++; + source++; + + if (++col >= dw) { + col = 0; + dest += target_w - dw; + } + } +} + +/*! Draw an RGBA texture into a target pixel buffer. */ +void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, + gint target_w, gint target_h, + RrRect *area) +{ + RrImagePic *scaled; + + scaled = ResizeImage(rgba->data, rgba->width, rgba->height, + area->width, area->height); + + if (scaled) { +#ifdef DEBUG + g_warning("Scaling an RGBA! You should avoid this and just make " + "it the right size yourself!"); +#endif + DrawRGBA(target, target_w, target_h, + scaled->data, scaled->width, scaled->height, + rgba->alpha, area); + RrImagePicFree(scaled); + } + else + DrawRGBA(target, target_w, target_h, + rgba->data, rgba->width, rgba->height, + rgba->alpha, area); +} + +/*! Draw an RrImage texture into a target pixel buffer. If the RrImage does + not contain a picture of the appropriate size, then one of its "original" + pictures will be resized and used (and stored in the RrImage as a "resized" + picture). + */ +void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, + gint target_w, gint target_h, + RrRect *area) +{ + gint i, min_diff, min_i, min_aspect_diff, min_aspect_i; + RrImage *self; + RrImageSet *set; + RrImagePic *pic; + gboolean free_pic; + + self = img->image; + set = self->set; + pic = NULL; + free_pic = FALSE; + + /* is there an original of this size? (only the larger of + w or h has to be right cuz we maintain aspect ratios) */ + for (i = 0; i < set->n_original; ++i) + if ((set->original[i]->width >= set->original[i]->height && + set->original[i]->width == area->width) || + (set->original[i]->width <= set->original[i]->height && + set->original[i]->height == area->height)) + { + pic = set->original[i]; + break; + } + + /* is there a resize of this size? */ + for (i = 0; i < set->n_resized; ++i) + if ((set->resized[i]->width >= set->resized[i]->height && + set->resized[i]->width == area->width) || + (set->resized[i]->width <= set->resized[i]->height && + set->resized[i]->height == area->height)) + { + gint j; + RrImagePic *saved; + + /* save the selected one */ + saved = set->resized[i]; + + /* shift all the others down */ + for (j = i; j > 0; --j) + set->resized[j] = set->resized[j-1]; + + /* and move the selected one to the top of the list */ + set->resized[0] = saved; + + pic = set->resized[0]; + break; + } + + if (!pic) { + gdouble aspect; + RrImageSet *cache_set; + + /* find an original with a close size */ + min_diff = min_aspect_diff = -1; + min_i = min_aspect_i = 0; + aspect = ((gdouble)area->width) / area->height; + for (i = 0; i < set->n_original; ++i) { + gint diff; + gint wdiff, hdiff; + gdouble myasp; + + /* our size difference metric.. */ + wdiff = set->original[i]->width - area->width; + if (wdiff < 0) wdiff *= 2; /* prefer scaling down than up */ + hdiff = set->original[i]->height - area->height; + if (hdiff < 0) hdiff *= 2; /* prefer scaling down than up */ + diff = (wdiff * wdiff) + (hdiff * hdiff); + + /* find the smallest difference */ + if (min_diff < 0 || diff < min_diff) { + min_diff = diff; + min_i = i; + } + /* and also find the smallest difference with the same aspect + ratio (and prefer this one) */ + myasp = ((gdouble)set->original[i]->width) / + set->original[i]->height; + if (ABS(aspect - myasp) < 0.0000001 && + (min_aspect_diff < 0 || diff < min_aspect_diff)) + { + min_aspect_diff = diff; + min_aspect_i = i; + } + } + + /* use the aspect ratio correct source if there is one */ + if (min_aspect_i >= 0) + min_i = min_aspect_i; + + /* resize the original to the given area */ + pic = ResizeImage(set->original[min_i]->data, + set->original[min_i]->width, + set->original[min_i]->height, + area->width, area->height); + + /* is it already in the cache ? */ + cache_set = g_hash_table_lookup(set->cache->pic_table, pic); + if (cache_set) { + /* merge this set with the one found in the cache - they are + apparently the same image ! then next time we won't have to do + this resizing, we will use the cache_set's pic instead. */ + set = RrImageSetMergeSets(set, cache_set); + free_pic = TRUE; + } + else { + /* add the resized image to the image, as the first in the resized + list */ + while (set->n_resized >= set->cache->max_resized_saved) + /* remove the last one (last used one) to make space for + adding our resized picture */ + RrImageSetRemovePictureAt(set, set->n_resized-1, FALSE); + if (set->cache->max_resized_saved) + /* add it to the resized list */ + RrImageSetAddPicture(set, pic, FALSE); + else + free_pic = TRUE; /* don't leak mem! */ + } + } + + /* The RrImageSet may have changed if we merged it with another, so the + RrImage object needs to be updated to use the new merged RrImageSet. */ + self->set = set; + + g_assert(pic != NULL); + + DrawRGBA(target, target_w, target_h, + pic->data, pic->width, pic->height, + img->alpha, area); + if (free_pic) + RrImagePicFree(pic); +} diff --git a/obrender/image.h b/obrender/image.h new file mode 100644 index 0000000..28f29c2 --- /dev/null +++ b/obrender/image.h @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + image.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __image_h +#define __image_h + +#include "render.h" +#include "geom.h" + +void RrImageDrawImage(RrPixel32 *target, RrTextureImage *img, + gint target_w, gint target_h, + RrRect *area); +void RrImageDrawRGBA(RrPixel32 *target, RrTextureRGBA *rgba, + gint target_w, gint target_h, + RrRect *area); + +#endif diff --git a/obrender/imagecache.c b/obrender/imagecache.c new file mode 100644 index 0000000..909d874 --- /dev/null +++ b/obrender/imagecache.c @@ -0,0 +1,140 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + imagecache.c for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "imagecache.h" +#include "image.h" + +static gboolean RrImagePicEqual(const RrImagePic *p1, + const RrImagePic *p2); + +RrImageCache* RrImageCacheNew(gint max_resized_saved) +{ + RrImageCache *self; + + g_assert(max_resized_saved >= 0); + + self = g_slice_new(RrImageCache); + self->ref = 1; + self->max_resized_saved = max_resized_saved; + self->pic_table = g_hash_table_new((GHashFunc)RrImagePicHash, + (GEqualFunc)RrImagePicEqual); + self->name_table = g_hash_table_new(g_str_hash, g_str_equal); + return self; +} + +void RrImageCacheRef(RrImageCache *self) +{ + ++self->ref; +} + +void RrImageCacheUnref(RrImageCache *self) +{ + if (self && --self->ref == 0) { + g_assert(g_hash_table_size(self->pic_table) == 0); + g_hash_table_unref(self->pic_table); + self->pic_table = NULL; + + g_assert(g_hash_table_size(self->name_table) == 0); + g_hash_table_destroy(self->name_table); + self->name_table = NULL; + + g_slice_free(RrImageCache, self); + } +} + +#define hashsize(n) ((RrPixel32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) +/* mix -- mix 3 32-bit values reversibly. */ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} +/* final -- final mixing of 3 32-bit values (a,b,c) into c */ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* This is a fast, reversable hash function called "lookup3", found here: + http://burtleburtle.net/bob/c/lookup3.c, by Bob Jenkins + + This hashing algorithm is "reversible", that is, not cryptographically + secure at all. But we don't care about that, we just want something to + tell when images are the same or different relatively quickly. +*/ +guint32 hashword(const guint32 *key, gint length, guint32 initval) +{ + guint32 a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval; + + /* handle most of the key */ + while (length > 3) + { + a += key[0]; + b += key[1]; + c += key[2]; + mix(a,b,c); + length -= 3; + key += 3; + } + + /* handle the last 3 guint32's */ + switch(length) /* all the case statements fall through */ + { + case 3: c+=key[2]; + case 2: b+=key[1]; + case 1: a+=key[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /* report the result */ + return c; +} + +/*! This is some arbitrary initial value for the hashing function. It's + constant so that you get the same result from the same data each time. +*/ +#define HASH_INITVAL 0xf00d + +guint RrImagePicHash(const RrImagePic *p) +{ + return hashword(p->data, p->width * p->height, HASH_INITVAL); +} + +static gboolean RrImagePicEqual(const RrImagePic *p1, + const RrImagePic *p2) +{ + return p1->width == p2->width && p1->height == p2->height && + p1->sum == p2->sum; +} diff --git a/obrender/imagecache.h b/obrender/imagecache.h new file mode 100644 index 0000000..f9f5e4d --- /dev/null +++ b/obrender/imagecache.h @@ -0,0 +1,58 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + imagecache.h for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __imagecache_h +#define __imagecache_h + +#include <glib.h> + +struct _RrImagePic; + +guint RrImagePicHash(const struct _RrImagePic *p); + +/*! Create a new image cache. An image cache is basically a hash table to look + up RrImages. Each RrImage in the cache may contain one or more Pictures, + that is one or more actual copies of image data at various sizes. For eg, + for a window, all of its various icons are loaded into the same RrImage. + When an RrImage is drawn and a picture inside it needs to be resized, that + is also saved within the RrImage. + + For each picture that an RrImage has, the picture is hashed and that is used + as a key to find the RrImage. So, given any picture in any RrImage in the + cache, if you hash it, you will find the RrImage. +*/ +struct _RrImageCache { + gint ref; + /*! When an original picture is resized for an RrImage, the resized picture + is saved in the RrImage. This specifies how many pictures should be + saved at a time. When this is exceeded, the least recently used + "resized" picture is deleted. + */ + gint max_resized_saved; + + /*! A hash table of image sets in the cache that don't have a file name + attached to them, with their key being a hash of the contents of the + image. */ + GHashTable *pic_table; + + /*! Used to find out if an image file has already been loaded into an + image set. Provides a quick file_name -> RrImageSet lookup. */ + GHashTable *name_table; +}; + +#endif diff --git a/obrender/instance.c b/obrender/instance.c new file mode 100644 index 0000000..af0420a --- /dev/null +++ b/obrender/instance.c @@ -0,0 +1,309 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + instance.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "instance.h" + +static RrInstance *definst = NULL; + +static void RrTrueColorSetup (RrInstance *inst); +static void RrPseudoColorSetup (RrInstance *inst); + +#ifdef DEBUG +#include "color.h" +#endif +static void +dest(gpointer data) +{ +#ifdef DEBUG + RrColor *c = data; + if (c->refcount > 0) + g_error("color %d (%d,%d,%d) in hash table with %d " + "leftover references", + c->id, RrColorRed(c), RrColorGreen(c), RrColorBlue(c), + c->refcount); +#endif +} + +#if 0 +static void f(gpointer key, gpointer value, gpointer n) +{ + RrColor *c = value; + if (c->id == *(gint*)n) + g_message("color %d has %d references", c->id, c->refcount); +} + +void print_refs(gint id) +{ + g_hash_table_foreach(RrColorHash(definst), f, &id); +} +#endif + +RrInstance* RrInstanceNew (Display *display, gint screen) +{ + g_type_init(); /* supposedly needed for pango but seems to work without */ + + definst = g_slice_new(RrInstance); + definst->display = display; + definst->screen = screen; + + definst->depth = DefaultDepth(display, screen); + definst->visual = DefaultVisual(display, screen); + definst->colormap = DefaultColormap(display, screen); + definst->pango = pango_xft_get_context(display, screen); + + definst->pseudo_colors = NULL; + + definst->color_hash = g_hash_table_new_full(g_int_hash, g_int_equal, + NULL, dest); + + switch (definst->visual->class) { + case TrueColor: + RrTrueColorSetup(definst); + break; + case PseudoColor: + case StaticColor: + case GrayScale: + case StaticGray: + RrPseudoColorSetup(definst); + break; + default: + g_critical("Unsupported visual class"); + g_free (definst); + return definst = NULL; + } + return definst; +} + +static void RrTrueColorSetup (RrInstance *inst) +{ + gulong red_mask, green_mask, blue_mask; + XImage *timage = NULL; + + timage = XCreateImage(inst->display, inst->visual, inst->depth, + ZPixmap, 0, NULL, 1, 1, 32, 0); + g_assert(timage != NULL); + /* find the offsets for each color in the visual's masks */ + inst->red_mask = red_mask = timage->red_mask; + inst->green_mask = green_mask = timage->green_mask; + inst->blue_mask = blue_mask = timage->blue_mask; + + inst->red_offset = 0; + inst->green_offset = 0; + inst->blue_offset = 0; + + while (! (red_mask & 1)) { inst->red_offset++; red_mask >>= 1; } + while (! (green_mask & 1)) { inst->green_offset++; green_mask >>= 1; } + while (! (blue_mask & 1)) { inst->blue_offset++; blue_mask >>= 1; } + + inst->red_shift = inst->green_shift = inst->blue_shift = 8; + while (red_mask) { red_mask >>= 1; inst->red_shift--; } + while (green_mask) { green_mask >>= 1; inst->green_shift--; } + while (blue_mask) { blue_mask >>= 1; inst->blue_shift--; } + XFree(timage); +} + +#define RrPseudoNcolors(inst) (1 << (inst->pseudo_bpc * 3)) + +static void RrPseudoColorSetup (RrInstance *inst) +{ + XColor icolors[256]; + gint tr, tg, tb, n, r, g, b, i, incolors, ii; + gulong dev; + gint cpc, _ncolors; + + /* determine the number of colors and the bits-per-color */ + inst->pseudo_bpc = 2; /* XXX THIS SHOULD BE A USER OPTION */ + g_assert(inst->pseudo_bpc >= 1); + _ncolors = RrPseudoNcolors(inst); + + if (_ncolors > 1 << inst->depth) { + g_message("Invalid colormap size. Resizing."); + inst->pseudo_bpc = 1 << (inst->depth/3) >> 3; + _ncolors = 1 << (inst->pseudo_bpc * 3); + } + + /* build a color cube */ + inst->pseudo_colors = g_new(XColor, _ncolors); + cpc = 1 << inst->pseudo_bpc; /* colors per channel */ + + for (n = 0, r = 0; r < cpc; r++) + for (g = 0; g < cpc; g++) + for (b = 0; b < cpc; b++, n++) { + tr = (gint)(((gfloat)(r)/(gfloat)(cpc-1)) * 0xFF); + tg = (gint)(((gfloat)(g)/(gfloat)(cpc-1)) * 0xFF); + tb = (gint)(((gfloat)(b)/(gfloat)(cpc-1)) * 0xFF); + inst->pseudo_colors[n].red = tr | tr << 8; + inst->pseudo_colors[n].green = tg | tg << 8; + inst->pseudo_colors[n].blue = tb | tb << 8; + /* used to track allocation */ + inst->pseudo_colors[n].flags = DoRed|DoGreen|DoBlue; + } + + /* allocate the colors */ + for (i = 0; i < _ncolors; i++) + if (!XAllocColor(inst->display, inst->colormap, + &inst->pseudo_colors[i])) + inst->pseudo_colors[i].flags = 0; /* mark it as unallocated */ + + /* try allocate any colors that failed allocation above */ + + /* get the allocated values from the X server + (only the first 256 XXX why!?) + */ + incolors = (((1 << inst->depth) > 256) ? 256 : (1 << inst->depth)); + for (i = 0; i < incolors; i++) + icolors[i].pixel = i; + XQueryColors(inst->display, inst->colormap, icolors, incolors); + + /* try match unallocated ones */ + for (i = 0; i < _ncolors; i++) { + if (!inst->pseudo_colors[i].flags) { /* if it wasn't allocated... */ + gulong closest = 0xffffffff, close = 0; + for (ii = 0; ii < incolors; ii++) { + /* find deviations */ + r = (inst->pseudo_colors[i].red - icolors[ii].red) & 0xff; + g = (inst->pseudo_colors[i].green - icolors[ii].green) & 0xff; + b = (inst->pseudo_colors[i].blue - icolors[ii].blue) & 0xff; + /* find a weighted absolute deviation */ + dev = (r * r) + (g * g) + (b * b); + + if (dev < closest) { + closest = dev; + close = ii; + } + } + + inst->pseudo_colors[i].red = icolors[close].red; + inst->pseudo_colors[i].green = icolors[close].green; + inst->pseudo_colors[i].blue = icolors[close].blue; + inst->pseudo_colors[i].pixel = icolors[close].pixel; + + /* try alloc this closest color, it had better succeed! */ + if (XAllocColor(inst->display, inst->colormap, + &inst->pseudo_colors[i])) + /* mark as alloced */ + inst->pseudo_colors[i].flags = DoRed|DoGreen|DoBlue; + else + /* wtf has gone wrong, its already alloced for chissake! */ + g_assert_not_reached(); + } + } +} + +void RrInstanceFree (RrInstance *inst) +{ + if (inst) { + if (inst == definst) definst = NULL; + g_free(inst->pseudo_colors); + g_hash_table_destroy(inst->color_hash); + g_object_unref(inst->pango); + g_slice_free(RrInstance, inst); + } +} + +Display* RrDisplay (const RrInstance *inst) +{ + return (inst ? inst : definst)->display; +} + +gint RrScreen (const RrInstance *inst) +{ + return (inst ? inst : definst)->screen; +} + +Window RrRootWindow (const RrInstance *inst) +{ + return RootWindow (RrDisplay (inst), RrScreen (inst)); +} + +Visual *RrVisual (const RrInstance *inst) +{ + return (inst ? inst : definst)->visual; +} + +gint RrDepth (const RrInstance *inst) +{ + return (inst ? inst : definst)->depth; +} + +Colormap RrColormap (const RrInstance *inst) +{ + return (inst ? inst : definst)->colormap; +} + +gint RrRedOffset (const RrInstance *inst) +{ + return (inst ? inst : definst)->red_offset; +} + +gint RrGreenOffset (const RrInstance *inst) +{ + return (inst ? inst : definst)->green_offset; +} + +gint RrBlueOffset (const RrInstance *inst) +{ + return (inst ? inst : definst)->blue_offset; +} + +gint RrRedShift (const RrInstance *inst) +{ + return (inst ? inst : definst)->red_shift; +} + +gint RrGreenShift (const RrInstance *inst) +{ + return (inst ? inst : definst)->green_shift; +} + +gint RrBlueShift (const RrInstance *inst) +{ + return (inst ? inst : definst)->blue_shift; +} + +gint RrRedMask (const RrInstance *inst) +{ + return (inst ? inst : definst)->red_mask; +} + +gint RrGreenMask (const RrInstance *inst) +{ + return (inst ? inst : definst)->green_mask; +} + +gint RrBlueMask (const RrInstance *inst) +{ + return (inst ? inst : definst)->blue_mask; +} + +guint RrPseudoBPC (const RrInstance *inst) +{ + return (inst ? inst : definst)->pseudo_bpc; +} + +XColor *RrPseudoColors (const RrInstance *inst) +{ + return (inst ? inst : definst)->pseudo_colors; +} + +GHashTable* RrColorHash (const RrInstance *inst) +{ + return (inst ? inst : definst)->color_hash; +} diff --git a/obrender/instance.h b/obrender/instance.h new file mode 100644 index 0000000..324f061 --- /dev/null +++ b/obrender/instance.h @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + instance.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __render_instance_h +#define __render_instance_h + +#include <X11/Xlib.h> +#include <glib.h> +#include <pango/pangoxft.h> + +struct _RrInstance { + Display *display; + gint screen; + + Visual *visual; + gint depth; + Colormap colormap; + PangoContext *pango; + + gint red_offset; + gint green_offset; + gint blue_offset; + + gint red_shift; + gint green_shift; + gint blue_shift; + + gint red_mask; + gint green_mask; + gint blue_mask; + + gint pseudo_bpc; + XColor *pseudo_colors; + + GHashTable *color_hash; +}; + +guint RrPseudoBPC (const RrInstance *inst); +XColor* RrPseudoColors (const RrInstance *inst); +GHashTable* RrColorHash (const RrInstance *inst); + +#endif diff --git a/obrender/mask.c b/obrender/mask.c new file mode 100644 index 0000000..506d5b2 --- /dev/null +++ b/obrender/mask.c @@ -0,0 +1,82 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + mask.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "color.h" +#include "mask.h" + +RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst, + gint w, gint h, const gchar *data) +{ + RrPixmapMask *m = g_slice_new(RrPixmapMask); + m->inst = inst; + m->width = w; + m->height = h; + /* round up to nearest byte */ + m->data = g_memdup(data, (w + 7) / 8 * h); + m->mask = XCreateBitmapFromData(RrDisplay(inst), RrRootWindow(inst), + data, w, h); + return m; +} + +void RrPixmapMaskFree(RrPixmapMask *m) +{ + if (m) { + XFreePixmap(RrDisplay(m->inst), m->mask); + g_free(m->data); + g_slice_free(RrPixmapMask, m); + } +} + +void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area) +{ + gint x, y; + if (m->mask == NULL) return; /* no mask given */ + + /* set the clip region */ + x = area->x + (area->width - m->mask->width) / 2; + y = area->y + (area->height - m->mask->height) / 2; + + if (x < 0) x = 0; + if (y < 0) y = 0; + + XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), m->mask->mask); + XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), x, y); + + /* fill in the clipped region */ + XFillRectangle(RrDisplay(m->mask->inst), p, RrColorGC(m->color), x, y, + x + m->mask->width, y + m->mask->height); + + /* unset the clip region */ + XSetClipMask(RrDisplay(m->mask->inst), RrColorGC(m->color), None); + XSetClipOrigin(RrDisplay(m->mask->inst), RrColorGC(m->color), 0, 0); +} + +RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src) +{ + RrPixmapMask *m = g_slice_new(RrPixmapMask); + m->inst = src->inst; + m->width = src->width; + m->height = src->height; + /* round up to nearest byte */ + m->data = g_memdup(src->data, (src->width + 7) / 8 * src->height); + m->mask = XCreateBitmapFromData(RrDisplay(m->inst), RrRootWindow(m->inst), + m->data, m->width, m->height); + return m; +} diff --git a/obrender/mask.h b/obrender/mask.h new file mode 100644 index 0000000..4dc8503 --- /dev/null +++ b/obrender/mask.h @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + mask.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __mask_h +#define __mask_h + +#include "render.h" +#include "geom.h" + +RrPixmapMask *RrPixmapMaskNew(const RrInstance *inst, + gint w, gint h, const gchar *data); +void RrPixmapMaskFree(RrPixmapMask *m); +RrPixmapMask *RrPixmapMaskCopy(const RrPixmapMask *src); +void RrPixmapMaskDraw(Pixmap p, const RrTextureMask *m, const RrRect *area); + +#endif diff --git a/obrender/obrender-3.5.pc.in b/obrender/obrender-3.5.pc.in new file mode 100644 index 0000000..2c8a435 --- /dev/null +++ b/obrender/obrender-3.5.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +xcflags=@X_CFLAGS@ +xlibs=@X_LIBS@ + +Name: ObRender +Description: Openbox Render Library +Version: @RR_VERSION@ +Requires: obt-3.5 glib-2.0 xft pangoxft @PKG_CONFIG_IMLIB@ +Libs: -L${libdir} -lobrender ${xlibs} +Cflags: -I${includedir}/openbox/@RR_VERSION@ ${xcflags} diff --git a/obrender/render.c b/obrender/render.c new file mode 100644 index 0000000..fe9a2a8 --- /dev/null +++ b/obrender/render.c @@ -0,0 +1,575 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + render.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "gradient.h" +#include "font.h" +#include "mask.h" +#include "color.h" +#include "image.h" +#include "theme.h" + +#include <glib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xft/Xft.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +static void pixel_data_to_pixmap(RrAppearance *l, + gint x, gint y, gint w, gint h); + +Pixmap RrPaintPixmap(RrAppearance *a, gint w, gint h) +{ + gint i, transferred = 0, force_transfer = 0; + Pixmap oldp = None; + RrRect tarea; /* area in which to draw textures */ + gboolean resized; + + if (w <= 0 || h <= 0) return None; + + if (a->surface.parentx < 0 || a->surface.parenty < 0) { + /* ob_debug("Invalid parent co-ordinates\n"); */ + return None; + } + + if (a->surface.grad == RR_SURFACE_PARENTREL && + (a->surface.parentx >= a->surface.parent->w || + a->surface.parenty >= a->surface.parent->h)) + { + return None; + } + + resized = (a->w != w || a->h != h); + + oldp = a->pixmap; /* save to free after changing the visible pixmap */ + a->pixmap = XCreatePixmap(RrDisplay(a->inst), + RrRootWindow(a->inst), + w, h, RrDepth(a->inst)); + + g_assert(a->pixmap != None); + a->w = w; + a->h = h; + + if (a->xftdraw != NULL) + XftDrawDestroy(a->xftdraw); + a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap, + RrVisual(a->inst), RrColormap(a->inst)); + g_assert(a->xftdraw != NULL); + + if (resized) { + g_free(a->surface.pixel_data); + a->surface.pixel_data = g_new(RrPixel32, w * h); + } + + RrRender(a, w, h); + + { + gint l, t, r, b; + RrMargins(a, &l, &t, &r, &b); + RECT_SET(tarea, l, t, w - l - r, h - t - b); + } + + for (i = 0; i < a->textures; i++) { + switch (a->texture[i].type) { + case RR_TEXTURE_NONE: + break; + case RR_TEXTURE_TEXT: + if (!transferred) { + transferred = 1; + if ((a->surface.grad != RR_SURFACE_SOLID) + || (a->surface.interlaced)) + pixel_data_to_pixmap(a, 0, 0, w, h); + } + if (a->xftdraw == NULL) { + a->xftdraw = XftDrawCreate(RrDisplay(a->inst), a->pixmap, + RrVisual(a->inst), + RrColormap(a->inst)); + } + RrFontDraw(a->xftdraw, &a->texture[i].data.text, &tarea); + break; + case RR_TEXTURE_LINE_ART: + if (!transferred) { + transferred = 1; + if ((a->surface.grad != RR_SURFACE_SOLID) + || (a->surface.interlaced)) + pixel_data_to_pixmap(a, 0, 0, w, h); + } + XDrawLine(RrDisplay(a->inst), a->pixmap, + RrColorGC(a->texture[i].data.lineart.color), + a->texture[i].data.lineart.x1, + a->texture[i].data.lineart.y1, + a->texture[i].data.lineart.x2, + a->texture[i].data.lineart.y2); + break; + case RR_TEXTURE_MASK: + if (!transferred) { + transferred = 1; + if ((a->surface.grad != RR_SURFACE_SOLID) + || (a->surface.interlaced)) + pixel_data_to_pixmap(a, 0, 0, w, h); + } + RrPixmapMaskDraw(a->pixmap, &a->texture[i].data.mask, &tarea); + break; + case RR_TEXTURE_IMAGE: + g_assert(!transferred); + { + RrRect narea = tarea; + RrTextureImage *img = &a->texture[i].data.image; + narea.x += img->tx; + narea.width -= img->tx; + narea.y += img->ty; + narea.height -= img->ty; + if (img->twidth) + narea.width = MIN(narea.width, img->twidth); + if (img->theight) + narea.height = MIN(narea.height, img->theight); + RrImageDrawImage(a->surface.pixel_data, + &a->texture[i].data.image, + a->w, a->h, + &narea); + } + force_transfer = 1; + break; + case RR_TEXTURE_RGBA: + g_assert(!transferred); + { + RrRect narea = tarea; + RrTextureRGBA *rgb = &a->texture[i].data.rgba; + narea.x += rgb->tx; + narea.width -= rgb->tx; + narea.y += rgb->ty; + narea.height -= rgb->ty; + if (rgb->twidth) + narea.width = MIN(narea.width, rgb->twidth); + if (rgb->theight) + narea.height = MIN(narea.height, rgb->theight); + RrImageDrawRGBA(a->surface.pixel_data, + &a->texture[i].data.rgba, + a->w, a->h, + &narea); + } + force_transfer = 1; + break; + case RR_TEXTURE_NUM_TYPES: + g_assert_not_reached(); + } + } + + if (!transferred) { + transferred = 1; + if ((a->surface.grad != RR_SURFACE_SOLID) || (a->surface.interlaced) || + force_transfer) + { + pixel_data_to_pixmap(a, 0, 0, w, h); + } + } + + return oldp; +} + +void RrPaint(RrAppearance *a, Window win, gint w, gint h) +{ + Pixmap oldp; + + oldp = RrPaintPixmap(a, w, h); + XSetWindowBackgroundPixmap(RrDisplay(a->inst), win, a->pixmap); + XClearWindow(RrDisplay(a->inst), win); + /* free this after changing the visible pixmap */ + if (oldp) XFreePixmap(RrDisplay(a->inst), oldp); +} + +RrAppearance *RrAppearanceNew(const RrInstance *inst, gint numtex) +{ + RrAppearance *out; + + out = g_slice_new0(RrAppearance); + out->inst = inst; + out->textures = numtex; + out->surface.bevel_light_adjust = 128; + out->surface.bevel_dark_adjust = 64; + if (numtex) out->texture = g_new0(RrTexture, numtex); + + return out; +} + +void RrAppearanceRemoveTextures(RrAppearance *a) +{ + g_free(a->texture); + a->textures = 0; +} + +void RrAppearanceAddTextures(RrAppearance *a, gint numtex) +{ + g_assert(a->textures == 0); + + a->textures = numtex; + if (numtex) a->texture = g_new0(RrTexture, numtex); +} + +void RrAppearanceClearTextures(RrAppearance *a) +{ + memset(a->texture, 0, a->textures * sizeof(RrTexture)); +} + +/* deep copy of orig, means reset ref to 1 on copy + * and copy each thing memwise. */ +RrAppearance *RrAppearanceCopy(RrAppearance *orig) +{ + RrSurface *spo, *spc; + RrAppearance *copy = g_slice_new(RrAppearance); + + copy->inst = orig->inst; + + spo = &(orig->surface); + spc = &(copy->surface); + spc->grad = spo->grad; + spc->relief = spo->relief; + spc->bevel = spo->bevel; + if (spo->primary != NULL) + spc->primary = RrColorNew(copy->inst, + spo->primary->r, + spo->primary->g, + spo->primary->b); + else spc->primary = NULL; + + if (spo->secondary != NULL) + spc->secondary = RrColorNew(copy->inst, + spo->secondary->r, + spo->secondary->g, + spo->secondary->b); + else spc->secondary = NULL; + + if (spo->border_color != NULL) + spc->border_color = RrColorNew(copy->inst, + spo->border_color->r, + spo->border_color->g, + spo->border_color->b); + else spc->border_color = NULL; + + if (spo->interlace_color != NULL) + spc->interlace_color = RrColorNew(copy->inst, + spo->interlace_color->r, + spo->interlace_color->g, + spo->interlace_color->b); + else spc->interlace_color = NULL; + + if (spo->bevel_dark != NULL) + spc->bevel_dark = RrColorNew(copy->inst, + spo->bevel_dark->r, + spo->bevel_dark->g, + spo->bevel_dark->b); + else spc->bevel_dark = NULL; + + if (spo->bevel_light != NULL) + spc->bevel_light = RrColorNew(copy->inst, + spo->bevel_light->r, + spo->bevel_light->g, + spo->bevel_light->b); + else spc->bevel_light = NULL; + + if (spo->split_primary != NULL) + spc->split_primary = RrColorNew(copy->inst, + spo->split_primary->r, + spo->split_primary->g, + spo->split_primary->b); + else spc->split_primary = NULL; + + if (spo->split_secondary != NULL) + spc->split_secondary = RrColorNew(copy->inst, + spo->split_secondary->r, + spo->split_secondary->g, + spo->split_secondary->b); + else spc->split_secondary = NULL; + + spc->interlaced = spo->interlaced; + spc->bevel_light_adjust = spo->bevel_light_adjust; + spc->bevel_dark_adjust = spo->bevel_dark_adjust; + spc->border = spo->border; + spc->parent = NULL; + spc->parentx = spc->parenty = 0; + spc->pixel_data = NULL; + + copy->textures = orig->textures; + copy->texture = g_memdup(orig->texture, + orig->textures * sizeof(RrTexture)); + copy->pixmap = None; + copy->xftdraw = NULL; + copy->w = copy->h = 0; + return copy; +} + +/* now decrements ref counter, and frees only if ref <= 0 */ +void RrAppearanceFree(RrAppearance *a) +{ + if (a) { + RrSurface *p; + if (a->pixmap != None) XFreePixmap(RrDisplay(a->inst), a->pixmap); + if (a->xftdraw != NULL) XftDrawDestroy(a->xftdraw); + if (a->textures) + g_free(a->texture); + p = &a->surface; + RrColorFree(p->primary); + RrColorFree(p->secondary); + RrColorFree(p->border_color); + RrColorFree(p->interlace_color); + RrColorFree(p->bevel_dark); + RrColorFree(p->bevel_light); + RrColorFree(p->split_primary); + RrColorFree(p->split_secondary); + g_free(p->pixel_data); + p->pixel_data = NULL; + g_slice_free(RrAppearance, a); + } +} + +static void pixel_data_to_pixmap(RrAppearance *l, + gint x, gint y, gint w, gint h) +{ + RrPixel32 *in, *scratch; + Pixmap out; + XImage *im = NULL; + im = XCreateImage(RrDisplay(l->inst), RrVisual(l->inst), RrDepth(l->inst), + ZPixmap, 0, NULL, w, h, 32, 0); + g_assert(im != NULL); + + in = l->surface.pixel_data; + out = l->pixmap; + +/* this malloc is a complete waste of time on normal 32bpp + as reduce_depth just sets im->data = data and returns +*/ + scratch = g_new(RrPixel32, im->width * im->height); + im->data = (gchar*) scratch; + RrReduceDepth(l->inst, in, im); + XPutImage(RrDisplay(l->inst), out, + DefaultGC(RrDisplay(l->inst), RrScreen(l->inst)), + im, 0, 0, x, y, w, h); + im->data = NULL; + XDestroyImage(im); + g_free(scratch); +} + +void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b) +{ + *l = *t = *r = *b = 0; + + if (a->surface.grad != RR_SURFACE_PARENTREL) { + if (a->surface.relief != RR_RELIEF_FLAT) { + switch (a->surface.bevel) { + case RR_BEVEL_1: + *l = *t = *r = *b = 1; + break; + case RR_BEVEL_2: + *l = *t = *r = *b = 2; + break; + case RR_BEVEL_NUM_TYPES: + g_assert_not_reached(); + } + } else if (a->surface.border) { + *l = *t = *r = *b = 1; + } + } +} + +void RrMinSize(RrAppearance *a, gint *w, gint *h) +{ + *w = RrMinWidth(a); + *h = RrMinHeight(a); +} + +gint RrMinWidth(RrAppearance *a) +{ + gint i; + RrSize *m; + gint l, t, r, b; + gint w = 0; + + RrMargins(a, &l, &t, &r, &b); + + for (i = 0; i < a->textures; ++i) { + switch (a->texture[i].type) { + case RR_TEXTURE_NONE: + break; + case RR_TEXTURE_MASK: + w = MAX(w, a->texture[i].data.mask.mask->width); + break; + case RR_TEXTURE_TEXT: + m = RrFontMeasureString(a->texture[i].data.text.font, + a->texture[i].data.text.string, + a->texture[i].data.text.shadow_offset_x, + a->texture[i].data.text.shadow_offset_y, + a->texture[i].data.text.flow, + a->texture[i].data.text.maxwidth); + w = MAX(w, m->width); + g_slice_free(RrSize, m); + break; + case RR_TEXTURE_RGBA: + w += MAX(w, a->texture[i].data.rgba.width); + break; + case RR_TEXTURE_IMAGE: + /* images resize so they don't contribute anything to the min */ + break; + case RR_TEXTURE_LINE_ART: + w = MAX(w, MAX(a->texture[i].data.lineart.x1 - l - r, + a->texture[i].data.lineart.x2 - l - r)); + break; + case RR_TEXTURE_NUM_TYPES: + g_assert_not_reached(); + } + } + + w += l + r; + + if (w < 1) w = 1; + return w; +} + +gint RrMinHeight(RrAppearance *a) +{ + gint i; + gint l, t, r, b; + RrSize *m; + gint h = 0; + + RrMargins(a, &l, &t, &r, &b); + + for (i = 0; i < a->textures; ++i) { + switch (a->texture[i].type) { + case RR_TEXTURE_NONE: + break; + case RR_TEXTURE_MASK: + h = MAX(h, a->texture[i].data.mask.mask->height); + break; + case RR_TEXTURE_TEXT: + if (a->texture[i].data.text.flow) { + g_assert(a->texture[i].data.text.string != NULL); + + m = RrFontMeasureString + (a->texture[i].data.text.font, + a->texture[i].data.text.string, + a->texture[i].data.text.shadow_offset_x, + a->texture[i].data.text.shadow_offset_y, + a->texture[i].data.text.flow, + a->texture[i].data.text.maxwidth); + h += MAX(h, m->height); + g_slice_free(RrSize, m); + } + else + h += MAX(h, + RrFontHeight + (a->texture[i].data.text.font, + a->texture[i].data.text.shadow_offset_y)); + break; + case RR_TEXTURE_RGBA: + h += MAX(h, a->texture[i].data.rgba.height); + break; + case RR_TEXTURE_IMAGE: + /* images resize so they don't contribute anything to the min */ + break; + case RR_TEXTURE_LINE_ART: + h = MAX(h, MAX(a->texture[i].data.lineart.y1 - t - b, + a->texture[i].data.lineart.y2 - t - b)); + break; + case RR_TEXTURE_NUM_TYPES: + g_assert_not_reached(); + } + } + + h += t + b; + + if (h < 1) h = 1; + return h; +} + +static void reverse_bits(gchar *c, gint n) +{ + gint i; + for (i = 0; i < n; i++, c++) + *c = (((*c * 0x0802UL & 0x22110UL) | + (*c * 0x8020UL & 0x88440UL)) * 0x10101UL) >> 16; +} + +gboolean RrPixmapToRGBA(const RrInstance *inst, + Pixmap pmap, Pixmap mask, + gint *w, gint *h, RrPixel32 **data) +{ + Window xr; + gint xx, xy; + guint pw, ph, mw, mh, xb, xd, i, x, y, di; + XImage *xi, *xm = NULL; + + if (!XGetGeometry(RrDisplay(inst), pmap, + &xr, &xx, &xy, &pw, &ph, &xb, &xd)) + return FALSE; + + if (mask) { + if (!XGetGeometry(RrDisplay(inst), mask, + &xr, &xx, &xy, &mw, &mh, &xb, &xd)) + return FALSE; + if (pw != mw || ph != mh || xd != 1) + return FALSE; + } + + xi = XGetImage(RrDisplay(inst), pmap, + 0, 0, pw, ph, 0xffffffff, ZPixmap); + if (!xi) + return FALSE; + + if (mask) { + xm = XGetImage(RrDisplay(inst), mask, + 0, 0, mw, mh, 0xffffffff, ZPixmap); + if (!xm) { + XDestroyImage(xi); + return FALSE; + } + if ((xm->bits_per_pixel == 1) && (xm->bitmap_bit_order != LSBFirst)) + reverse_bits(xm->data, xm->bytes_per_line * xm->height); + } + + if ((xi->bits_per_pixel == 1) && (xi->bitmap_bit_order != LSBFirst)) + reverse_bits(xi->data, xi->bytes_per_line * xi->height); + + *data = g_new(RrPixel32, pw * ph); + RrIncreaseDepth(inst, *data, xi); + + if (mask) { + /* apply transparency from the mask */ + di = 0; + for (i = 0, y = 0; y < ph; ++y) { + for (x = 0; x < pw; ++x, ++i) { + if (!((((unsigned)xm->data[di + x / 8]) >> (x % 8)) & 0x1)) + (*data)[i] &= ~(0xff << RrDefaultAlphaOffset); + } + di += xm->bytes_per_line; + } + } + + *w = pw; + *h = ph; + + XDestroyImage(xi); + if (mask) + XDestroyImage(xm); + + return TRUE; +} diff --git a/obrender/render.h b/obrender/render.h new file mode 100644 index 0000000..a5d6500 --- /dev/null +++ b/obrender/render.h @@ -0,0 +1,462 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + render.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __render_h +#define __render_h + +#include <X11/Xlib.h> /* some platforms dont include this as needed for Xft */ +#include <pango/pangoxft.h> +#include <glib.h> + +G_BEGIN_DECLS + +#include "obrender/geom.h" +#include "obrender/version.h" + +typedef union _RrTextureData RrTextureData; +typedef struct _RrAppearance RrAppearance; +typedef struct _RrSurface RrSurface; +typedef struct _RrFont RrFont; +typedef struct _RrTexture RrTexture; +typedef struct _RrTextureMask RrTextureMask; +typedef struct _RrTextureRGBA RrTextureRGBA; +typedef struct _RrTextureImage RrTextureImage; +typedef struct _RrTextureText RrTextureText; +typedef struct _RrTextureLineArt RrTextureLineArt; +typedef struct _RrPixmapMask RrPixmapMask; +typedef struct _RrInstance RrInstance; +typedef struct _RrColor RrColor; +typedef struct _RrImage RrImage; +typedef struct _RrImageSet RrImageSet; +typedef struct _RrImagePic RrImagePic; +typedef struct _RrImageCache RrImageCache; +typedef struct _RrButton RrButton; + +typedef guint32 RrPixel32; /* RGBA format */ +typedef guint16 RrPixel16; +typedef guchar RrPixel8; + +typedef enum { + RR_RELIEF_FLAT, + RR_RELIEF_RAISED, + RR_RELIEF_SUNKEN, + RR_RELIEF_NUM_TYPES +} RrReliefType; + +typedef enum { + RR_BEVEL_1, + RR_BEVEL_2, + RR_BEVEL_NUM_TYPES +} RrBevelType; + +typedef enum { + RR_SURFACE_NONE, + RR_SURFACE_PARENTREL, + RR_SURFACE_SOLID, + RR_SURFACE_SPLIT_VERTICAL, + RR_SURFACE_HORIZONTAL, + RR_SURFACE_VERTICAL, + RR_SURFACE_DIAGONAL, + RR_SURFACE_CROSS_DIAGONAL, + RR_SURFACE_PYRAMID, + RR_SURFACE_MIRROR_HORIZONTAL, + RR_SURFACE_NUM_TYPES +} RrSurfaceColorType; + +typedef enum { + RR_TEXTURE_NONE, + RR_TEXTURE_MASK, + RR_TEXTURE_TEXT, + RR_TEXTURE_LINE_ART, + RR_TEXTURE_RGBA, + RR_TEXTURE_IMAGE, + RR_TEXTURE_NUM_TYPES +} RrTextureType; + +typedef enum { + RR_JUSTIFY_LEFT, + RR_JUSTIFY_CENTER, + RR_JUSTIFY_RIGHT, + RR_JUSTIFY_NUM_TYPES +} RrJustify; + +/* Put middle first so it's the default */ +typedef enum { + RR_ELLIPSIZE_MIDDLE, + RR_ELLIPSIZE_NONE, + RR_ELLIPSIZE_START, + RR_ELLIPSIZE_END, + RR_ELLIPSIZE_NUM_TYPES +} RrEllipsizeMode; + +typedef enum { + RR_FONTWEIGHT_LIGHT, + RR_FONTWEIGHT_NORMAL, + RR_FONTWEIGHT_SEMIBOLD, + RR_FONTWEIGHT_BOLD, + RR_FONTWEIGHT_ULTRABOLD, + RR_FONTWEIGHT_NUM_TYPES +} RrFontWeight; + +typedef enum { + RR_FONTSLANT_NORMAL, + RR_FONTSLANT_ITALIC, + RR_FONTSLANT_OBLIQUE, + RR_FONTSLANT_NUM_TYPES +} RrFontSlant; + +struct _RrSurface { + RrSurfaceColorType grad; + RrReliefType relief; + RrBevelType bevel; + RrColor *primary; + RrColor *secondary; + RrColor *border_color; + RrColor *bevel_dark; + RrColor *bevel_light; + RrColor *interlace_color; + gboolean interlaced; + gboolean border; + RrAppearance *parent; + gint parentx; + gint parenty; + RrPixel32 *pixel_data; + gint bevel_dark_adjust; /* 0-255, default is 64 */ + gint bevel_light_adjust; /* 0-255, default is 128 */ + RrColor *split_primary; + RrColor *split_secondary; +}; + +struct _RrTextureText { + RrFont *font; + RrJustify justify; + RrColor *color; + const gchar *string; + gint shadow_offset_x; + gint shadow_offset_y; + RrColor *shadow_color; + gboolean shortcut; /*!< Underline a character */ + guint shortcut_pos; /*!< Position in bytes of the character to underline */ + RrEllipsizeMode ellipsize; + gboolean flow; /* allow multiple lines. must set maxwidth below */ + gint maxwidth; + guchar shadow_alpha; /* at the bottom to improve alignment */ +}; + +struct _RrPixmapMask { + const RrInstance *inst; + Pixmap mask; + gint width; + gint height; + gchar *data; +}; + +struct _RrTextureMask { + RrColor *color; + RrPixmapMask *mask; +}; + +struct _RrTextureRGBA { + gint width; + gint height; + gint alpha; + RrPixel32 *data; + /* size and position to draw at (if these are zero, then it will be + drawn to fill the entire texture */ + gint tx; + gint ty; + gint twidth; + gint theight; +}; + +struct _RrTextureImage { + RrImage *image; + gint alpha; + /* size and position to draw at (if these are zero, then it will be + drawn to fill the entire texture */ + gint tx; + gint ty; + gint twidth; + gint theight; +}; + +struct _RrTextureLineArt { + RrColor *color; + gint x1; + gint y1; + gint x2; + gint y2; +}; + +union _RrTextureData { + RrTextureRGBA rgba; + RrTextureImage image; + RrTextureText text; + RrTextureMask mask; + RrTextureLineArt lineart; +}; + +struct _RrTexture { + /* If changing the type of a texture, you should DEFINITELY call + RrAppearanceClearTextures() first! */ + RrTextureType type; + RrTextureData data; +}; + +struct _RrAppearance { + const RrInstance *inst; + + RrSurface surface; + gint textures; + RrTexture *texture; + Pixmap pixmap; + XftDraw *xftdraw; + + /* cached for internal use */ + gint w, h; +}; + +/*! Holds a RGBA image picture */ +struct _RrImagePic { + gint width, height; + RrPixel32 *data; + /* The sum of all the pixels. This is used to compare pictures if their + hashes match. */ + gint sum; +}; + +typedef void (*RrImageDestroyFunc)(RrImage *image, gpointer data); + +/*! An RrImage refers to a RrImageSet. If multiple RrImageSets end up + holding the same image data, they will be marged and the RrImages that + point to them would be updated. */ +struct _RrImage { + gint ref; + RrImageSet *set; + + /* This function (if not NULL) will be called just before destroying + RrImage. */ + RrImageDestroyFunc destroy_func; + gpointer destroy_data; +}; + +/*! An RrImage is a sort of meta-image. It can contain multiple versions + of an image at different sizes, which may or may not be completely different + pictures */ +struct _RrImageSet +{ + RrImageCache *cache; + + /*! If a picture is loaded by a name, then it has a name attached to it. + This contains a list of strings, containing all names that have ever + been associated with the RrImageSet. A name in the RrImageCache can + only be associated with a single RrImageSet. */ + GSList *names; + + /*! RrImages that point at this RrImageSet. If this is empty, then there + are no images using the set and it can be freed. */ + GSList *images; + + /*! An array of "originals", that is of RrPictures that have been added + to the image in various sizes, and that have not been resized. These + are explicitly added to the RrImageSet. */ + RrImagePic **original; + gint n_original; + /*! An array of "resized" pictures. When an "original" RrPicture + needs to be resized for drawing, it is saved in here so that it doesn't + need to be resized again. These are automatically added to the + RrImage. */ + RrImagePic **resized; + gint n_resized; +}; + +struct _RrButton { + const RrInstance *inst; + + /* colors */ + RrColor *focused_unpressed_color; + RrColor *unfocused_unpressed_color; + RrColor *focused_pressed_color; + RrColor *unfocused_pressed_color; + RrColor *disabled_focused_color; + RrColor *disabled_unfocused_color; + RrColor *hover_focused_color; + RrColor *hover_unfocused_color; + RrColor *toggled_hover_focused_color; + RrColor *toggled_hover_unfocused_color; + RrColor *toggled_focused_pressed_color; + RrColor *toggled_unfocused_pressed_color; + RrColor *toggled_focused_unpressed_color; + RrColor *toggled_unfocused_unpressed_color; + + /* masks */ + RrPixmapMask *mask; + RrPixmapMask *pressed_mask; + RrPixmapMask *disabled_mask; + RrPixmapMask *hover_mask; + RrPixmapMask *toggled_mask; + RrPixmapMask *toggled_hover_mask; + RrPixmapMask *toggled_pressed_mask; + + /* textures */ + RrAppearance *a_focused_unpressed; + RrAppearance *a_unfocused_unpressed; + RrAppearance *a_focused_pressed; + RrAppearance *a_unfocused_pressed; + RrAppearance *a_disabled_focused; + RrAppearance *a_disabled_unfocused; + RrAppearance *a_hover_focused; + RrAppearance *a_hover_unfocused; + RrAppearance *a_toggled_focused_unpressed; + RrAppearance *a_toggled_unfocused_unpressed; + RrAppearance *a_toggled_focused_pressed; + RrAppearance *a_toggled_unfocused_pressed; + RrAppearance *a_toggled_hover_focused; + RrAppearance *a_toggled_hover_unfocused; + +}; + +/* these are the same on all endian machines because it seems to be dependant + on the endianness of the gfx card, not the cpu. */ +#define RrDefaultAlphaOffset 24 +#define RrDefaultRedOffset 16 +#define RrDefaultGreenOffset 8 +#define RrDefaultBlueOffset 0 + +#define RrDefaultFontFamily "arial,sans" +#define RrDefaultFontSize 8 +#define RrDefaultFontWeight RR_FONTWEIGHT_NORMAL +#define RrDefaultFontSlant RR_FONTSLANT_NORMAL + +RrInstance* RrInstanceNew (Display *display, gint screen); +void RrInstanceFree (RrInstance *inst); + +Display* RrDisplay (const RrInstance *inst); +gint RrScreen (const RrInstance *inst); +Window RrRootWindow (const RrInstance *inst); +Visual* RrVisual (const RrInstance *inst); +gint RrDepth (const RrInstance *inst); +Colormap RrColormap (const RrInstance *inst); +gint RrRedOffset (const RrInstance *inst); +gint RrGreenOffset (const RrInstance *inst); +gint RrBlueOffset (const RrInstance *inst); +gint RrRedShift (const RrInstance *inst); +gint RrGreenShift (const RrInstance *inst); +gint RrBlueShift (const RrInstance *inst); +gint RrRedMask (const RrInstance *inst); +gint RrGreenMask (const RrInstance *inst); +gint RrBlueMask (const RrInstance *inst); + +RrColor *RrColorNew (const RrInstance *inst, gint r, gint g, gint b); +RrColor *RrColorCopy (RrColor *c); +RrColor *RrColorParse (const RrInstance *inst, gchar *colorname); +void RrColorFree (RrColor *in); + +gint RrColorRed (const RrColor *c); +gint RrColorGreen (const RrColor *c); +gint RrColorBlue (const RrColor *c); +gulong RrColorPixel (const RrColor *c); +GC RrColorGC (RrColor *c); + +RrAppearance *RrAppearanceNew (const RrInstance *inst, gint numtex); +RrAppearance *RrAppearanceCopy (RrAppearance *a); +void RrAppearanceFree (RrAppearance *a); +void RrAppearanceRemoveTextures(RrAppearance *a); +void RrAppearanceAddTextures(RrAppearance *a, gint numtex); +/*! Always call this when changing the type of a texture in an appearance */ +void RrAppearanceClearTextures(RrAppearance *a); + +RrButton *RrButtonNew (const RrInstance *inst); +void RrButtonFree(RrButton *b); + +RrFont *RrFontOpen (const RrInstance *inst, const gchar *name, + gint size, RrFontWeight weight, RrFontSlant slant); +RrFont *RrFontOpenDefault (const RrInstance *inst); +void RrFontClose (RrFont *f); +/*! Returns an RrSize, that was allocated with g_slice_new(). Use g_slice_free() to + free it. */ +RrSize *RrFontMeasureString (const RrFont *f, const gchar *str, + gint shadow_offset_x, gint shadow_offset_y, + gboolean flow, gint maxwidth); +gint RrFontHeight (const RrFont *f, gint shadow_offset_y); +gint RrFontMaxCharWidth (const RrFont *f); + +/* Paint into the appearance. The old pixmap is returned (if there was one). It + is the responsibility of the caller to call XFreePixmap on the return when + it is non-null. */ +Pixmap RrPaintPixmap (RrAppearance *a, gint w, gint h); +void RrPaint (RrAppearance *a, Window win, gint w, gint h); +void RrMinSize (RrAppearance *a, gint *w, gint *h); +gint RrMinWidth (RrAppearance *a); +/* For text textures, if flow is TRUE, then the string must be set before + calling this, otherwise it doesn't need to be */ +gint RrMinHeight (RrAppearance *a); +void RrMargins (RrAppearance *a, gint *l, gint *t, gint *r, gint *b); + +gboolean RrPixmapToRGBA(const RrInstance *inst, + Pixmap pmap, Pixmap mask, + gint *w, gint *h, RrPixel32 **data); + +/*! Create a new image cache for RrImages. + @param max_resized_saved The number of resized copies of an image to save +*/ +RrImageCache* RrImageCacheNew(gint max_resized_saved); +void RrImageCacheRef(RrImageCache *self); +void RrImageCacheUnref(RrImageCache *self); + +/*! Create a new image, or return one from the cache that matches. + @param cache The image cache. + @param old The current RrImage, which the new image should be added to. + Use this if loading a different sized version of the same image. + The returned RrImage should replace the one passed in as old. + Pass NULL here if adding an image which is (or may be) entirely new. + @param name The name of the icon to be loaded off disk, or used in the cache + @return Returns NULL if unable to load an image by the name and it is not in + the cache already +*/ +RrImage* RrImageNewFromName(RrImageCache *cache, const gchar *name); + +/*! Create a new image, or return one from the cache that matches. + @param cache The image cache. + @param data The image data in RGBA32 format. There should be @w * @h many + values in the data array. + @param w The width of the image data. + @param h The height of the image data. + @return Returns NULL if unable to load an image by the name and it is not in + the cache already +*/ +RrImage* RrImageNewFromData(RrImageCache *cache, RrPixel32 *data, + gint w, gint h); + +/*! Add a new size of a picture to an image. + If a picture has multiple versions of different sizes (example 16x16, 32x32 + and so on), they should all be under the same RrImage. This adds a new + size to an existing RrImage, associating the newly sized picture with the + others in the RrImage - classifying them as being the same logical image at a + different dimention. +*/ +void RrImageAddFromData(RrImage *image, RrPixel32 *data, gint w, gint h); + +void RrImageRef(RrImage *im); +void RrImageUnref(RrImage *im); + +G_END_DECLS + +#endif /*__render_h*/ diff --git a/obrender/test.c b/obrender/test.c new file mode 100644 index 0000000..55ab621 --- /dev/null +++ b/obrender/test.c @@ -0,0 +1,115 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + test.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + Copyright (c) 2003 Derek Foreman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/shape.h> +#include <string.h> +#include <stdlib.h> +#include "render.h" +#include <glib.h> + +static gint x_error_handler(Display * disp, XErrorEvent * error) +{ + gchar buf[1024]; + XGetErrorText(disp, error->error_code, buf, 1024); + printf("%s\n", buf); + return 0; +} + +Display *ob_display; +gint ob_screen; +Window ob_root; + +gint main() +{ + Window win; + RrInstance *inst; + RrAppearance *look; + int done; + + Window root; + XEvent report; + gint h = 500, w = 500; + + ob_display = XOpenDisplay(NULL); + XSetErrorHandler(x_error_handler); + ob_screen = DefaultScreen(ob_display); + ob_root = RootWindow(ob_display, ob_screen); + win = + XCreateWindow(ob_display, RootWindow(ob_display, 0), + 10, 10, w, h, 10, + CopyFromParent, /* depth */ + CopyFromParent, /* class */ + CopyFromParent, /* visual */ + 0, /* valuemask */ + 0); /* attributes */ + XMapWindow(ob_display, win); + XSelectInput(ob_display, win, ExposureMask | StructureNotifyMask); + root = RootWindow (ob_display, DefaultScreen (ob_display)); + inst = RrInstanceNew(ob_display, ob_screen); + + look = RrAppearanceNew(inst, 0); + look->surface.grad = RR_SURFACE_MIRROR_HORIZONTAL; + look->surface.secondary = RrColorParse(inst, "Yellow"); + look->surface.split_secondary = RrColorParse(inst, "Red"); + look->surface.split_primary = RrColorParse(inst, "Green"); + look->surface.primary = RrColorParse(inst, "Blue"); + look->surface.interlaced = FALSE; + if (ob_display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + +#if BIGTEST + int i; + look->surface.pixel_data = g_new(RrPixel32, w*h); + for (i = 0; i < 10000; ++i) { + printf("\r%d", i); + fflush(stdout); + RrRender(look, w, h); + } + exit (0); +#endif + + RrPaint(look, win, w, h); + done = 0; + while (!done) { + XNextEvent(ob_display, &report); + switch (report.type) { + case Expose: + break; + case ConfigureNotify: + RrPaint(look, win, + report.xconfigure.width, + report.xconfigure.height); + break; + case UnmapNotify: + done = 1; + break; + } + } + + RrAppearanceFree (look); + RrInstanceFree (inst); + + return 1; +} diff --git a/obrender/theme.c b/obrender/theme.c new file mode 100644 index 0000000..b20182a --- /dev/null +++ b/obrender/theme.c @@ -0,0 +1,2169 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + theme.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "render.h" +#include "color.h" +#include "font.h" +#include "mask.h" +#include "theme.h" +#include "icon.h" +#include "obt/paths.h" + +#include <X11/Xlib.h> +#include <X11/Xresource.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +static XrmDatabase loaddb(const gchar *name, gchar **path); +static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value); +static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value); +static gboolean read_color(XrmDatabase db, const RrInstance *inst, + const gchar *rname, RrColor **value); +static gboolean read_mask(const RrInstance *inst, const gchar *path, + RrTheme *theme, const gchar *maskname, + RrPixmapMask **value); +static gboolean read_appearance(XrmDatabase db, const RrInstance *inst, + const gchar *rname, RrAppearance *value, + gboolean allow_trans); +static int parse_inline_number(const char *p); +static RrPixel32* read_c_image(gint width, gint height, const guint8 *data); +static void set_default_appearance(RrAppearance *a); +static void read_button_colors(XrmDatabase db, const RrInstance *inst, + const RrTheme *theme, RrButton *btn, + const gchar *btnname); + +static RrFont *get_font(RrFont *target, RrFont **default_font, + const RrInstance *inst) +{ + if (target) { + RrFontRef(target); + return target; + } else { + /* Only load the default font once */ + if (*default_font) { + RrFontRef(*default_font); + } else { + *default_font = RrFontOpenDefault(inst); + } + return *default_font; + } +} + +#define READ_INT(x_resstr, x_var, x_min, x_max, x_def) \ + if (!read_int(db, x_resstr, & x_var) || \ + x_var < x_min || x_var > x_max) \ + x_var = x_def; + +#define READ_COLOR(x_resstr, x_var, x_def) \ + if (!read_color(db, inst, x_resstr, & x_var)) \ + x_var = x_def; + +#define READ_COLOR_(x_res1, x_res2, x_var, x_def) \ + if (!read_color(db, inst, x_res1, & x_var) && \ + !read_color(db, inst, x_res2, & x_var)) \ + x_var = x_def; + +#define READ_MASK_COPY(x_file, x_var, x_copysrc) \ + if (!read_mask(inst, path, theme, x_file, & x_var)) \ + x_var = RrPixmapMaskCopy(x_copysrc); + +#define READ_APPEARANCE(x_resstr, x_var, x_parrel) \ + if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) \ + set_default_appearance(x_var); + +#define READ_APPEARANCE_COPY(x_resstr, x_var, x_parrel, x_defval) \ + if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) {\ + RrAppearanceFree(x_var); \ + x_var = RrAppearanceCopy(x_defval); } + +#define READ_APPEARANCE_COPY_TEXTURES(x_resstr, x_var, x_parrel, x_defval, n_tex) \ + if (!read_appearance(db, inst, x_resstr, x_var, x_parrel)) {\ + RrAppearanceFree(x_var); \ + x_var = RrAppearanceCopy(x_defval); \ + RrAppearanceRemoveTextures(x_var); \ + RrAppearanceAddTextures(x_var, 5); } + +#define READ_APPEARANCE_(x_res1, x_res2, x_var, x_parrel, x_defval) \ + if (!read_appearance(db, inst, x_res1, x_var, x_parrel) && \ + !read_appearance(db, inst, x_res2, x_var, x_parrel)) {\ + RrAppearanceFree(x_var); \ + x_var = RrAppearanceCopy(x_defval); } + +RrTheme* RrThemeNew(const RrInstance *inst, const gchar *name, + gboolean allow_fallback, + RrFont *active_window_font, RrFont *inactive_window_font, + RrFont *menu_title_font, RrFont *menu_item_font, + RrFont *active_osd_font, RrFont *inactive_osd_font) +{ + XrmDatabase db = NULL; + RrJustify winjust, mtitlejust; + gchar *str; + RrTheme *theme; + RrFont *default_font = NULL; + gchar *path; + gboolean userdef; + gint menu_overlap = 0; + RrAppearance *a_disabled_focused_tmp; + RrAppearance *a_disabled_unfocused_tmp; + RrAppearance *a_hover_focused_tmp; + RrAppearance *a_hover_unfocused_tmp; + RrAppearance *a_focused_unpressed_tmp; + RrAppearance *a_focused_pressed_tmp; + RrAppearance *a_unfocused_unpressed_tmp; + RrAppearance *a_unfocused_pressed_tmp; + RrAppearance *a_toggled_hover_focused_tmp; + RrAppearance *a_toggled_hover_unfocused_tmp; + RrAppearance *a_toggled_focused_unpressed_tmp; + RrAppearance *a_toggled_focused_pressed_tmp; + RrAppearance *a_toggled_unfocused_unpressed_tmp; + RrAppearance *a_toggled_unfocused_pressed_tmp; + + if (name) { + db = loaddb(name, &path); + if (db == NULL) { + g_message("Unable to load the theme '%s'", name); + if (allow_fallback) + g_message("Falling back to the default theme '%s'", + DEFAULT_THEME); + /* fallback to the default theme */ + name = NULL; + } + } + if (name == NULL) { + if (allow_fallback) { + db = loaddb(DEFAULT_THEME, &path); + if (db == NULL) { + g_message("Unable to load the theme '%s'", DEFAULT_THEME); + return NULL; + } + } else + return NULL; + } + + /* initialize temp reading textures */ + a_disabled_focused_tmp = RrAppearanceNew(inst, 1); + a_disabled_unfocused_tmp = RrAppearanceNew(inst, 1); + a_hover_focused_tmp = RrAppearanceNew(inst, 1); + a_hover_unfocused_tmp = RrAppearanceNew(inst, 1); + a_toggled_focused_unpressed_tmp = RrAppearanceNew(inst, 1); + a_toggled_unfocused_unpressed_tmp = RrAppearanceNew(inst, 1); + a_toggled_hover_focused_tmp = RrAppearanceNew(inst, 1); + a_toggled_hover_unfocused_tmp = RrAppearanceNew(inst, 1); + a_toggled_focused_pressed_tmp = RrAppearanceNew(inst, 1); + a_toggled_unfocused_pressed_tmp = RrAppearanceNew(inst, 1); + a_focused_unpressed_tmp = RrAppearanceNew(inst, 1); + a_focused_pressed_tmp = RrAppearanceNew(inst, 1); + a_unfocused_unpressed_tmp = RrAppearanceNew(inst, 1); + a_unfocused_pressed_tmp = RrAppearanceNew(inst, 1); + + /* initialize theme */ + theme = g_slice_new0(RrTheme); + + theme->inst = inst; + theme->name = g_strdup(name ? name : DEFAULT_THEME); + + /* init buttons */ + theme->btn_max = RrButtonNew(inst); + theme->btn_close = RrButtonNew(inst); + theme->btn_desk = RrButtonNew(inst); + theme->btn_shade = RrButtonNew(inst); + theme->btn_iconify = RrButtonNew(inst); + + /* init appearances */ + theme->a_focused_grip = RrAppearanceNew(inst, 0); + theme->a_unfocused_grip = RrAppearanceNew(inst, 0); + theme->a_focused_title = RrAppearanceNew(inst, 0); + theme->a_unfocused_title = RrAppearanceNew(inst, 0); + theme->a_focused_label = RrAppearanceNew(inst, 1); + theme->a_unfocused_label = RrAppearanceNew(inst, 1); + theme->a_icon = RrAppearanceNew(inst, 1); + theme->a_focused_handle = RrAppearanceNew(inst, 0); + theme->a_unfocused_handle = RrAppearanceNew(inst, 0); + theme->a_menu = RrAppearanceNew(inst, 0); + theme->a_menu_title = RrAppearanceNew(inst, 0); + theme->a_menu_text_title = RrAppearanceNew(inst, 1); + theme->a_menu_normal = RrAppearanceNew(inst, 0); + theme->a_menu_selected = RrAppearanceNew(inst, 0); + theme->a_menu_disabled = RrAppearanceNew(inst, 0); + /* a_menu_disabled_selected is copied from a_menu_selected */ + theme->a_menu_text_normal = RrAppearanceNew(inst, 1); + theme->a_menu_text_selected = RrAppearanceNew(inst, 1); + theme->a_menu_text_disabled = RrAppearanceNew(inst, 1); + theme->a_menu_text_disabled_selected = RrAppearanceNew(inst, 1); + theme->a_menu_bullet_normal = RrAppearanceNew(inst, 1); + theme->a_menu_bullet_selected = RrAppearanceNew(inst, 1); + theme->a_clear = RrAppearanceNew(inst, 0); + theme->a_clear_tex = RrAppearanceNew(inst, 1); + theme->osd_bg = RrAppearanceNew(inst, 0); + theme->osd_hilite_label = RrAppearanceNew(inst, 1); + theme->osd_hilite_bg = RrAppearanceNew(inst, 0); + theme->osd_unhilite_label = RrAppearanceNew(inst, 1); + theme->osd_unhilite_bg = RrAppearanceNew(inst, 0); + theme->osd_unpressed_button = RrAppearanceNew(inst, 1); + theme->osd_pressed_button = RrAppearanceNew(inst, 5); + theme->osd_focused_button = RrAppearanceNew(inst, 5); + + /* load the font stuff */ + theme->win_font_focused = get_font(active_window_font, + &default_font, inst); + theme->win_font_unfocused = get_font(inactive_window_font, + &default_font, inst); + + winjust = RR_JUSTIFY_LEFT; + if (read_string(db, "window.label.text.justify", &str)) { + if (!g_ascii_strcasecmp(str, "right")) + winjust = RR_JUSTIFY_RIGHT; + else if (!g_ascii_strcasecmp(str, "center")) + winjust = RR_JUSTIFY_CENTER; + } + + theme->menu_title_font = get_font(menu_title_font, &default_font, inst); + + mtitlejust = RR_JUSTIFY_LEFT; + if (read_string(db, "menu.title.text.justify", &str)) { + if (!g_ascii_strcasecmp(str, "right")) + mtitlejust = RR_JUSTIFY_RIGHT; + else if (!g_ascii_strcasecmp(str, "center")) + mtitlejust = RR_JUSTIFY_CENTER; + } + + theme->menu_font = get_font(menu_item_font, &default_font, inst); + + theme->osd_font_hilite = get_font(active_osd_font, &default_font, inst); + theme->osd_font_unhilite = get_font(inactive_osd_font, &default_font,inst); + + /* load direct dimensions */ + READ_INT("menu.overlap", menu_overlap, -100, 100, 0); + READ_INT("menu.overlap.x", theme->menu_overlap_x, -100, 100, menu_overlap); + READ_INT("menu.overlap.y", theme->menu_overlap_y, -100, 100, menu_overlap); + READ_INT("window.handle.width", theme->handle_height, 0, 100, 6); + READ_INT("padding.width", theme->paddingx, 0, 100, 3); + READ_INT("padding.height", theme->paddingy, 0, 100, theme->paddingx); + READ_INT("border.width", theme->fbwidth, 0, 100, 1); + READ_INT("menu.border.width", theme->mbwidth, 0, 100, theme->fbwidth); + READ_INT("osd.border.width", theme->obwidth, 0, 100, theme->fbwidth); + READ_INT("undecorated.border.width", theme->ubwidth, 0, 100, + theme->fbwidth); + READ_INT("menu.separator.width", theme->menu_sep_width, 1, 100, 1); + READ_INT("menu.separator.padding.width", theme->menu_sep_paddingx, + 0, 100, 6); + READ_INT("menu.separator.padding.height", theme->menu_sep_paddingy, + 0, 100, 3); + READ_INT("window.client.padding.width", theme->cbwidthx, 0, 100, + theme->paddingx); + READ_INT("window.client.padding.height", theme->cbwidthy, 0, 100, + theme->cbwidthx); + + /* load colors */ + READ_COLOR_("window.active.border.color", "border.color", + theme->frame_focused_border_color, RrColorNew(inst, 0, 0, 0)); + /* undecorated focused border color inherits from frame focused border + color */ + READ_COLOR("window.undecorated.active.border.color", + theme->frame_undecorated_focused_border_color, + RrColorCopy(theme->frame_focused_border_color)); + /* title separator focused color inherits from focused border color */ + READ_COLOR("window.active.title.separator.color", + theme->title_separator_focused_color, + RrColorCopy(theme->frame_focused_border_color)); + + /* unfocused border color inherits from frame focused border color */ + READ_COLOR("window.inactive.border.color", + theme->frame_unfocused_border_color, + RrColorCopy(theme->frame_focused_border_color)); + + /* undecorated unfocused border color inherits from frame unfocused border + color */ + READ_COLOR("window.undecorated.inactive.border.color", + theme->frame_undecorated_unfocused_border_color, + RrColorCopy(theme->frame_unfocused_border_color)); + + /* title separator unfocused color inherits from unfocused border color */ + READ_COLOR("window.inactive.title.separator.color", + theme->title_separator_unfocused_color, + RrColorCopy(theme->frame_unfocused_border_color)); + + /* menu border color inherits from frame focused border color */ + READ_COLOR("menu.border.color", theme->menu_border_color, + RrColorCopy(theme->frame_focused_border_color)); + + /* osd border color inherits from frame focused border color */ + READ_COLOR("osd.border.color", theme->osd_border_color, + RrColorCopy(theme->frame_focused_border_color)); + + READ_COLOR("window.active.client.color", theme->cb_focused_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("window.inactive.client.color", theme->cb_unfocused_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("window.active.label.text.color", theme->title_focused_color, + RrColorNew(inst, 0x0, 0x0, 0x0)); + + READ_COLOR("window.inactive.label.text.color", theme->title_unfocused_color, + RrColorCopy(theme->title_unfocused_color)); + + READ_COLOR_("osd.active.label.text.color", + "osd.label.text.color", + theme->osd_text_active_color, RrColorCopy(theme->title_focused_color)); + + READ_COLOR("osd.inactive.label.text.color", theme->osd_text_inactive_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("window.active.button.unpressed.image.color", + theme->titlebut_focused_unpressed_color, + RrColorNew(inst, 0, 0, 0)); + + READ_COLOR("window.inactive.button.unpressed.image.color", + theme->titlebut_unfocused_unpressed_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("window.active.button.pressed.image.color", + theme->titlebut_focused_pressed_color, + RrColorCopy(theme->titlebut_focused_unpressed_color)); + + READ_COLOR("window.inactive.button.pressed.image.color", + theme->titlebut_unfocused_pressed_color, + RrColorCopy(theme->titlebut_unfocused_unpressed_color)); + + READ_COLOR("window.active.button.disabled.image.color", + theme->titlebut_disabled_focused_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("window.inactive.button.disabled.image.color", + theme->titlebut_disabled_unfocused_color, + RrColorNew(inst, 0, 0, 0)); + + READ_COLOR("window.active.button.hover.image.color", + theme->titlebut_hover_focused_color, + RrColorCopy(theme->titlebut_focused_unpressed_color)); + + READ_COLOR("window.inactive.button.hover.image.color", + theme->titlebut_hover_unfocused_color, + RrColorCopy(theme->titlebut_unfocused_unpressed_color)); + + READ_COLOR_("window.active.button.toggled.unpressed.image.color", + "window.active.button.toggled.image.color", + theme->titlebut_toggled_focused_unpressed_color, + RrColorCopy(theme->titlebut_focused_pressed_color)); + + READ_COLOR_("window.inactive.button.toggled.unpressed.image.color", + "window.inactive.button.toggled.image.color", + theme->titlebut_toggled_unfocused_unpressed_color, + RrColorCopy(theme->titlebut_unfocused_pressed_color)); + + READ_COLOR("window.active.button.toggled.hover.image.color", + theme->titlebut_toggled_hover_focused_color, + RrColorCopy(theme->titlebut_toggled_focused_unpressed_color)); + + READ_COLOR("window.inactive.button.toggled.hover.image.color", + theme->titlebut_toggled_hover_unfocused_color, + RrColorCopy(theme->titlebut_toggled_unfocused_unpressed_color)); + + READ_COLOR("window.active.button.toggled.pressed.image.color", + theme->titlebut_toggled_focused_pressed_color, + RrColorCopy(theme->titlebut_focused_pressed_color)); + + READ_COLOR("window.inactive.button.toggled.pressed.image.color", + theme->titlebut_toggled_unfocused_pressed_color, + RrColorCopy(theme->titlebut_unfocused_pressed_color)); + + READ_COLOR("menu.title.text.color", theme->menu_title_color, + RrColorNew(inst, 0, 0, 0)); + + READ_COLOR("menu.items.text.color", theme->menu_color, + RrColorNew(inst, 0xff, 0xff, 0xff)); + + READ_COLOR("menu.bullet.image.color", theme->menu_bullet_color, + RrColorCopy(theme->menu_color)); + + READ_COLOR("menu.items.disabled.text.color", theme->menu_disabled_color, + RrColorNew(inst, 0, 0, 0)); + + READ_COLOR("menu.items.active.disabled.text.color", + theme->menu_disabled_selected_color, + RrColorCopy(theme->menu_disabled_color)); + + READ_COLOR("menu.items.active.text.color", theme->menu_selected_color, + RrColorNew(inst, 0, 0, 0)); + + READ_COLOR("menu.separator.color", theme->menu_sep_color, + RrColorCopy(theme->menu_color)); + + READ_COLOR("menu.bullet.selected.image.color", + theme->menu_bullet_selected_color, + RrColorCopy(theme->menu_selected_color)); + + READ_COLOR("osd.button.unpressed.text.color", theme->osd_unpressed_color, + RrColorCopy(theme->osd_text_active_color)); + READ_COLOR("osd.button.pressed.text.color", theme->osd_pressed_color, + RrColorCopy(theme->osd_text_active_color)); + READ_COLOR("osd.button.focused.text.color", theme->osd_focused_color, + RrColorCopy(theme->osd_text_active_color)); + READ_COLOR("osd.button.pressed.box.color", theme->osd_pressed_lineart, + RrColorCopy(theme->titlebut_focused_pressed_color)); + READ_COLOR("osd.button.focused.box.color", theme->osd_focused_lineart, + RrColorCopy(theme->titlebut_hover_focused_color)); + + /* load the image masks */ + + /* maximize button masks */ + userdef = TRUE; + if (!read_mask(inst, path, theme, "max.xbm", &theme->btn_max->mask)) { + guchar data[] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; + theme->btn_max->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + userdef = FALSE; + } + if (!read_mask(inst, path, theme, "max_toggled.xbm", + &theme->btn_max->toggled_mask)) + { + if (userdef) + theme->btn_max->toggled_mask = RrPixmapMaskCopy(theme->btn_max->mask); + else { + guchar data[] = { 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }; + theme->btn_max->toggled_mask = RrPixmapMaskNew(inst, 6, 6,(gchar*)data); + } + } + READ_MASK_COPY("max_pressed.xbm", theme->btn_max->pressed_mask, + theme->btn_max->mask); + READ_MASK_COPY("max_disabled.xbm", theme->btn_max->disabled_mask, + theme->btn_max->mask); + READ_MASK_COPY("max_hover.xbm", theme->btn_max->hover_mask, + theme->btn_max->mask); + READ_MASK_COPY("max_toggled_pressed.xbm", + theme->btn_max->toggled_pressed_mask, + theme->btn_max->toggled_mask); + READ_MASK_COPY("max_toggled_hover.xbm", + theme->btn_max->toggled_hover_mask, + theme->btn_max->toggled_mask); + + /* iconify button masks */ + if (!read_mask(inst, path, theme, "iconify.xbm", &theme->btn_iconify->mask)) { + guchar data[] = { 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f }; + theme->btn_iconify->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + } + READ_MASK_COPY("iconify_pressed.xbm", theme->btn_iconify->pressed_mask, + theme->btn_iconify->mask); + READ_MASK_COPY("iconify_disabled.xbm", theme->btn_iconify->disabled_mask, + theme->btn_iconify->mask); + READ_MASK_COPY("iconify_hover.xbm", theme->btn_iconify->hover_mask, + theme->btn_iconify->mask); + + /* all desktops button masks */ + userdef = TRUE; + if (!read_mask(inst, path, theme, "desk.xbm", &theme->btn_desk->mask)) { + guchar data[] = { 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }; + theme->btn_desk->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + userdef = FALSE; + } + if (!read_mask(inst, path, theme, "desk_toggled.xbm", + &theme->btn_desk->toggled_mask)) { + if (userdef) + theme->btn_desk->toggled_mask = RrPixmapMaskCopy(theme->btn_desk->mask); + else { + guchar data[] = { 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }; + theme->btn_desk->toggled_mask = + RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + } + } + READ_MASK_COPY("desk_pressed.xbm", theme->btn_desk->pressed_mask, + theme->btn_desk->mask); + READ_MASK_COPY("desk_disabled.xbm", theme->btn_desk->disabled_mask, + theme->btn_desk->mask); + READ_MASK_COPY("desk_hover.xbm", theme->btn_desk->hover_mask, theme->btn_desk->mask); + READ_MASK_COPY("desk_toggled_pressed.xbm", + theme->btn_desk->toggled_pressed_mask, theme->btn_desk->toggled_mask); + READ_MASK_COPY("desk_toggled_hover.xbm", theme->btn_desk->toggled_hover_mask, + theme->btn_desk->toggled_mask); + + /* shade button masks */ + if (!read_mask(inst, path, theme, "shade.xbm", &theme->btn_shade->mask)) { + guchar data[] = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 }; + theme->btn_shade->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + } + READ_MASK_COPY("shade_toggled.xbm", theme->btn_shade->toggled_mask, + theme->btn_shade->mask); + READ_MASK_COPY("shade_pressed.xbm", theme->btn_shade->pressed_mask, + theme->btn_shade->mask); + READ_MASK_COPY("shade_disabled.xbm", theme->btn_shade->disabled_mask, + theme->btn_shade->mask); + READ_MASK_COPY("shade_hover.xbm", theme->btn_shade->hover_mask, + theme->btn_shade->mask); + READ_MASK_COPY("shade_toggled_pressed.xbm", + theme->btn_shade->toggled_pressed_mask, + theme->btn_shade->toggled_mask); + READ_MASK_COPY("shade_toggled_hover.xbm", + theme->btn_shade->toggled_hover_mask, + theme->btn_shade->toggled_mask); + + /* close button masks */ + if (!read_mask(inst, path, theme, "close.xbm", &theme->btn_close->mask)) { + guchar data[] = { 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }; + theme->btn_close->mask = RrPixmapMaskNew(inst, 6, 6, (gchar*)data); + } + READ_MASK_COPY("close_pressed.xbm", theme->btn_close->pressed_mask, + theme->btn_close->mask); + READ_MASK_COPY("close_disabled.xbm", theme->btn_close->disabled_mask, + theme->btn_close->mask); + READ_MASK_COPY("close_hover.xbm", theme->btn_close->hover_mask, + theme->btn_close->mask); + + /* submenu bullet mask */ + if (!read_mask(inst, path, theme, "bullet.xbm", &theme->menu_bullet_mask)) + { + guchar data[] = { 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 }; + theme->menu_bullet_mask = RrPixmapMaskNew(inst, 4, 7, (gchar*)data); + } + + /* up and down arrows */ + { + guchar data[] = { 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00 }; + theme->down_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data); + } + { + guchar data[] = { 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00 }; + theme->up_arrow_mask = RrPixmapMaskNew(inst, 9, 4, (gchar*)data); + } + + /* setup the default window icon */ + theme->def_win_icon = read_c_image(OB_DEFAULT_ICON_WIDTH, + OB_DEFAULT_ICON_HEIGHT, + OB_DEFAULT_ICON_pixel_data); + theme->def_win_icon_w = OB_DEFAULT_ICON_WIDTH; + theme->def_win_icon_h = OB_DEFAULT_ICON_HEIGHT; + + /* the toggled hover mask = the toggled unpressed mask (i.e. no change) */ + theme->btn_max->toggled_hover_mask = + RrPixmapMaskCopy(theme->btn_max->toggled_mask); + theme->btn_desk->toggled_hover_mask = + RrPixmapMaskCopy(theme->btn_desk->toggled_mask); + theme->btn_shade->toggled_hover_mask = + RrPixmapMaskCopy(theme->btn_shade->toggled_mask); + /* the toggled pressed mask = the toggled unpressed mask (i.e. no change)*/ + theme->btn_max->toggled_pressed_mask = + RrPixmapMaskCopy(theme->btn_max->toggled_mask); + theme->btn_desk->toggled_pressed_mask = + RrPixmapMaskCopy(theme->btn_desk->toggled_mask); + theme->btn_shade->toggled_pressed_mask = + RrPixmapMaskCopy(theme->btn_shade->toggled_mask); + + /* read the decoration textures */ + READ_APPEARANCE("window.active.title.bg", theme->a_focused_title, FALSE); + READ_APPEARANCE("window.inactive.title.bg", theme->a_unfocused_title, + FALSE); + READ_APPEARANCE("window.active.label.bg", theme->a_focused_label, TRUE); + READ_APPEARANCE("window.inactive.label.bg", theme->a_unfocused_label, + TRUE); + READ_APPEARANCE("window.active.handle.bg", theme->a_focused_handle, FALSE); + READ_APPEARANCE("window.inactive.handle.bg",theme->a_unfocused_handle, + FALSE); + READ_APPEARANCE("window.active.grip.bg", theme->a_focused_grip, TRUE); + READ_APPEARANCE("window.inactive.grip.bg", theme->a_unfocused_grip, TRUE); + READ_APPEARANCE("menu.items.bg", theme->a_menu, FALSE); + READ_APPEARANCE("menu.title.bg", theme->a_menu_title, TRUE); + READ_APPEARANCE("menu.items.active.bg", theme->a_menu_selected, TRUE); + + theme->a_menu_disabled_selected = + RrAppearanceCopy(theme->a_menu_selected); + + /* read appearances for non-decorations (on-screen-display) */ + if (!read_appearance(db, inst, "osd.bg", theme->osd_bg, FALSE)) { + RrAppearanceFree(theme->osd_bg); + theme->osd_bg = RrAppearanceCopy(theme->a_focused_title); + } + if (!read_appearance(db, inst, "osd.active.label.bg", + theme->osd_hilite_label, TRUE) && + !read_appearance(db, inst, "osd.label.bg", + theme->osd_hilite_label, TRUE)) { + RrAppearanceFree(theme->osd_hilite_label); + theme->osd_hilite_label = RrAppearanceCopy(theme->a_focused_label); + } + if (!read_appearance(db, inst, "osd.inactive.label.bg", + theme->osd_unhilite_label, TRUE)) { + RrAppearanceFree(theme->osd_unhilite_label); + theme->osd_unhilite_label = RrAppearanceCopy(theme->a_unfocused_label); + } + /* osd_hilite_fg can't be parentrel */ + if (!read_appearance(db, inst, "osd.hilight.bg", + theme->osd_hilite_bg, FALSE)) { + RrAppearanceFree(theme->osd_hilite_bg); + if (theme->a_focused_label->surface.grad != RR_SURFACE_PARENTREL) + theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_label); + else + theme->osd_hilite_bg = RrAppearanceCopy(theme->a_focused_title); + } + /* osd_unhilite_fg can't be parentrel either */ + if (!read_appearance(db, inst, "osd.unhilight.bg", + theme->osd_unhilite_bg, FALSE)) { + RrAppearanceFree(theme->osd_unhilite_bg); + if (theme->a_unfocused_label->surface.grad != RR_SURFACE_PARENTREL) + theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_label); + else + theme->osd_unhilite_bg=RrAppearanceCopy(theme->a_unfocused_title); + } + + /* read buttons textures */ + + /* bases: unpressed, pressed, disabled */ + READ_APPEARANCE("window.active.button.unpressed.bg", + a_focused_unpressed_tmp, TRUE); + READ_APPEARANCE("window.inactive.button.unpressed.bg", + a_unfocused_unpressed_tmp, TRUE); + READ_APPEARANCE("window.active.button.pressed.bg", + a_focused_pressed_tmp, TRUE); + READ_APPEARANCE("window.inactive.button.pressed.bg", + a_unfocused_pressed_tmp, TRUE); + READ_APPEARANCE("window.active.button.disabled.bg", + a_disabled_focused_tmp, TRUE); + READ_APPEARANCE("window.inactive.button.disabled.bg", + a_disabled_unfocused_tmp, TRUE); + + /* hover */ + READ_APPEARANCE_COPY("window.active.button.hover.bg", + a_hover_focused_tmp, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.hover.bg", + a_hover_unfocused_tmp, TRUE, + a_unfocused_unpressed_tmp); + + /* toggled unpressed */ + READ_APPEARANCE_("window.active.button.toggled.unpressed.bg", + "window.active.button.toggled.bg", + a_toggled_focused_unpressed_tmp, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_("window.inactive.button.toggled.unpressed.bg", + "window.inactive.button.toggled.bg", + a_toggled_unfocused_unpressed_tmp, TRUE, + a_unfocused_pressed_tmp); + + /* toggled pressed */ + READ_APPEARANCE_COPY("window.active.button.toggled.pressed.bg", + a_toggled_focused_pressed_tmp, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.toggled.pressed.bg", + a_toggled_unfocused_pressed_tmp, TRUE, + a_unfocused_pressed_tmp); + + /* toggled hover */ + READ_APPEARANCE_COPY("window.active.button.toggled.hover.bg", + a_toggled_hover_focused_tmp, TRUE, + a_toggled_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.toggled.hover.bg", + a_toggled_hover_unfocused_tmp, TRUE, + a_toggled_unfocused_unpressed_tmp); + + + /* now do individual buttons, if specified */ + + /* max button */ + read_button_colors(db, inst, theme, theme->btn_max, "max"); + + /* bases: unpressed, pressed, disabled */ + READ_APPEARANCE_COPY("window.active.button.max.unpressed.bg", + theme->btn_max->a_focused_unpressed, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.max.unpressed.bg", + theme->btn_max->a_unfocused_unpressed, TRUE, + a_unfocused_unpressed_tmp); + READ_APPEARANCE_COPY("window.active.button.max.pressed.bg", + theme->btn_max->a_focused_pressed, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.max.pressed.bg", + theme->btn_max->a_unfocused_pressed, TRUE, + a_unfocused_pressed_tmp); + READ_APPEARANCE_COPY("window.active.button.max.disabled.bg", + theme->btn_max->a_disabled_focused, TRUE, + a_disabled_focused_tmp); + READ_APPEARANCE_COPY("window.inactive.button.max.disabled.bg", + theme->btn_max->a_disabled_unfocused, TRUE, + a_disabled_unfocused_tmp); + + /* hover */ + READ_APPEARANCE_COPY("window.active.button.max.hover.bg", + theme->btn_max->a_hover_focused, TRUE, + theme->btn_max->a_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.max.hover.bg", + theme->btn_max->a_hover_unfocused, TRUE, + theme->btn_max->a_unfocused_unpressed); + + /* toggled unpressed */ + READ_APPEARANCE_("window.active.button.max.toggled.unpressed.bg", + "window.active.button.max.toggled.bg", + theme->btn_max->a_toggled_focused_unpressed, TRUE, + theme->btn_max->a_focused_pressed); + READ_APPEARANCE_("window.inactive.button.max.toggled.unpressed.bg", + "window.inactive.button.max.toggled.bg", + theme->btn_max->a_toggled_unfocused_unpressed, TRUE, + theme->btn_max->a_unfocused_pressed); + + /* toggled pressed */ + READ_APPEARANCE_COPY("window.active.button.max.toggled.pressed.bg", + theme->btn_max->a_toggled_focused_pressed, TRUE, + theme->btn_max->a_focused_pressed); + READ_APPEARANCE_COPY("window.inactive.button.max.toggled.pressed.bg", + theme->btn_max->a_toggled_unfocused_pressed, TRUE, + theme->btn_max->a_unfocused_pressed); + + /* toggled hover */ + READ_APPEARANCE_COPY("window.active.button.max.toggled.hover.bg", + theme->btn_max->a_toggled_hover_focused, TRUE, + theme->btn_max->a_toggled_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.max.toggled.hover.bg", + theme->btn_max->a_toggled_hover_unfocused, TRUE, + theme->btn_max->a_toggled_unfocused_unpressed); + + /* close button */ + read_button_colors(db, inst, theme, theme->btn_close, "close"); + + READ_APPEARANCE_COPY("window.active.button.close.unpressed.bg", + theme->btn_close->a_focused_unpressed, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.close.unpressed.bg", + theme->btn_close->a_unfocused_unpressed, TRUE, + a_unfocused_unpressed_tmp); + READ_APPEARANCE_COPY("window.active.button.close.pressed.bg", + theme->btn_close->a_focused_pressed, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.close.pressed.bg", + theme->btn_close->a_unfocused_pressed, TRUE, + a_unfocused_pressed_tmp); + READ_APPEARANCE_COPY("window.active.button.close.disabled.bg", + theme->btn_close->a_disabled_focused, TRUE, + a_disabled_focused_tmp); + READ_APPEARANCE_COPY("window.inactive.button.close.disabled.bg", + theme->btn_close->a_disabled_unfocused, TRUE, + a_disabled_unfocused_tmp); + READ_APPEARANCE_COPY("window.active.button.close.hover.bg", + theme->btn_close->a_hover_focused, TRUE, + theme->btn_close->a_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.close.hover.bg", + theme->btn_close->a_hover_unfocused, TRUE, + theme->btn_close->a_unfocused_unpressed); + + /* desk button */ + read_button_colors(db, inst, theme, theme->btn_desk, "desk"); + + /* bases: unpressed, pressed, disabled */ + READ_APPEARANCE_COPY("window.active.button.desk.unpressed.bg", + theme->btn_desk->a_focused_unpressed, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.desk.unpressed.bg", + theme->btn_desk->a_unfocused_unpressed, TRUE, + a_unfocused_unpressed_tmp); + READ_APPEARANCE_COPY("window.active.button.desk.pressed.bg", + theme->btn_desk->a_focused_pressed, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.desk.pressed.bg", + theme->btn_desk->a_unfocused_pressed, TRUE, + a_unfocused_pressed_tmp); + READ_APPEARANCE_COPY("window.active.button.desk.disabled.bg", + theme->btn_desk->a_disabled_focused, TRUE, + a_disabled_focused_tmp); + READ_APPEARANCE_COPY("window.inactive.button.desk.disabled.bg", + theme->btn_desk->a_disabled_unfocused, TRUE, + a_disabled_unfocused_tmp); + + /* hover */ + READ_APPEARANCE_COPY("window.active.button.desk.hover.bg", + theme->btn_desk->a_hover_focused, TRUE, + theme->btn_desk->a_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.desk.hover.bg", + theme->btn_desk->a_hover_unfocused, TRUE, + theme->btn_desk->a_unfocused_unpressed); + + /* toggled unpressed */ + READ_APPEARANCE_("window.active.button.desk.toggled.unpressed.bg", + "window.active.button.desk.toggled.bg", + theme->btn_desk->a_toggled_focused_unpressed, TRUE, + theme->btn_desk->a_focused_pressed); + READ_APPEARANCE_("window.inactive.button.desk.toggled.unpressed.bg", + "window.inactive.button.desk.toggled.bg", + theme->btn_desk->a_toggled_unfocused_unpressed, TRUE, + theme->btn_desk->a_unfocused_pressed); + + /* toggled pressed */ + READ_APPEARANCE_COPY("window.active.button.desk.toggled.pressed.bg", + theme->btn_desk->a_toggled_focused_pressed, TRUE, + theme->btn_desk->a_focused_pressed); + READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.pressed.bg", + theme->btn_desk->a_toggled_unfocused_pressed, TRUE, + theme->btn_desk->a_unfocused_pressed); + + /* toggled hover */ + READ_APPEARANCE_COPY("window.active.button.desk.toggled.hover.bg", + theme->btn_desk->a_toggled_hover_focused, TRUE, + theme->btn_desk->a_toggled_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.desk.toggled.hover.bg", + theme->btn_desk->a_toggled_hover_unfocused, TRUE, + theme->btn_desk->a_toggled_unfocused_unpressed); + + /* shade button */ + read_button_colors(db, inst, theme, theme->btn_shade, "shade"); + + /* bases: unpressed, pressed, disabled */ + READ_APPEARANCE_COPY("window.active.button.shade.unpressed.bg", + theme->btn_shade->a_focused_unpressed, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.shade.unpressed.bg", + theme->btn_shade->a_unfocused_unpressed, TRUE, + a_unfocused_unpressed_tmp); + READ_APPEARANCE_COPY("window.active.button.shade.pressed.bg", + theme->btn_shade->a_focused_pressed, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.shade.pressed.bg", + theme->btn_shade->a_unfocused_pressed, TRUE, + a_unfocused_pressed_tmp); + READ_APPEARANCE_COPY("window.active.button.shade.disabled.bg", + theme->btn_shade->a_disabled_focused, TRUE, + a_disabled_focused_tmp); + READ_APPEARANCE_COPY("window.inactive.button.shade.disabled.bg", + theme->btn_shade->a_disabled_unfocused, TRUE, + a_disabled_unfocused_tmp); + + /* hover */ + READ_APPEARANCE_COPY("window.active.button.shade.hover.bg", + theme->btn_shade->a_hover_focused, TRUE, + theme->btn_shade->a_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.shade.hover.bg", + theme->btn_shade->a_hover_unfocused, TRUE, + theme->btn_shade->a_unfocused_unpressed); + + /* toggled unpressed */ + READ_APPEARANCE_("window.active.button.shade.toggled.unpressed.bg", + "window.active.button.shade.toggled.bg", + theme->btn_shade->a_toggled_focused_unpressed, TRUE, + theme->btn_shade->a_focused_pressed); + READ_APPEARANCE_("window.inactive.button.shade.toggled.unpressed.bg", + "window.inactive.button.shade.toggled.bg", + theme->btn_shade->a_toggled_unfocused_unpressed, TRUE, + theme->btn_shade->a_unfocused_pressed); + + /* toggled pressed */ + READ_APPEARANCE_COPY("window.active.button.shade.toggled.pressed.bg", + theme->btn_shade->a_toggled_focused_pressed, TRUE, + theme->btn_shade->a_focused_pressed); + READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.pressed.bg", + theme->btn_shade->a_toggled_unfocused_pressed, TRUE, + theme->btn_shade->a_unfocused_pressed); + + /* toggled hover */ + READ_APPEARANCE_COPY("window.active.button.shade.toggled.hover.bg", + theme->btn_shade->a_toggled_hover_focused, TRUE, + theme->btn_shade->a_toggled_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.shade.toggled.hover.bg", + theme->btn_shade->a_toggled_hover_unfocused, TRUE, + theme->btn_shade->a_toggled_unfocused_unpressed); + + /* iconify button */ + read_button_colors(db, inst, theme, theme->btn_iconify, "iconify"); + + READ_APPEARANCE_COPY("window.active.button.iconify.unpressed.bg", + theme->btn_iconify->a_focused_unpressed, TRUE, + a_focused_unpressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.iconify.unpressed.bg", + theme->btn_iconify->a_unfocused_unpressed, TRUE, + a_unfocused_unpressed_tmp); + READ_APPEARANCE_COPY("window.active.button.iconify.pressed.bg", + theme->btn_iconify->a_focused_pressed, TRUE, + a_focused_pressed_tmp); + READ_APPEARANCE_COPY("window.inactive.button.iconify.pressed.bg", + theme->btn_iconify->a_unfocused_pressed, TRUE, + a_unfocused_pressed_tmp); + READ_APPEARANCE_COPY("window.active.button.iconify.disabled.bg", + theme->btn_iconify->a_disabled_focused, TRUE, + a_disabled_focused_tmp); + READ_APPEARANCE_COPY("window.inactive.button.iconify.disabled.bg", + theme->btn_iconify->a_disabled_unfocused, TRUE, + a_disabled_unfocused_tmp); + READ_APPEARANCE_COPY("window.active.button.iconify.hover.bg", + theme->btn_iconify->a_hover_focused, TRUE, + theme->btn_iconify->a_focused_unpressed); + READ_APPEARANCE_COPY("window.inactive.button.iconify.hover.bg", + theme->btn_iconify->a_hover_unfocused, TRUE, + theme->btn_iconify->a_unfocused_unpressed); + + /* osd buttons */ + READ_APPEARANCE_COPY("osd.button.unpressed.bg", theme->osd_unpressed_button, TRUE, a_focused_unpressed_tmp); + READ_APPEARANCE_COPY_TEXTURES("osd.button.pressed.bg", theme->osd_pressed_button, TRUE, a_focused_pressed_tmp, 5); + READ_APPEARANCE_COPY_TEXTURES("osd.button.focused.bg", theme->osd_focused_button, TRUE, a_focused_unpressed_tmp, 5); + + theme->a_icon->surface.grad = + theme->a_clear->surface.grad = + theme->a_clear_tex->surface.grad = + theme->a_menu_text_title->surface.grad = + theme->a_menu_normal->surface.grad = + theme->a_menu_disabled->surface.grad = + theme->a_menu_text_normal->surface.grad = + theme->a_menu_text_selected->surface.grad = + theme->a_menu_text_disabled->surface.grad = + theme->a_menu_text_disabled_selected->surface.grad = + theme->a_menu_bullet_normal->surface.grad = + theme->a_menu_bullet_selected->surface.grad = RR_SURFACE_PARENTREL; + + /* set up the textures */ + theme->a_focused_label->texture[0].type = RR_TEXTURE_TEXT; + theme->a_focused_label->texture[0].data.text.justify = winjust; + theme->a_focused_label->texture[0].data.text.font=theme->win_font_focused; + theme->a_focused_label->texture[0].data.text.color = + theme->title_focused_color; + + if (read_string(db, "window.active.label.text.font", &str)) { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->a_focused_label->texture[0].data.text.shadow_offset_x = i; + theme->a_focused_label->texture[0].data.text.shadow_offset_y = i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->title_focused_shadow_color = RrColorNew(inst, j, j, j); + theme->title_focused_shadow_alpha = i; + } else { + theme->title_focused_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->title_focused_shadow_alpha = 50; + } + } + + theme->a_focused_label->texture[0].data.text.shadow_color = + theme->title_focused_shadow_color; + theme->a_focused_label->texture[0].data.text.shadow_alpha = + theme->title_focused_shadow_alpha; + + theme->osd_hilite_label->texture[0].type = RR_TEXTURE_TEXT; + theme->osd_hilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT; + theme->osd_hilite_label->texture[0].data.text.font = + theme->osd_font_hilite; + theme->osd_hilite_label->texture[0].data.text.color = + theme->osd_text_active_color; + + if (read_string(db, "osd.active.label.text.font", &str) || + read_string(db, "osd.label.text.font", &str)) + { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->osd_hilite_label->texture[0].data.text.shadow_offset_x = i; + theme->osd_hilite_label->texture[0].data.text.shadow_offset_y = i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->osd_text_active_shadow_color = RrColorNew(inst, j, j, j); + theme->osd_text_active_shadow_alpha = i; + } else { + theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->osd_text_active_shadow_alpha = 50; + } + } else { + /* inherit the font settings from the focused label */ + theme->osd_hilite_label->texture[0].data.text.shadow_offset_x = + theme->a_focused_label->texture[0].data.text.shadow_offset_x; + theme->osd_hilite_label->texture[0].data.text.shadow_offset_y = + theme->a_focused_label->texture[0].data.text.shadow_offset_y; + if (theme->title_focused_shadow_color) + theme->osd_text_active_shadow_color = + RrColorCopy(theme->title_focused_shadow_color); + else + theme->osd_text_active_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->osd_text_active_shadow_alpha = + theme->title_focused_shadow_alpha; + } + + theme->osd_hilite_label->texture[0].data.text.shadow_color = + theme->osd_text_active_shadow_color; + theme->osd_hilite_label->texture[0].data.text.shadow_alpha = + theme->osd_text_active_shadow_alpha; + + theme->osd_unpressed_button->texture[0].type = + theme->osd_pressed_button->texture[0].type = + theme->osd_focused_button->texture[0].type = + RR_TEXTURE_TEXT; + + theme->osd_unpressed_button->texture[0].data.text.justify = + theme->osd_pressed_button->texture[0].data.text.justify = + theme->osd_focused_button->texture[0].data.text.justify = + RR_JUSTIFY_CENTER; + + theme->osd_unpressed_button->texture[0].data.text.font = + theme->osd_pressed_button->texture[0].data.text.font = + theme->osd_focused_button->texture[0].data.text.font = + theme->osd_font_hilite; + + theme->osd_unpressed_button->texture[0].data.text.color = + theme->osd_unpressed_color; + theme->osd_pressed_button->texture[0].data.text.color = + theme->osd_pressed_color; + theme->osd_focused_button->texture[0].data.text.color = + theme->osd_focused_color; + + theme->osd_pressed_button->texture[1].data.lineart.color = + theme->osd_pressed_button->texture[2].data.lineart.color = + theme->osd_pressed_button->texture[3].data.lineart.color = + theme->osd_pressed_button->texture[4].data.lineart.color = + theme->osd_pressed_lineart; + + theme->osd_focused_button->texture[1].data.lineart.color = + theme->osd_focused_button->texture[2].data.lineart.color = + theme->osd_focused_button->texture[3].data.lineart.color = + theme->osd_focused_button->texture[4].data.lineart.color = + theme->osd_focused_lineart; + + theme->a_unfocused_label->texture[0].type = RR_TEXTURE_TEXT; + theme->a_unfocused_label->texture[0].data.text.justify = winjust; + theme->a_unfocused_label->texture[0].data.text.font = + theme->win_font_unfocused; + theme->a_unfocused_label->texture[0].data.text.color = + theme->title_unfocused_color; + + if (read_string(db, "window.inactive.label.text.font", &str)) { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->a_unfocused_label->texture[0].data.text.shadow_offset_x = i; + theme->a_unfocused_label->texture[0].data.text.shadow_offset_y = i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->title_unfocused_shadow_color = RrColorNew(inst, j, j, j); + theme->title_unfocused_shadow_alpha = i; + } else { + theme->title_unfocused_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->title_unfocused_shadow_alpha = 50; + } + } + + theme->a_unfocused_label->texture[0].data.text.shadow_color = + theme->title_unfocused_shadow_color; + theme->a_unfocused_label->texture[0].data.text.shadow_alpha = + theme->title_unfocused_shadow_alpha; + + theme->osd_unhilite_label->texture[0].type = RR_TEXTURE_TEXT; + theme->osd_unhilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT; + theme->osd_unhilite_label->texture[0].data.text.font = + theme->osd_font_unhilite; + theme->osd_unhilite_label->texture[0].data.text.color = + theme->osd_text_inactive_color; + + if (read_string(db, "osd.inactive.label.text.font", &str)) + { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x=i; + theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y=i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->osd_text_inactive_shadow_color = RrColorNew(inst, j, j, j); + theme->osd_text_inactive_shadow_alpha = i; + } else { + theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->osd_text_inactive_shadow_alpha = 50; + } + } else { + /* inherit the font settings from the unfocused label */ + theme->osd_unhilite_label->texture[0].data.text.shadow_offset_x = + theme->a_unfocused_label->texture[0].data.text.shadow_offset_x; + theme->osd_unhilite_label->texture[0].data.text.shadow_offset_y = + theme->a_unfocused_label->texture[0].data.text.shadow_offset_y; + if (theme->title_unfocused_shadow_color) + theme->osd_text_inactive_shadow_color = + RrColorCopy(theme->title_unfocused_shadow_color); + else + theme->osd_text_inactive_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->osd_text_inactive_shadow_alpha = + theme->title_unfocused_shadow_alpha; + } + + theme->osd_unhilite_label->texture[0].data.text.shadow_color = + theme->osd_text_inactive_shadow_color; + theme->osd_unhilite_label->texture[0].data.text.shadow_alpha = + theme->osd_text_inactive_shadow_alpha; + + theme->a_menu_text_title->texture[0].type = RR_TEXTURE_TEXT; + theme->a_menu_text_title->texture[0].data.text.justify = mtitlejust; + theme->a_menu_text_title->texture[0].data.text.font = + theme->menu_title_font; + theme->a_menu_text_title->texture[0].data.text.color = + theme->menu_title_color; + + if (read_string(db, "menu.title.text.font", &str)) { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->a_menu_text_title->texture[0].data.text.shadow_offset_x = i; + theme->a_menu_text_title->texture[0].data.text.shadow_offset_y = i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->menu_title_shadow_color = RrColorNew(inst, j, j, j); + theme->menu_title_shadow_alpha = i; + } else { + theme->menu_title_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->menu_title_shadow_alpha = 50; + } + } + + theme->a_menu_text_title->texture[0].data.text.shadow_color = + theme->menu_title_shadow_color; + theme->a_menu_text_title->texture[0].data.text.shadow_alpha = + theme->menu_title_shadow_alpha; + + theme->a_menu_text_normal->texture[0].type = + theme->a_menu_text_selected->texture[0].type = + theme->a_menu_text_disabled->texture[0].type = + theme->a_menu_text_disabled_selected->texture[0].type = + RR_TEXTURE_TEXT; + theme->a_menu_text_normal->texture[0].data.text.justify = + theme->a_menu_text_selected->texture[0].data.text.justify = + theme->a_menu_text_disabled->texture[0].data.text.justify = + theme->a_menu_text_disabled_selected->texture[0].data.text.justify = + RR_JUSTIFY_LEFT; + theme->a_menu_text_normal->texture[0].data.text.font = + theme->a_menu_text_selected->texture[0].data.text.font = + theme->a_menu_text_disabled->texture[0].data.text.font = + theme->a_menu_text_disabled_selected->texture[0].data.text.font = + theme->menu_font; + theme->a_menu_text_normal->texture[0].data.text.color = theme->menu_color; + theme->a_menu_text_selected->texture[0].data.text.color = + theme->menu_selected_color; + theme->a_menu_text_disabled->texture[0].data.text.color = + theme->menu_disabled_color; + theme->a_menu_text_disabled_selected->texture[0].data.text.color = + theme->menu_disabled_selected_color; + + if (read_string(db, "menu.items.font", &str)) { + char *p; + gint i = 0; + gint j; + if (strstr(str, "shadow=y")) { + if ((p = strstr(str, "shadowoffset="))) + i = parse_inline_number(p + strlen("shadowoffset=")); + else + i = 1; + theme->a_menu_text_normal-> + texture[0].data.text.shadow_offset_x = i; + theme->a_menu_text_normal-> + texture[0].data.text.shadow_offset_y = i; + theme->a_menu_text_selected-> + texture[0].data.text.shadow_offset_x = i; + theme->a_menu_text_selected-> + texture[0].data.text.shadow_offset_y = i; + theme->a_menu_text_disabled-> + texture[0].data.text.shadow_offset_x = i; + theme->a_menu_text_disabled-> + texture[0].data.text.shadow_offset_y = i; + theme->a_menu_text_disabled_selected-> + texture[0].data.text.shadow_offset_x = i; + theme->a_menu_text_disabled_selected-> + texture[0].data.text.shadow_offset_y = i; + } + if ((p = strstr(str, "shadowtint="))) + { + i = parse_inline_number(p + strlen("shadowtint=")); + j = (i > 0 ? 0 : 255); + i = ABS(i*255/100); + + theme->menu_text_normal_shadow_color = RrColorNew(inst, j, j, j); + theme->menu_text_selected_shadow_color = RrColorNew(inst, j, j, j); + theme->menu_text_disabled_shadow_color = RrColorNew(inst, j, j, j); + theme->menu_text_normal_shadow_alpha = i; + theme->menu_text_selected_shadow_alpha = i; + theme->menu_text_disabled_shadow_alpha = i; + theme->menu_text_disabled_selected_shadow_alpha = i; + } else { + theme->menu_text_normal_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->menu_text_selected_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->menu_text_disabled_shadow_color = RrColorNew(inst, 0, 0, 0); + theme->menu_text_normal_shadow_alpha = 50; + theme->menu_text_selected_shadow_alpha = 50; + theme->menu_text_disabled_selected_shadow_alpha = 50; + } + } + + theme->a_menu_text_normal->texture[0].data.text.shadow_color = + theme->menu_text_normal_shadow_color; + theme->a_menu_text_normal->texture[0].data.text.shadow_alpha = + theme->menu_text_normal_shadow_alpha; + theme->a_menu_text_selected->texture[0].data.text.shadow_color = + theme->menu_text_selected_shadow_color; + theme->a_menu_text_selected->texture[0].data.text.shadow_alpha = + theme->menu_text_selected_shadow_alpha; + theme->a_menu_text_disabled->texture[0].data.text.shadow_color = + theme->menu_text_disabled_shadow_color; + theme->a_menu_text_disabled->texture[0].data.text.shadow_alpha = + theme->menu_text_disabled_shadow_alpha; + theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_color = + theme->menu_text_disabled_shadow_color; + theme->a_menu_text_disabled_selected->texture[0].data.text.shadow_alpha = + theme->menu_text_disabled_shadow_alpha; + + theme->btn_max->a_disabled_focused->texture[0].type = + theme->btn_max->a_disabled_unfocused->texture[0].type = + theme->btn_max->a_hover_focused->texture[0].type = + theme->btn_max->a_hover_unfocused->texture[0].type = + theme->btn_max->a_toggled_hover_focused->texture[0].type = + theme->btn_max->a_toggled_hover_unfocused->texture[0].type = + theme->btn_max->a_toggled_focused_unpressed->texture[0].type = + theme->btn_max->a_toggled_unfocused_unpressed->texture[0].type = + theme->btn_max->a_toggled_focused_pressed->texture[0].type = + theme->btn_max->a_toggled_unfocused_pressed->texture[0].type = + theme->btn_max->a_focused_unpressed->texture[0].type = + theme->btn_max->a_focused_pressed->texture[0].type = + theme->btn_max->a_unfocused_unpressed->texture[0].type = + theme->btn_max->a_unfocused_pressed->texture[0].type = + theme->btn_close->a_disabled_focused->texture[0].type = + theme->btn_close->a_disabled_unfocused->texture[0].type = + theme->btn_close->a_hover_focused->texture[0].type = + theme->btn_close->a_hover_unfocused->texture[0].type = + theme->btn_close->a_focused_unpressed->texture[0].type = + theme->btn_close->a_focused_pressed->texture[0].type = + theme->btn_close->a_unfocused_unpressed->texture[0].type = + theme->btn_close->a_unfocused_pressed->texture[0].type = + theme->btn_desk->a_disabled_focused->texture[0].type = + theme->btn_desk->a_disabled_unfocused->texture[0].type = + theme->btn_desk->a_hover_focused->texture[0].type = + theme->btn_desk->a_hover_unfocused->texture[0].type = + theme->btn_desk->a_toggled_hover_focused->texture[0].type = + theme->btn_desk->a_toggled_hover_unfocused->texture[0].type = + theme->btn_desk->a_toggled_focused_unpressed->texture[0].type = + theme->btn_desk->a_toggled_unfocused_unpressed->texture[0].type = + theme->btn_desk->a_toggled_focused_pressed->texture[0].type = + theme->btn_desk->a_toggled_unfocused_pressed->texture[0].type = + theme->btn_desk->a_focused_unpressed->texture[0].type = + theme->btn_desk->a_focused_pressed->texture[0].type = + theme->btn_desk->a_unfocused_unpressed->texture[0].type = + theme->btn_desk->a_unfocused_pressed->texture[0].type = + theme->btn_shade->a_disabled_focused->texture[0].type = + theme->btn_shade->a_disabled_unfocused->texture[0].type = + theme->btn_shade->a_hover_focused->texture[0].type = + theme->btn_shade->a_hover_unfocused->texture[0].type = + theme->btn_shade->a_toggled_hover_focused->texture[0].type = + theme->btn_shade->a_toggled_hover_unfocused->texture[0].type = + theme->btn_shade->a_toggled_focused_unpressed->texture[0].type = + theme->btn_shade->a_toggled_unfocused_unpressed->texture[0].type = + theme->btn_shade->a_toggled_focused_pressed->texture[0].type = + theme->btn_shade->a_toggled_unfocused_pressed->texture[0].type = + theme->btn_shade->a_focused_unpressed->texture[0].type = + theme->btn_shade->a_focused_pressed->texture[0].type = + theme->btn_shade->a_unfocused_unpressed->texture[0].type = + theme->btn_shade->a_unfocused_pressed->texture[0].type = + theme->btn_iconify->a_disabled_focused->texture[0].type = + theme->btn_iconify->a_disabled_unfocused->texture[0].type = + theme->btn_iconify->a_hover_focused->texture[0].type = + theme->btn_iconify->a_hover_unfocused->texture[0].type = + theme->btn_iconify->a_focused_unpressed->texture[0].type = + theme->btn_iconify->a_focused_pressed->texture[0].type = + theme->btn_iconify->a_unfocused_unpressed->texture[0].type = + theme->btn_iconify->a_unfocused_pressed->texture[0].type = + theme->a_menu_bullet_normal->texture[0].type = + theme->a_menu_bullet_selected->texture[0].type = RR_TEXTURE_MASK; + + theme->btn_max->a_disabled_focused->texture[0].data.mask.mask = + theme->btn_max->a_disabled_unfocused->texture[0].data.mask.mask = + theme->btn_max->disabled_mask; + theme->btn_max->a_hover_focused->texture[0].data.mask.mask = + theme->btn_max->a_hover_unfocused->texture[0].data.mask.mask = + theme->btn_max->hover_mask; + theme->btn_max->a_focused_pressed->texture[0].data.mask.mask = + theme->btn_max->a_unfocused_pressed->texture[0].data.mask.mask = + theme->btn_max->pressed_mask; + theme->btn_max->a_focused_unpressed->texture[0].data.mask.mask = + theme->btn_max->a_unfocused_unpressed->texture[0].data.mask.mask = + theme->btn_max->mask; + theme->btn_max->a_toggled_hover_focused->texture[0].data.mask.mask = + theme->btn_max->a_toggled_hover_unfocused->texture[0].data.mask.mask = + theme->btn_max->toggled_hover_mask; + theme->btn_max->a_toggled_focused_unpressed->texture[0].data.mask.mask = + theme->btn_max->a_toggled_unfocused_unpressed-> + texture[0].data.mask.mask = theme->btn_max->toggled_mask; + theme->btn_max->a_toggled_focused_pressed->texture[0].data.mask.mask = + theme->btn_max->a_toggled_unfocused_pressed->texture[0].data.mask.mask + = theme->btn_max->toggled_pressed_mask; + theme->btn_close->a_disabled_focused->texture[0].data.mask.mask = + theme->btn_close->a_disabled_unfocused->texture[0].data.mask.mask = + theme->btn_close->disabled_mask; + theme->btn_close->a_hover_focused->texture[0].data.mask.mask = + theme->btn_close->a_hover_unfocused->texture[0].data.mask.mask = + theme->btn_close->hover_mask; + theme->btn_close->a_focused_pressed->texture[0].data.mask.mask = + theme->btn_close->a_unfocused_pressed->texture[0].data.mask.mask = + theme->btn_close->pressed_mask; + theme->btn_close->a_focused_unpressed->texture[0].data.mask.mask = + theme->btn_close->a_unfocused_unpressed->texture[0].data.mask.mask = + theme->btn_close->mask; + theme->btn_desk->a_disabled_focused->texture[0].data.mask.mask = + theme->btn_desk->a_disabled_unfocused->texture[0].data.mask.mask = + theme->btn_desk->disabled_mask; + theme->btn_desk->a_hover_focused->texture[0].data.mask.mask = + theme->btn_desk->a_hover_unfocused->texture[0].data.mask.mask = + theme->btn_desk->hover_mask; + theme->btn_desk->a_focused_pressed->texture[0].data.mask.mask = + theme->btn_desk->a_unfocused_pressed->texture[0].data.mask.mask = + theme->btn_desk->pressed_mask; + theme->btn_desk->a_focused_unpressed->texture[0].data.mask.mask = + theme->btn_desk->a_unfocused_unpressed->texture[0].data.mask.mask = + theme->btn_desk->mask; + theme->btn_desk->a_toggled_hover_focused->texture[0].data.mask.mask = + theme->btn_desk->a_toggled_hover_unfocused->texture[0].data.mask.mask = + theme->btn_desk->toggled_hover_mask; + theme->btn_desk->a_toggled_focused_unpressed->texture[0].data.mask.mask = + theme->btn_desk->a_toggled_unfocused_unpressed-> + texture[0].data.mask.mask = theme->btn_desk->toggled_mask; + theme->btn_desk->a_toggled_focused_pressed->texture[0].data.mask.mask = + theme->btn_desk->a_toggled_unfocused_pressed->texture[0].data.mask.mask + = theme->btn_desk->toggled_pressed_mask; + theme->btn_shade->a_disabled_focused->texture[0].data.mask.mask = + theme->btn_shade->a_disabled_unfocused->texture[0].data.mask.mask = + theme->btn_shade->disabled_mask; + theme->btn_shade->a_hover_focused->texture[0].data.mask.mask = + theme->btn_shade->a_hover_unfocused->texture[0].data.mask.mask = + theme->btn_shade->hover_mask; + theme->btn_shade->a_focused_pressed->texture[0].data.mask.mask = + theme->btn_shade->a_unfocused_pressed->texture[0].data.mask.mask = + theme->btn_shade->pressed_mask; + theme->btn_shade->a_focused_unpressed->texture[0].data.mask.mask = + theme->btn_shade->a_unfocused_unpressed->texture[0].data.mask.mask = + theme->btn_shade->mask; + theme->btn_shade->a_toggled_hover_focused->texture[0].data.mask.mask = + theme->btn_shade->a_toggled_hover_unfocused->texture[0].data.mask.mask + = theme->btn_shade->toggled_hover_mask; + theme->btn_shade->a_toggled_focused_unpressed->texture[0].data.mask.mask = + theme->btn_shade->a_toggled_unfocused_unpressed-> + texture[0].data.mask.mask = theme->btn_shade->toggled_mask; + theme->btn_shade->a_toggled_focused_pressed->texture[0].data.mask.mask = + theme->btn_shade->a_toggled_unfocused_pressed-> + texture[0].data.mask.mask = theme->btn_shade->toggled_pressed_mask; + theme->btn_iconify->a_disabled_focused->texture[0].data.mask.mask = + theme->btn_iconify->a_disabled_unfocused->texture[0].data.mask.mask = + theme->btn_iconify->disabled_mask; + theme->btn_iconify->a_hover_focused->texture[0].data.mask.mask = + theme->btn_iconify->a_hover_unfocused->texture[0].data.mask.mask = + theme->btn_iconify->hover_mask; + theme->btn_iconify->a_focused_pressed->texture[0].data.mask.mask = + theme->btn_iconify->a_unfocused_pressed->texture[0].data.mask.mask = + theme->btn_iconify->pressed_mask; + theme->btn_iconify->a_focused_unpressed->texture[0].data.mask.mask = + theme->btn_iconify->a_unfocused_unpressed->texture[0].data.mask.mask = + theme->btn_iconify->mask; + theme->a_menu_bullet_normal->texture[0].data.mask.mask = + theme->a_menu_bullet_selected->texture[0].data.mask.mask = + theme->menu_bullet_mask; + theme->btn_max->a_disabled_focused->texture[0].data.mask.color = + theme->btn_max->disabled_focused_color; + theme->btn_close->a_disabled_focused->texture[0].data.mask.color = + theme->btn_close->disabled_focused_color; + theme->btn_desk->a_disabled_focused->texture[0].data.mask.color = + theme->btn_desk->disabled_focused_color; + theme->btn_shade->a_disabled_focused->texture[0].data.mask.color = + theme->btn_shade->disabled_focused_color; + theme->btn_iconify->a_disabled_focused->texture[0].data.mask.color = + theme->btn_iconify->disabled_focused_color; + theme->btn_max->a_disabled_unfocused->texture[0].data.mask.color = + theme->btn_max->disabled_unfocused_color; + theme->btn_close->a_disabled_unfocused->texture[0].data.mask.color = + theme->btn_close->disabled_unfocused_color; + theme->btn_desk->a_disabled_unfocused->texture[0].data.mask.color = + theme->btn_desk->disabled_unfocused_color; + theme->btn_shade->a_disabled_unfocused->texture[0].data.mask.color = + theme->btn_shade->disabled_unfocused_color; + theme->btn_iconify->a_disabled_unfocused->texture[0].data.mask.color = + theme->btn_iconify->disabled_unfocused_color; + theme->btn_max->a_hover_focused->texture[0].data.mask.color = + theme->btn_max->hover_focused_color; + theme->btn_close->a_hover_focused->texture[0].data.mask.color = + theme->btn_close->hover_focused_color; + theme->btn_desk->a_hover_focused->texture[0].data.mask.color = + theme->btn_desk->hover_focused_color; + theme->btn_shade->a_hover_focused->texture[0].data.mask.color = + theme->btn_shade->hover_focused_color; + theme->btn_iconify->a_hover_focused->texture[0].data.mask.color = + theme->btn_iconify->hover_focused_color; + theme->btn_max->a_hover_unfocused->texture[0].data.mask.color = + theme->btn_max->hover_unfocused_color; + theme->btn_close->a_hover_unfocused->texture[0].data.mask.color = + theme->btn_close->hover_unfocused_color; + theme->btn_desk->a_hover_unfocused->texture[0].data.mask.color = + theme->btn_desk->hover_unfocused_color; + theme->btn_shade->a_hover_unfocused->texture[0].data.mask.color = + theme->btn_shade->hover_unfocused_color; + theme->btn_iconify->a_hover_unfocused->texture[0].data.mask.color = + theme->btn_iconify->hover_unfocused_color; + theme->btn_max->a_toggled_hover_focused->texture[0].data.mask.color = + theme->btn_max->toggled_hover_focused_color; + theme->btn_desk->a_toggled_hover_focused->texture[0].data.mask.color = + theme->btn_desk->toggled_hover_focused_color; + theme->btn_shade->a_toggled_hover_focused->texture[0].data.mask.color = + theme->btn_shade->toggled_hover_focused_color; + theme->btn_max->a_toggled_hover_unfocused->texture[0].data.mask.color = + theme->btn_max->toggled_hover_unfocused_color; + theme->btn_desk->a_toggled_hover_unfocused->texture[0].data.mask.color = + theme->btn_desk->toggled_hover_unfocused_color; + theme->btn_shade->a_toggled_hover_unfocused->texture[0].data.mask.color = + theme->btn_shade->toggled_hover_unfocused_color; + theme->btn_max->a_toggled_focused_unpressed->texture[0].data.mask.color = + theme->btn_max->toggled_focused_unpressed_color; + theme->btn_desk->a_toggled_focused_unpressed->texture[0].data.mask.color = + theme->btn_desk->toggled_focused_unpressed_color; + theme->btn_shade->a_toggled_focused_unpressed->texture[0].data.mask.color = + theme->btn_shade->toggled_focused_unpressed_color; + theme->btn_max->a_toggled_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_max->toggled_unfocused_unpressed_color; + theme->btn_desk->a_toggled_unfocused_unpressed->texture[0].data.mask.color + = theme->btn_desk->toggled_unfocused_unpressed_color; + theme->btn_shade->a_toggled_unfocused_unpressed->texture[0].data.mask.color + = theme->btn_shade->toggled_unfocused_unpressed_color; + theme->btn_max->a_toggled_focused_pressed->texture[0].data.mask.color = + theme->btn_max->toggled_focused_pressed_color; + theme->btn_desk->a_toggled_focused_pressed->texture[0].data.mask.color = + theme->btn_desk->toggled_focused_pressed_color; + theme->btn_shade->a_toggled_focused_pressed->texture[0].data.mask.color = + theme->btn_shade->toggled_focused_pressed_color; + theme->btn_max->a_toggled_unfocused_pressed->texture[0].data.mask.color = + theme->btn_max->toggled_unfocused_pressed_color; + theme->btn_desk->a_toggled_unfocused_pressed->texture[0].data.mask.color = + theme->btn_desk->toggled_unfocused_pressed_color; + theme->btn_shade->a_toggled_unfocused_pressed->texture[0].data.mask.color = + theme->btn_shade->toggled_unfocused_pressed_color; + theme->btn_max->a_focused_unpressed->texture[0].data.mask.color = + theme->btn_max->focused_unpressed_color; + theme->btn_close->a_focused_unpressed->texture[0].data.mask.color = + theme->btn_close->focused_unpressed_color; + theme->btn_desk->a_focused_unpressed->texture[0].data.mask.color = + theme->btn_desk->focused_unpressed_color; + theme->btn_shade->a_focused_unpressed->texture[0].data.mask.color = + theme->btn_shade->focused_unpressed_color; + theme->btn_iconify->a_focused_unpressed->texture[0].data.mask.color = + theme->btn_iconify->focused_unpressed_color; + theme->btn_max->a_focused_pressed->texture[0].data.mask.color = + theme->btn_max->focused_pressed_color; + theme->btn_close->a_focused_pressed->texture[0].data.mask.color = + theme->btn_close->focused_pressed_color; + theme->btn_desk->a_focused_pressed->texture[0].data.mask.color = + theme->btn_desk->focused_pressed_color; + theme->btn_shade->a_focused_pressed->texture[0].data.mask.color = + theme->btn_shade->focused_pressed_color; + theme->btn_iconify->a_focused_pressed->texture[0].data.mask.color = + theme->btn_iconify->focused_pressed_color; + theme->btn_max->a_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_max->unfocused_unpressed_color; + theme->btn_close->a_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_close->unfocused_unpressed_color; + theme->btn_desk->a_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_desk->unfocused_unpressed_color; + theme->btn_shade->a_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_shade->unfocused_unpressed_color; + theme->btn_iconify->a_unfocused_unpressed->texture[0].data.mask.color = + theme->btn_iconify->unfocused_unpressed_color; + theme->btn_max->a_unfocused_pressed->texture[0].data.mask.color = + theme->btn_max->unfocused_pressed_color; + theme->btn_close->a_unfocused_pressed->texture[0].data.mask.color = + theme->btn_close->unfocused_pressed_color; + theme->btn_desk->a_unfocused_pressed->texture[0].data.mask.color = + theme->btn_desk->unfocused_pressed_color; + theme->btn_shade->a_unfocused_pressed->texture[0].data.mask.color = + theme->btn_shade->unfocused_pressed_color; + theme->btn_iconify->a_unfocused_pressed->texture[0].data.mask.color = + theme->btn_iconify->unfocused_pressed_color; + theme->a_menu_bullet_normal->texture[0].data.mask.color = + theme->menu_bullet_color; + theme->a_menu_bullet_selected->texture[0].data.mask.color = + theme->menu_bullet_selected_color; + + g_free(path); + XrmDestroyDatabase(db); + + /* set the font heights */ + theme->win_font_height = RrFontHeight + (theme->win_font_focused, + theme->a_focused_label->texture[0].data.text.shadow_offset_y); + theme->win_font_height = + MAX(theme->win_font_height, + RrFontHeight + (theme->win_font_focused, + theme->a_unfocused_label->texture[0].data.text.shadow_offset_y)); + theme->menu_title_font_height = RrFontHeight + (theme->menu_title_font, + theme->a_menu_text_title->texture[0].data.text.shadow_offset_y); + theme->menu_font_height = RrFontHeight + (theme->menu_font, + theme->a_menu_text_normal->texture[0].data.text.shadow_offset_y); + + /* calculate some last extents */ + { + gint ft, fb, fl, fr, ut, ub, ul, ur; + + RrMargins(theme->a_focused_label, &fl, &ft, &fr, &fb); + RrMargins(theme->a_unfocused_label, &ul, &ut, &ur, &ub); + theme->label_height = theme->win_font_height + MAX(ft + fb, ut + ub); + theme->label_height += theme->label_height % 2; + + /* this would be nice I think, since padding.width can now be 0, + but it breaks frame.c horribly and I don't feel like fixing that + right now, so if anyone complains, here is how to keep text from + going over the title's bevel/border with a padding.width of 0 and a + bevelless/borderless label + RrMargins(theme->a_focused_title, &fl, &ft, &fr, &fb); + RrMargins(theme->a_unfocused_title, &ul, &ut, &ur, &ub); + theme->title_height = theme->label_height + + MAX(MAX(theme->padding * 2, ft + fb), + MAX(theme->padding * 2, ut + ub)); + */ + theme->title_height = theme->label_height + theme->paddingy * 2; + + RrMargins(theme->a_menu_title, &ul, &ut, &ur, &ub); + theme->menu_title_label_height = theme->menu_title_font_height+ut+ub; + theme->menu_title_height = theme->menu_title_label_height + + theme->paddingy * 2; + } + theme->button_size = theme->label_height - 2; + theme->grip_width = 25; + + RrAppearanceFree(a_disabled_focused_tmp); + RrAppearanceFree(a_disabled_unfocused_tmp); + RrAppearanceFree(a_hover_focused_tmp); + RrAppearanceFree(a_hover_unfocused_tmp); + RrAppearanceFree(a_focused_unpressed_tmp); + RrAppearanceFree(a_focused_pressed_tmp); + RrAppearanceFree(a_unfocused_unpressed_tmp); + RrAppearanceFree(a_unfocused_pressed_tmp); + RrAppearanceFree(a_toggled_hover_focused_tmp); + RrAppearanceFree(a_toggled_hover_unfocused_tmp); + RrAppearanceFree(a_toggled_focused_unpressed_tmp); + RrAppearanceFree(a_toggled_focused_pressed_tmp); + RrAppearanceFree(a_toggled_unfocused_unpressed_tmp); + RrAppearanceFree(a_toggled_unfocused_pressed_tmp); + + return theme; +} + +void RrThemeFree(RrTheme *theme) +{ + if (theme) { + g_free(theme->name); + + RrButtonFree(theme->btn_max); + RrButtonFree(theme->btn_close); + RrButtonFree(theme->btn_desk); + RrButtonFree(theme->btn_shade); + RrButtonFree(theme->btn_iconify); + + RrColorFree(theme->menu_border_color); + RrColorFree(theme->osd_border_color); + RrColorFree(theme->frame_focused_border_color); + RrColorFree(theme->frame_undecorated_focused_border_color); + RrColorFree(theme->frame_unfocused_border_color); + RrColorFree(theme->frame_undecorated_unfocused_border_color); + RrColorFree(theme->title_separator_focused_color); + RrColorFree(theme->title_separator_unfocused_color); + RrColorFree(theme->cb_unfocused_color); + RrColorFree(theme->cb_focused_color); + RrColorFree(theme->title_focused_color); + RrColorFree(theme->title_unfocused_color); + RrColorFree(theme->titlebut_disabled_focused_color); + RrColorFree(theme->titlebut_disabled_unfocused_color); + RrColorFree(theme->titlebut_hover_focused_color); + RrColorFree(theme->titlebut_hover_unfocused_color); + RrColorFree(theme->titlebut_toggled_hover_focused_color); + RrColorFree(theme->titlebut_toggled_hover_unfocused_color); + RrColorFree(theme->titlebut_toggled_focused_pressed_color); + RrColorFree(theme->titlebut_toggled_unfocused_pressed_color); + RrColorFree(theme->titlebut_toggled_focused_unpressed_color); + RrColorFree(theme->titlebut_toggled_unfocused_unpressed_color); + RrColorFree(theme->titlebut_focused_pressed_color); + RrColorFree(theme->titlebut_unfocused_pressed_color); + RrColorFree(theme->titlebut_focused_unpressed_color); + RrColorFree(theme->titlebut_unfocused_unpressed_color); + RrColorFree(theme->menu_title_color); + RrColorFree(theme->menu_sep_color); + RrColorFree(theme->menu_color); + RrColorFree(theme->menu_bullet_color); + RrColorFree(theme->menu_bullet_selected_color); + RrColorFree(theme->menu_selected_color); + RrColorFree(theme->menu_disabled_color); + RrColorFree(theme->menu_disabled_selected_color); + RrColorFree(theme->title_focused_shadow_color); + RrColorFree(theme->title_unfocused_shadow_color); + RrColorFree(theme->osd_text_active_color); + RrColorFree(theme->osd_text_inactive_color); + RrColorFree(theme->osd_text_active_shadow_color); + RrColorFree(theme->osd_text_inactive_shadow_color); + RrColorFree(theme->osd_pressed_color); + RrColorFree(theme->osd_unpressed_color); + RrColorFree(theme->osd_focused_color); + RrColorFree(theme->osd_pressed_lineart); + RrColorFree(theme->osd_focused_lineart); + RrColorFree(theme->menu_title_shadow_color); + RrColorFree(theme->menu_text_normal_shadow_color); + RrColorFree(theme->menu_text_selected_shadow_color); + RrColorFree(theme->menu_text_disabled_shadow_color); + RrColorFree(theme->menu_text_disabled_selected_shadow_color); + + g_free(theme->def_win_icon); + + RrPixmapMaskFree(theme->menu_bullet_mask); + RrPixmapMaskFree(theme->down_arrow_mask); + RrPixmapMaskFree(theme->up_arrow_mask); + + RrFontClose(theme->win_font_focused); + RrFontClose(theme->win_font_unfocused); + RrFontClose(theme->menu_title_font); + RrFontClose(theme->menu_font); + RrFontClose(theme->osd_font_hilite); + RrFontClose(theme->osd_font_unhilite); + + RrAppearanceFree(theme->a_focused_grip); + RrAppearanceFree(theme->a_unfocused_grip); + RrAppearanceFree(theme->a_focused_title); + RrAppearanceFree(theme->a_unfocused_title); + RrAppearanceFree(theme->a_focused_label); + RrAppearanceFree(theme->a_unfocused_label); + RrAppearanceFree(theme->a_icon); + RrAppearanceFree(theme->a_focused_handle); + RrAppearanceFree(theme->a_unfocused_handle); + RrAppearanceFree(theme->a_menu); + RrAppearanceFree(theme->a_menu_title); + RrAppearanceFree(theme->a_menu_text_title); + RrAppearanceFree(theme->a_menu_normal); + RrAppearanceFree(theme->a_menu_selected); + RrAppearanceFree(theme->a_menu_disabled); + RrAppearanceFree(theme->a_menu_disabled_selected); + RrAppearanceFree(theme->a_menu_text_normal); + RrAppearanceFree(theme->a_menu_text_selected); + RrAppearanceFree(theme->a_menu_text_disabled); + RrAppearanceFree(theme->a_menu_text_disabled_selected); + RrAppearanceFree(theme->a_menu_bullet_normal); + RrAppearanceFree(theme->a_menu_bullet_selected); + RrAppearanceFree(theme->a_clear); + RrAppearanceFree(theme->a_clear_tex); + RrAppearanceFree(theme->osd_bg); + RrAppearanceFree(theme->osd_hilite_bg); + RrAppearanceFree(theme->osd_hilite_label); + RrAppearanceFree(theme->osd_unhilite_bg); + RrAppearanceFree(theme->osd_unhilite_label); + RrAppearanceFree(theme->osd_pressed_button); + RrAppearanceFree(theme->osd_unpressed_button); + RrAppearanceFree(theme->osd_focused_button); + + g_slice_free(RrTheme, theme); + } +} + +static XrmDatabase loaddb(const gchar *name, gchar **path) +{ + GSList *it; + XrmDatabase db = NULL; + gchar *s; + + if (name[0] == '/') { + s = g_build_filename(name, "openbox-3", "themerc", NULL); + if ((db = XrmGetFileDatabase(s))) + *path = g_path_get_dirname(s); + g_free(s); + } else { + ObtPaths *p; + + p = obt_paths_new(); + + /* XXX backwards compatibility, remove me sometime later */ + s = g_build_filename(g_get_home_dir(), ".themes", name, + "openbox-3", "themerc", NULL); + if ((db = XrmGetFileDatabase(s))) + *path = g_path_get_dirname(s); + g_free(s); + + for (it = obt_paths_data_dirs(p); !db && it; it = g_slist_next(it)) + { + s = g_build_filename(it->data, "themes", name, + "openbox-3", "themerc", NULL); + if ((db = XrmGetFileDatabase(s))) + *path = g_path_get_dirname(s); + g_free(s); + } + + obt_paths_unref(p); + } + + if (db == NULL) { + s = g_build_filename(name, "themerc", NULL); + if ((db = XrmGetFileDatabase(s))) + *path = g_path_get_dirname(s); + g_free(s); + } + + return db; +} + +static gchar *create_class_name(const gchar *rname) +{ + gchar *rclass = g_strdup(rname); + gchar *p = rclass; + + while (TRUE) { + *p = toupper(*p); + p = strchr(p+1, '.'); + if (p == NULL) break; + ++p; + if (*p == '\0') break; + } + return rclass; +} + +static gboolean read_int(XrmDatabase db, const gchar *rname, gint *value) +{ + gboolean ret = FALSE; + gchar *rclass = create_class_name(rname); + gchar *rettype, *end; + XrmValue retvalue; + + if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) && + retvalue.addr != NULL) { + *value = (gint)strtol(retvalue.addr, &end, 10); + if (end != retvalue.addr) + ret = TRUE; + } + + g_free(rclass); + return ret; +} + +static gboolean read_string(XrmDatabase db, const gchar *rname, gchar **value) +{ + gboolean ret = FALSE; + gchar *rclass = create_class_name(rname); + gchar *rettype; + XrmValue retvalue; + + if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) && + retvalue.addr != NULL) { + g_strstrip(retvalue.addr); + *value = retvalue.addr; + ret = TRUE; + } + + g_free(rclass); + return ret; +} + +static gboolean read_color(XrmDatabase db, const RrInstance *inst, + const gchar *rname, RrColor **value) +{ + gboolean ret = FALSE; + gchar *rclass = create_class_name(rname); + gchar *rettype; + XrmValue retvalue; + + if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) && + retvalue.addr != NULL) { + RrColor *c; + + /* retvalue.addr is inside the xrdb database so we can't destroy it + but we can edit it in place, as g_strstrip does. */ + g_strstrip(retvalue.addr); + c = RrColorParse(inst, retvalue.addr); + if (c != NULL) { + *value = c; + ret = TRUE; + } + } + + g_free(rclass); + return ret; +} + +static gboolean read_mask(const RrInstance *inst, const gchar *path, + RrTheme *theme, const gchar *maskname, + RrPixmapMask **value) +{ + gboolean ret = FALSE; + gchar *s; + gint hx, hy; /* ignored */ + guint w, h; + guchar *b; + + s = g_build_filename(path, maskname, NULL); + if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) { + ret = TRUE; + *value = RrPixmapMaskNew(inst, w, h, (gchar*)b); + XFree(b); + } + g_free(s); + + return ret; +} + +static void parse_appearance(gchar *tex, RrSurfaceColorType *grad, + RrReliefType *relief, RrBevelType *bevel, + gboolean *interlaced, gboolean *border, + gboolean allow_trans) +{ + gchar *t; + + /* convert to all lowercase */ + for (t = tex; *t != '\0'; ++t) + *t = g_ascii_tolower(*t); + + if (allow_trans && strstr(tex, "parentrelative") != NULL) { + *grad = RR_SURFACE_PARENTREL; + } else { + if (strstr(tex, "gradient") != NULL) { + if (strstr(tex, "crossdiagonal") != NULL) + *grad = RR_SURFACE_CROSS_DIAGONAL; + else if (strstr(tex, "pyramid") != NULL) + *grad = RR_SURFACE_PYRAMID; + else if (strstr(tex, "mirrorhorizontal") != NULL) + *grad = RR_SURFACE_MIRROR_HORIZONTAL; + else if (strstr(tex, "horizontal") != NULL) + *grad = RR_SURFACE_HORIZONTAL; + else if (strstr(tex, "splitvertical") != NULL) + *grad = RR_SURFACE_SPLIT_VERTICAL; + else if (strstr(tex, "vertical") != NULL) + *grad = RR_SURFACE_VERTICAL; + else + *grad = RR_SURFACE_DIAGONAL; + } else { + *grad = RR_SURFACE_SOLID; + } + } + + if (strstr(tex, "sunken") != NULL) + *relief = RR_RELIEF_SUNKEN; + else if (strstr(tex, "flat") != NULL) + *relief = RR_RELIEF_FLAT; + else if (strstr(tex, "raised") != NULL) + *relief = RR_RELIEF_RAISED; + else + *relief = (*grad == RR_SURFACE_PARENTREL) ? + RR_RELIEF_FLAT : RR_RELIEF_RAISED; + + *border = FALSE; + if (*relief == RR_RELIEF_FLAT) { + if (strstr(tex, "border") != NULL) + *border = TRUE; + } else { + if (strstr(tex, "bevel2") != NULL) + *bevel = RR_BEVEL_2; + else + *bevel = RR_BEVEL_1; + } + + if (strstr(tex, "interlaced") != NULL) + *interlaced = TRUE; + else + *interlaced = FALSE; +} + +static gboolean read_appearance(XrmDatabase db, const RrInstance *inst, + const gchar *rname, RrAppearance *value, + gboolean allow_trans) +{ + gboolean ret = FALSE; + gchar *rclass = create_class_name(rname); + gchar *cname, *ctoname, *bcname, *icname, *hname, *sname; + gchar *csplitname, *ctosplitname; + gchar *rettype; + XrmValue retvalue; + gint i; + + cname = g_strconcat(rname, ".color", NULL); + ctoname = g_strconcat(rname, ".colorTo", NULL); + bcname = g_strconcat(rname, ".border.color", NULL); + icname = g_strconcat(rname, ".interlace.color", NULL); + hname = g_strconcat(rname, ".highlight", NULL); + sname = g_strconcat(rname, ".shadow", NULL); + csplitname = g_strconcat(rname, ".color.splitTo", NULL); + ctosplitname = g_strconcat(rname, ".colorTo.splitTo", NULL); + + if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) && + retvalue.addr != NULL) { + parse_appearance(retvalue.addr, + &value->surface.grad, + &value->surface.relief, + &value->surface.bevel, + &value->surface.interlaced, + &value->surface.border, + allow_trans); + if (!read_color(db, inst, cname, &value->surface.primary)) + value->surface.primary = RrColorNew(inst, 0, 0, 0); + if (!read_color(db, inst, ctoname, &value->surface.secondary)) + value->surface.secondary = RrColorNew(inst, 0, 0, 0); + if (value->surface.border) + if (!read_color(db, inst, bcname, + &value->surface.border_color)) + value->surface.border_color = RrColorNew(inst, 0, 0, 0); + if (value->surface.interlaced) + if (!read_color(db, inst, icname, + &value->surface.interlace_color)) + value->surface.interlace_color = RrColorNew(inst, 0, 0, 0); + if (read_int(db, hname, &i) && i >= 0) + value->surface.bevel_light_adjust = i; + if (read_int(db, sname, &i) && i >= 0 && i <= 256) + value->surface.bevel_dark_adjust = i; + + if (value->surface.grad == RR_SURFACE_SPLIT_VERTICAL) { + gint r, g, b; + + if (!read_color(db, inst, csplitname, + &value->surface.split_primary)) + { + r = value->surface.primary->r; + r += r >> 2; + g = value->surface.primary->g; + g += g >> 2; + b = value->surface.primary->b; + b += b >> 2; + if (r > 0xFF) r = 0xFF; + if (g > 0xFF) g = 0xFF; + if (b > 0xFF) b = 0xFF; + value->surface.split_primary = RrColorNew(inst, r, g, b); + } + + if (!read_color(db, inst, ctosplitname, + &value->surface.split_secondary)) + { + r = value->surface.secondary->r; + r += r >> 4; + g = value->surface.secondary->g; + g += g >> 4; + b = value->surface.secondary->b; + b += b >> 4; + if (r > 0xFF) r = 0xFF; + if (g > 0xFF) g = 0xFF; + if (b > 0xFF) b = 0xFF; + value->surface.split_secondary = RrColorNew(inst, r, g, b); + } + } + + ret = TRUE; + } + + g_free(ctosplitname); + g_free(csplitname); + g_free(sname); + g_free(hname); + g_free(icname); + g_free(bcname); + g_free(ctoname); + g_free(cname); + g_free(rclass); + return ret; +} + +static int parse_inline_number(const char *p) +{ + int neg = 1; + int res = 0; + if (*p == '-') { + neg = -1; + ++p; + } + for (; isdigit(*p); ++p) + res = res * 10 + *p - '0'; + res *= neg; + return res; +} + +static void set_default_appearance(RrAppearance *a) +{ + a->surface.grad = RR_SURFACE_SOLID; + a->surface.relief = RR_RELIEF_FLAT; + a->surface.bevel = RR_BEVEL_1; + a->surface.interlaced = FALSE; + a->surface.border = FALSE; + a->surface.primary = RrColorNew(a->inst, 0, 0, 0); + a->surface.secondary = RrColorNew(a->inst, 0, 0, 0); +} + +/* Reads the output from gimp's C-Source file format into valid RGBA data for + an RrTextureRGBA. */ +static RrPixel32* read_c_image(gint width, gint height, const guint8 *data) +{ + RrPixel32 *im, *p; + gint i; + + p = im = g_memdup(data, width * height * sizeof(RrPixel32)); + + for (i = 0; i < width * height; ++i) { + guchar a = ((*p >> 24) & 0xff); + guchar b = ((*p >> 16) & 0xff); + guchar g = ((*p >> 8) & 0xff); + guchar r = ((*p >> 0) & 0xff); + + *p = ((r << RrDefaultRedOffset) + + (g << RrDefaultGreenOffset) + + (b << RrDefaultBlueOffset) + + (a << RrDefaultAlphaOffset)); + p++; + } + + return im; +} + +static void read_button_colors(XrmDatabase db, const RrInstance *inst, + const RrTheme *theme, RrButton *btn, + const gchar *btnname) +{ + gchar *name; + + /* active unpressed */ + name = g_strdup_printf("window.active.button.%s.unpressed.image.color", + btnname); + READ_COLOR(name, btn->focused_unpressed_color, + RrColorCopy(theme->titlebut_focused_unpressed_color)); + g_free(name); + + /* inactive unpressed */ + name = g_strdup_printf("window.inactive.button.%s.unpressed.image.color", + btnname); + READ_COLOR(name, btn->unfocused_unpressed_color, + RrColorCopy(theme->titlebut_unfocused_unpressed_color)); + g_free(name); + + /* active pressed */ + name = g_strdup_printf("window.active.button.%s.pressed.image.color", + btnname); + READ_COLOR(name, btn->focused_pressed_color, + RrColorCopy(theme->titlebut_focused_pressed_color)); + g_free(name); + + /* inactive pressed */ + name = g_strdup_printf("window.inactive.button.%s.pressed.image.color", + btnname); + READ_COLOR(name, btn->unfocused_pressed_color, + RrColorCopy(theme->titlebut_unfocused_pressed_color)); + g_free(name); + + /* active disabled */ + name = g_strdup_printf("window.active.button.%s.disabled.image.color", + btnname); + READ_COLOR(name, btn->disabled_focused_color, + RrColorCopy(theme->titlebut_disabled_focused_color)); + g_free(name); + + /* inactive disabled */ + name = g_strdup_printf("window.inactive.button.%s.disabled.image.color", + btnname); + READ_COLOR(name, btn->disabled_unfocused_color, + RrColorCopy(theme->titlebut_disabled_unfocused_color)); + g_free(name); + + /* active hover */ + name = g_strdup_printf("window.active.button.%s.hover.image.color", + btnname); + READ_COLOR(name, btn->hover_focused_color, + RrColorCopy(theme->titlebut_hover_focused_color)); + g_free(name); + + /* inactive hover */ + name = g_strdup_printf("window.inactive.button.%s.hover.image.color", + btnname); + READ_COLOR(name, btn->hover_unfocused_color, + RrColorCopy(theme->titlebut_hover_unfocused_color)); + g_free(name); + + /* active toggled unpressed */ + name = g_strdup_printf("window.active.button.%s.toggled." + "unpressed.image.color", btnname); + READ_COLOR(name, btn->toggled_focused_unpressed_color, + RrColorCopy(theme->titlebut_toggled_focused_unpressed_color)); + g_free(name); + + /* inactive toggled unpressed */ + name = g_strdup_printf("window.inactive.button.%s.toggled." + "unpressed.image.color", btnname); + READ_COLOR(name, btn->toggled_unfocused_unpressed_color, + RrColorCopy(theme->titlebut_toggled_unfocused_unpressed_color)); + g_free(name); + + /* active toggled hover */ + name = g_strdup_printf("window.active.button.%s.toggled.hover.image.color", + btnname); + READ_COLOR(name, btn->toggled_hover_focused_color, + RrColorCopy(theme->titlebut_toggled_hover_focused_color)); + + g_free(name); + + /* inactive toggled hover */ + name = g_strdup_printf("window.inactive.button.%s.toggled.hover." + "image.color", btnname); + READ_COLOR(name, btn->toggled_hover_unfocused_color, + RrColorCopy(theme->titlebut_toggled_hover_unfocused_color)); + g_free(name); + + /* active toggled pressed */ + name = g_strdup_printf("window.active.button.%s.toggled.pressed." + "image.color", btnname); + READ_COLOR(name, btn->toggled_focused_pressed_color, + RrColorCopy(theme->titlebut_toggled_focused_pressed_color)); + g_free(name); + + /* inactive toggled pressed */ + name = g_strdup_printf("window.inactive.button.%s.toggled.pressed." + "image.color", btnname); + READ_COLOR(name, btn->toggled_unfocused_pressed_color, + RrColorCopy(theme->titlebut_toggled_unfocused_pressed_color)); + g_free(name); +} + + diff --git a/obrender/theme.h b/obrender/theme.h new file mode 100644 index 0000000..eb9ac3f --- /dev/null +++ b/obrender/theme.h @@ -0,0 +1,198 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + theme.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __theme_h +#define __theme_h + +#include "render.h" + +G_BEGIN_DECLS + +typedef struct _RrTheme RrTheme; + +struct _RrTheme { + const RrInstance *inst; + + /* style settings - fonts */ + RrFont *win_font_focused; + RrFont *win_font_unfocused; + RrFont *menu_title_font; + RrFont *menu_font; + RrFont *osd_font_hilite; + RrFont *osd_font_unhilite; + + /* style settings - geometry */ + gint paddingx; + gint paddingy; + gint handle_height; + gint fbwidth; /*!< frame border width */ + gint mbwidth; /*!< menu border width */ + gint obwidth; /*!< osd border width */ + gint ubwidth; /*!< undecorated frame border width */ + gint cbwidthx; + gint cbwidthy; + gint menu_overlap_x; + gint menu_overlap_y; + gint menu_sep_width; + gint menu_sep_paddingx; + gint menu_sep_paddingy; + /* these ones are calculated, not set directly by the theme file */ + gint win_font_height; + gint menu_title_font_height; + gint menu_font_height; + gint label_height; + gint title_height; + gint button_size; + gint grip_width; + gint menu_title_label_height; + gint menu_title_height; + + /* style settings - colors */ + RrColor *menu_border_color; + RrColor *osd_border_color; + RrColor *frame_focused_border_color; + RrColor *frame_undecorated_focused_border_color; + RrColor *frame_unfocused_border_color; + RrColor *frame_undecorated_unfocused_border_color; + RrColor *title_separator_focused_color; + RrColor *title_separator_unfocused_color; + RrColor *cb_focused_color; + RrColor *cb_unfocused_color; + RrColor *title_focused_color; + RrColor *title_unfocused_color; + RrColor *titlebut_disabled_focused_color; + RrColor *titlebut_disabled_unfocused_color; + RrColor *titlebut_hover_focused_color; + RrColor *titlebut_hover_unfocused_color; + RrColor *titlebut_toggled_hover_focused_color; + RrColor *titlebut_toggled_hover_unfocused_color; + RrColor *titlebut_toggled_focused_pressed_color; + RrColor *titlebut_toggled_unfocused_pressed_color; + RrColor *titlebut_toggled_focused_unpressed_color; + RrColor *titlebut_toggled_unfocused_unpressed_color; + RrColor *titlebut_focused_pressed_color; + RrColor *titlebut_unfocused_pressed_color; + RrColor *titlebut_focused_unpressed_color; + RrColor *titlebut_unfocused_unpressed_color; + RrColor *menu_title_color; + RrColor *menu_sep_color; + RrColor *menu_color; + RrColor *menu_bullet_color; + RrColor *menu_bullet_selected_color; + RrColor *menu_selected_color; + RrColor *menu_disabled_color; + RrColor *menu_disabled_selected_color; + RrColor *title_focused_shadow_color; + gchar title_focused_shadow_alpha; + RrColor *title_unfocused_shadow_color; + gchar title_unfocused_shadow_alpha; + RrColor *osd_text_active_color; + RrColor *osd_text_inactive_color; + RrColor *osd_text_active_shadow_color; + RrColor *osd_text_inactive_shadow_color; + gchar osd_text_active_shadow_alpha; + gchar osd_text_inactive_shadow_alpha; + RrColor *osd_pressed_color; + RrColor *osd_unpressed_color; + RrColor *osd_focused_color; + RrColor *osd_pressed_lineart; + RrColor *osd_focused_lineart; + RrColor *menu_title_shadow_color; + gchar menu_title_shadow_alpha; + RrColor *menu_text_normal_shadow_color; + gchar menu_text_normal_shadow_alpha; + RrColor *menu_text_selected_shadow_color; + gchar menu_text_selected_shadow_alpha; + RrColor *menu_text_disabled_shadow_color; + gchar menu_text_disabled_shadow_alpha; + RrColor *menu_text_disabled_selected_shadow_color; + gchar menu_text_disabled_selected_shadow_alpha; + + /* style settings - pics */ + RrPixel32 *def_win_icon; /* RGBA */ + gint def_win_icon_w; + gint def_win_icon_h; + + /* style settings - masks */ + RrPixmapMask *menu_bullet_mask; /* submenu pointer */ +#if 0 + RrPixmapMask *menu_toggle_mask; /* menu boolean */ +#endif + + RrPixmapMask *down_arrow_mask; + RrPixmapMask *up_arrow_mask; + + /* buttons */ + RrButton *btn_max; + RrButton *btn_close; + RrButton *btn_desk; + RrButton *btn_shade; + RrButton *btn_iconify; + + /* global appearances */ + RrAppearance *a_focused_grip; + RrAppearance *a_unfocused_grip; + RrAppearance *a_focused_title; + RrAppearance *a_unfocused_title; + RrAppearance *a_focused_label; + RrAppearance *a_unfocused_label; + /* always parentrelative, so no focused/unfocused */ + RrAppearance *a_icon; + RrAppearance *a_focused_handle; + RrAppearance *a_unfocused_handle; + RrAppearance *a_menu_text_title; + RrAppearance *a_menu_title; + RrAppearance *a_menu; + RrAppearance *a_menu_normal; + RrAppearance *a_menu_selected; + RrAppearance *a_menu_disabled; + RrAppearance *a_menu_disabled_selected; + RrAppearance *a_menu_text_normal; + RrAppearance *a_menu_text_disabled; + RrAppearance *a_menu_text_disabled_selected; + RrAppearance *a_menu_text_selected; + RrAppearance *a_menu_bullet_normal; + RrAppearance *a_menu_bullet_selected; + RrAppearance *a_clear; /* clear with no texture */ + RrAppearance *a_clear_tex; /* clear with a texture */ + + RrAppearance *osd_bg; /* can never be parent relative */ + RrAppearance *osd_hilite_bg; /* can never be parent relative */ + RrAppearance *osd_hilite_label; /* can be parent relative */ + RrAppearance *osd_unhilite_bg; /* can never be parent relative */ + RrAppearance *osd_unhilite_label; /* can be parent relative */ + RrAppearance *osd_pressed_button; + RrAppearance *osd_unpressed_button; + RrAppearance *osd_focused_button; + + gchar *name; +}; + +/*! The font values are all optional. If a NULL is used for any of them, then + the default font will be used. */ +RrTheme* RrThemeNew(const RrInstance *inst, const gchar *theme, + gboolean allow_fallback, + RrFont *active_window_font, RrFont *inactive_window_font, + RrFont *menu_title_font, RrFont *menu_item_font, + RrFont *active_osd_font, RrFont *inactive_osd_font); +void RrThemeFree(RrTheme *theme); + +G_END_DECLS + +#endif diff --git a/obrender/version.h.in b/obrender/version.h.in new file mode 100644 index 0000000..0ff30b5 --- /dev/null +++ b/obrender/version.h.in @@ -0,0 +1,15 @@ +#ifndef rr__version_h +#define rr__version_h + +#define RR_MAJOR_VERSION @RR_MAJOR_VERSION@ +#define RR_MINOR_VERSION @RR_MINOR_VERSION@ +#define RR_MICRO_VERSION @RR_MICRO_VERSION@ +#define RR_VERSION RR_MAJOR_VERSION.RR_MINOR_VERSION.RR_MICRO_VERSION + +#define RR_CHECK_VERSION(major,minor,micro) \ + (RR_MAJOR_VERSION > (major) || \ + (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION > (minor)) || \ + (RR_MAJOR_VERSION == (major) && RR_MINOR_VERSION == (minor) && \ + RR_MICRO_VERSION >= (micro))) + +#endif diff --git a/obt/Makefile b/obt/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/obt/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/obt/bsearch.h b/obt/bsearch.h new file mode 100644 index 0000000..9613c51 --- /dev/null +++ b/obt/bsearch.h @@ -0,0 +1,61 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/bsearch.h for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_bsearch_h +#define __obt_bsearch_h + +#include <glib.h> + +G_BEGIN_DECLS + +/*! Setup to do a binary search on an array holding elements of type @t */ +#define BSEARCH_SETUP(t) \ + register t l_BSEARCH, r_BSEARCH, out_BSEARCH; + +/*! Search an array @ar holding elements of type @t, starting at index @start, + with @size elements, looking for value @val. */ +#define BSEARCH(t, ar, start, size, val) \ +{ \ + l_BSEARCH = (start); \ + r_BSEARCH = (start)+(size)-1; \ + while (l_BSEARCH <= r_BSEARCH) { \ + /* m is in the middle, but to the left if there's an even number \ + of elements */ \ + out_BSEARCH = l_BSEARCH + (r_BSEARCH - l_BSEARCH)/2; \ + if ((val) == (ar)[out_BSEARCH]) { \ + break; \ + } \ + else if ((val) < (ar)[out_BSEARCH] && out_BSEARCH > 0) { \ + r_BSEARCH = out_BSEARCH-1; /* search to the left side */ \ + } \ + else \ + l_BSEARCH = out_BSEARCH+1; /* search to the left side */ \ + } \ +} + +/*! Returns true if the element last searched for was found in the array */ +#define BSEARCH_FOUND() (l_BSEARCH <= r_BSEARCH) +/*! Returns the position in the array at which the element last searched for + was found. */ +#define BSEARCH_AT() (out_BSEARCH) + + + +G_END_DECLS + +#endif diff --git a/obt/ddparse.c b/obt/ddparse.c new file mode 100644 index 0000000..149134d --- /dev/null +++ b/obt/ddparse.c @@ -0,0 +1,816 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/ddparse.c for the Openbox window manager + Copyright (c) 2009 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/ddparse.h" +#include "obt/link.h" +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif + +typedef struct _ObtDDParse ObtDDParse; + +/* Parses the value and adds it to the group's key_hash, with the given + key + Return TRUE if it is added to the hash table, and FALSE if not. +*/ +typedef gboolean (*ObtDDParseValueFunc)(gchar *key, const gchar *val, + ObtDDParse *parse, gboolean *error); + + +enum { + DE_TYPE = 1 << 0, + DE_TYPE_APPLICATION = 1 << 1, + DE_TYPE_LINK = 1 << 2, + DE_NAME = 1 << 3, + DE_EXEC = 1 << 4, + DE_URL = 1 << 5 +}; + +struct _ObtDDParse { + gchar *filename; + gulong lineno; + gulong flags; + ObtDDParseGroup *group; + /* the key is a group name, the value is a ObtDDParseGroup */ + GHashTable *group_hash; +}; + +struct _ObtDDParseGroup { + gchar *name; + gboolean seen; + ObtDDParseValueFunc value_func; + /* the key is a string (a key inside the group in the .desktop). + the value is an ObtDDParseValue */ + GHashTable *key_hash; +}; + +/* Displays a warning message including the file name and line number, and + sets the boolean @error to true if it points to a non-NULL address. +*/ +static void parse_error(const gchar *m, const ObtDDParse *const parse, + gboolean *error) +{ + if (!parse->filename) + g_warning("%s at line %lu of input", m, parse->lineno); + else + g_warning("%s at line %lu of file %s", + m, parse->lineno, parse->filename); + if (error) *error = TRUE; +} + +static void parse_value_free(ObtDDParseValue *v) +{ + switch (v->type) { + case OBT_DDPARSE_EXEC: + case OBT_DDPARSE_STRING: + case OBT_DDPARSE_LOCALESTRING: + g_free(v->value.string); break; + case OBT_DDPARSE_STRINGS: + case OBT_DDPARSE_LOCALESTRINGS: + g_strfreev(v->value.strings.a); + v->value.strings.n = 0; + break; + case OBT_DDPARSE_BOOLEAN: + case OBT_DDPARSE_NUMERIC: + case OBT_DDPARSE_ENUM_TYPE: + case OBT_DDPARSE_ENVIRONMENTS: + break; + default: + g_assert_not_reached(); + } + g_slice_free(ObtDDParseValue, v); +} + +static ObtDDParseGroup* parse_group_new(gchar *name, ObtDDParseValueFunc f) +{ + ObtDDParseGroup *g = g_slice_new(ObtDDParseGroup); + g->name = name; + g->value_func = f; + g->seen = FALSE; + g->key_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, + (GDestroyNotify)parse_value_free); + return g; +} + +static void parse_group_free(ObtDDParseGroup *g) +{ + g_free(g->name); + g_hash_table_destroy(g->key_hash); + g_slice_free(ObtDDParseGroup, g); +} + +/*! Reads an input string, strips out invalid stuff, and parses + backslash-stuff. + */ +static gchar* parse_value_string(const gchar *in, + gboolean locale, + gboolean semicolonterminate, + gulong *len, + const ObtDDParse *const parse, + gboolean *error) +{ + gint bytes; + gboolean backslash; + gchar *out, *o; + const gchar *end, *i; + + /* find the end/size of the string */ + backslash = FALSE; + for (end = in; *end; ++end) { + if (semicolonterminate) { + if (backslash) backslash = FALSE; + else if (*end == '\\') backslash = TRUE; + else if (*end == ';') break; + } + } + bytes = end - in; + + g_return_val_if_fail(in != NULL, NULL); + + if (locale && !g_utf8_validate(in, bytes, &end)) { + parse_error("Invalid bytes in localestring", parse, error); + bytes = end - in; + } + + out = g_new(char, bytes + 1); + if (len) *len = 0; + i = in; o = out; + backslash = FALSE; + while (i < end) { + const gchar *next; + + /* find the next character in the string */ + if (!locale) next = i+1; + else if (!(next = g_utf8_find_next_char(i, end))) next = end; + + if (backslash) { + switch(*i) { + case 's': *o++ = ' '; break; + case 'n': *o++ = '\n'; break; + case 't': *o++ = '\t'; break; + case 'r': *o++ = '\r'; break; + case ';': *o++ = ';'; break; + case '\\': *o++ = '\\'; break; + default: + parse_error((locale ? + "Invalid escape sequence in localestring" : + "Invalid escape sequence in string"), + parse, error); + } + backslash = FALSE; + } + else if (*i == '\\') + backslash = TRUE; + else if ((guchar)*i >= 127 || (guchar)*i < 32) { + /* avoid ascii control characters */ + parse_error("Found control character in string", parse, error); + break; + } + else { + const gulong s = next-i; + memcpy(o, i, s); + o += s; + if (len) *len += s; + } + i = next; + } + *o = '\0'; + return out; +} + + +/*! Reads a list of input strings, strips out invalid stuff, and parses + backslash-stuff. + */ +static gchar** parse_value_strings(const gchar *in, + gboolean locale, + gulong *nstrings, + const ObtDDParse *const parse, + gboolean *error) +{ + gchar **out; + const gchar *i; + + out = g_new(gchar*, 1); + out[0] = NULL; + *nstrings = 0; + + i = in; + while (TRUE) { + gchar *a; + gulong len; + + a = parse_value_string(i, locale, TRUE, &len, parse, error); + i += len; + + if (len) { + (*nstrings)++; + out = g_renew(gchar*, out, *nstrings+1); + out[*nstrings-1] = a; + out[*nstrings] = NULL; + } + + if (!*i) break; /* no more strings */ + ++i; + } + return out; +} + +static guint parse_value_environments(const gchar *in, + const ObtDDParse *const parse, + gboolean *error) +{ + const gchar *s; + guint mask = 0; + + s = in; + while (*s) { + switch (*(s++)) { + case 'G': + if (strcmp(s, "NOME") == 0) { + mask |= OBT_LINK_ENV_GNOME; + s += 4; + } + break; + case 'K': + if (strcmp(s, "DE") == 0) { + mask |= OBT_LINK_ENV_KDE; + s += 2; + } + break; + case 'L': + if (strcmp(s, "XDE") == 0) { + mask |= OBT_LINK_ENV_LXDE; + s += 3; + } + break; + case 'R': + if (strcmp(s, "OX") == 0) { + mask |= OBT_LINK_ENV_ROX; + s += 2; + } + break; + case 'X': + if (strcmp(s, "FCE") == 0) { + mask |= OBT_LINK_ENV_XFCE; + s += 3; + } + break; + case 'O': + switch (*(s++)) { + case 'l': + if (strcmp(s, "d") == 0) { + mask |= OBT_LINK_ENV_OLD; + s += 1; + } + break; + case 'P': + if (strcmp(s, "ENBOX") == 0) { + mask |= OBT_LINK_ENV_OPENBOX; + s += 5; + } + break; + } + } + /* find the next string, or the end of the sequence */ + while (*s && *s != ';') ++s; + } + return mask; +} + +static gboolean parse_value_boolean(const gchar *in, + const ObtDDParse *const parse, + gboolean *error) +{ + if (strcmp(in, "true") == 0) + return TRUE; + else if (strcmp(in, "false") != 0) + parse_error("Invalid boolean value", parse, error); + return FALSE; +} + +static gfloat parse_value_numeric(const gchar *in, + const ObtDDParse *const parse, + gboolean *error) +{ + gfloat out = 0; + if (sscanf(in, "%f", &out) == 0) + parse_error("Invalid numeric value", parse, error); + return out; +} + +static gboolean parse_file_line(FILE *f, gchar **buf, + gulong *size, gulong *read, + ObtDDParse *parse, gboolean *error) +{ + const gulong BUFMUL = 80; + size_t ret; + gulong i, null; + + if (*size == 0) { + g_assert(*read == 0); + *size = BUFMUL; + *buf = g_new(char, *size); + } + + /* remove everything up to a null zero already in the buffer and shift + the rest to the front */ + null = *size; + for (i = 0; i < *read; ++i) { + if (null < *size) + (*buf)[i-null-1] = (*buf)[i]; + else if ((*buf)[i] == '\0') + null = i; + } + if (null < *size) + *read -= null + 1; + + /* is there already a newline in the buffer? */ + for (i = 0; i < *read; ++i) + if ((*buf)[i] == '\n') { + /* turn it into a null zero and done */ + (*buf)[i] = '\0'; + return TRUE; + } + + /* we need to read some more to find a newline */ + while (TRUE) { + gulong eol; + gchar *newread; + + newread = *buf + *read; + ret = fread(newread, sizeof(char), *size-*read, f); + if (ret < *size - *read && !feof(f)) { + parse_error("Error reading", parse, error); + return FALSE; + } + *read += ret; + + /* strip out null zeros in the input and look for an endofline */ + null = 0; + eol = *size; + for (i = newread-*buf; i < *read; ++i) { + if (null > 0) + (*buf)[i] = (*buf)[i+null]; + if ((*buf)[i] == '\0') { + ++null; + --(*read); + --i; /* try again */ + } + else if ((*buf)[i] == '\n' && eol == *size) { + eol = i; + /* turn it into a null zero */ + (*buf)[i] = '\0'; + } + } + + if (eol != *size) + /* found an endofline, done */ + break; + else if (feof(f) && *read < *size) { + /* found the endoffile, done (if there is space) */ + if (*read > 0) { + /* stick a null zero on if there is test on the last line */ + (*buf)[(*read)++] = '\0'; + } + break; + } + else { + /* read more */ + size += BUFMUL; + *buf = g_renew(char, *buf, *size); + } + } + return *read > 0; +} + +static void parse_group(const gchar *buf, gulong len, + ObtDDParse *parse, gboolean *error) +{ + ObtDDParseGroup *g; + gchar *group; + gulong i; + + /* get the group name */ + group = g_strndup(buf+1, len-2); + for (i = 0; i < len-2; ++i) + if ((guchar)group[i] < 32 || (guchar)group[i] >= 127) { + /* valid ASCII only */ + parse_error("Invalid character found", parse, NULL); + group[i] = '\0'; /* stopping before this character */ + break; + } + + /* make sure it's a new group */ + g = g_hash_table_lookup(parse->group_hash, group); + if (g && g->seen) { + parse_error("Duplicate group found", parse, error); + g_free(group); + return; + } + /* if it's the first group, make sure it's named Desktop Entry */ + else if (!parse->group && strcmp(group, "Desktop Entry") != 0) + { + parse_error("Incorrect group found, " + "expected [Desktop Entry]", + parse, error); + g_free(group); + return; + } + else { + if (!g) { + g = parse_group_new(group, NULL); + g_hash_table_insert(parse->group_hash, g->name, g); + } + else + g_free(group); + + g->seen = TRUE; + parse->group = g; + g_print("Found group %s\n", g->name); + } +} + +static void parse_key_value(const gchar *buf, gulong len, + ObtDDParse *parse, gboolean *error) +{ + gulong i, keyend, valstart, eq; + char *key; + + /* find the end of the key */ + for (i = 0; i < len; ++i) + if (!(((guchar)buf[i] >= 'A' && (guchar)buf[i] <= 'Z') || + ((guchar)buf[i] >= 'a' && (guchar)buf[i] <= 'z') || + ((guchar)buf[i] >= '0' && (guchar)buf[i] <= '9') || + ((guchar)buf[i] == '-'))) { + /* not part of the key */ + break; + } + keyend = i; + + if (keyend < 1) { + parse_error("Empty key", parse, error); + return; + } + /* find the = character */ + for (i = keyend; i < len; ++i) { + if (buf[i] == '=') { + eq = i; + break; + } + else if (buf[i] != ' ') { + parse_error("Invalid character in key name", parse, error); + return ; + } + } + if (i == len) { + parse_error("Key without value found", parse, error); + return; + } + /* find the start of the value */ + for (i = eq+1; i < len; ++i) + if (buf[i] != ' ') { + valstart = i; + break; + } + if (i == len) { + parse_error("Empty value found", parse, error); + return; + } + + key = g_strndup(buf, keyend); + if (g_hash_table_lookup(parse->group->key_hash, key)) { + parse_error("Duplicate key found", parse, error); + g_free(key); + return; + } + g_print("Found key/value %s=%s.\n", key, buf+valstart); + if (parse->group->value_func) + if (!parse->group->value_func(key, buf+valstart, parse, error)) { + parse_error("Unknown key", parse, error); + g_free(key); + } +} + +static gboolean parse_file(FILE *f, ObtDDParse *parse) +{ + gchar *buf = NULL; + gulong bytes = 0, read = 0; + gboolean error = FALSE; + + while (!error && parse_file_line(f, &buf, &bytes, &read, parse, &error)) { + gulong len = strlen(buf); + if (buf[0] == '#' || buf[0] == '\0') + ; /* ignore comment lines */ + else if (buf[0] == '[' && buf[len-1] == ']') + parse_group(buf, len, parse, &error); + else if (!parse->group) + /* just ignore keys outside of groups */ + parse_error("Key found before group", parse, NULL); + else + /* ignore errors in key-value pairs and continue */ + parse_key_value(buf, len, parse, NULL); + ++parse->lineno; + } + + if (buf) g_free(buf); + return !error; +} + +static gboolean parse_desktop_entry_value(gchar *key, const gchar *val, + ObtDDParse *parse, gboolean *error) +{ + ObtDDParseValue v, *pv; + + switch (key[0]) { + case 'C': + switch (key[1]) { + case 'a': /* Categories */ + if (strcmp(key+2, "tegories")) return FALSE; + v.type = OBT_DDPARSE_STRINGS; break; + case 'o': /* Comment */ + if (strcmp(key+2, "mment")) return FALSE; + v.type = OBT_DDPARSE_LOCALESTRING; break; + default: + return FALSE; + } + break; + case 'E': /* Exec */ + if (strcmp(key+1, "xec")) return FALSE; + v.type = OBT_DDPARSE_EXEC; parse->flags |= DE_EXEC; break; + case 'G': /* GenericName */ + if (strcmp(key+1, "enericName")) return FALSE; + v.type = OBT_DDPARSE_LOCALESTRING; break; + case 'I': /* Icon */ + if (strcmp(key+1, "con")) return FALSE; + v.type = OBT_DDPARSE_LOCALESTRING; break; + case 'H': /* Hidden */ + if (strcmp(key+1, "idden")) return FALSE; + v.type = OBT_DDPARSE_BOOLEAN; break; + case 'M': /* MimeType */ + if (strcmp(key+1, "imeType")) return FALSE; + v.type = OBT_DDPARSE_STRINGS; break; + case 'N': + switch (key[1]) { + case 'a': /* Name */ + if (strcmp(key+2, "me")) return FALSE; + v.type = OBT_DDPARSE_LOCALESTRING; parse->flags |= DE_NAME; break; + case 'o': + switch (key[2]) { + case 'D': /* NoDisplay */ + if (strcmp(key+3, "isplay")) return FALSE; + v.type = OBT_DDPARSE_BOOLEAN; break; + case 't': /* NotShowIn */ + if (strcmp(key+3, "ShowIn")) return FALSE; + v.type = OBT_DDPARSE_STRINGS; break; + default: + return FALSE; + } + break; + default: + return FALSE; + } + break; + case 'P': /* Path */ + if (strcmp(key+1, "ath")) return FALSE; + v.type = OBT_DDPARSE_STRING; break; + case 'S': /* Path */ + if (key[1] == 't' && key[2] == 'a' && key[3] == 'r' && + key[4] == 't' && key[5] == 'u' && key[6] == 'p') + switch (key[7]) { + case 'N': /* StartupNotify */ + if (strcmp(key+8, "otify")) return FALSE; + v.type = OBT_DDPARSE_BOOLEAN; break; + case 'W': /* StartupWMClass */ + if (strcmp(key+8, "MClass")) return FALSE; + v.type = OBT_DDPARSE_STRING; break; + default: + return FALSE; + } + else + return FALSE; + break; + case 'T': + switch (key[1]) { + case 'e': /* Terminal */ + if (strcmp(key+2, "rminal")) return FALSE; + v.type = OBT_DDPARSE_BOOLEAN; break; + case 'r': /* TryExec */ + if (strcmp(key+2, "yExec")) return FALSE; + v.type = OBT_DDPARSE_STRING; break; + case 'y': /* Type */ + if (strcmp(key+2, "pe")) return FALSE; + v.type = OBT_DDPARSE_ENUM_TYPE; parse->flags |= DE_TYPE; break; + default: + return FALSE; + } + break; + case 'U': /* URL */ + if (strcmp(key+1, "RL")) return FALSE; + v.type = OBT_DDPARSE_STRING; parse->flags |= DE_URL; break; + case 'V': /* MimeType */ + if (strcmp(key+1, "ersion")) return FALSE; + v.type = OBT_DDPARSE_STRING; break; + default: + return FALSE; + } + + /* parse the value */ + switch (v.type) { + case OBT_DDPARSE_EXEC: { + gchar *c, *m; + gboolean percent; + gboolean found; + + v.value.string = parse_value_string(val, FALSE, FALSE, NULL, + parse, error); + g_assert(v.value.string); + + /* an exec string can only contain one of the file/url-opening %'s */ + percent = found = FALSE; + for (c = v.value.string; *c; ++c) { + if (percent) { + switch (*c) { + case 'f': + case 'F': + case 'u': + case 'U': + if (found) { + m = g_strdup_printf("Malformed Exec key, " + "extraneous %%%c", *c); + parse_error(m, parse, error); + g_free(m); + } + found = TRUE; + break; + case 'd': + case 'D': + case 'n': + case 'N': + case 'v': + case 'm': + m = g_strdup_printf("Malformed Exec key, " + "uses deprecated %%%c", *c); + parse_error(m, parse, NULL); /* just a warning */ + g_free(m); + break; + case 'i': + case 'c': + case 'k': + case '%': + break; + default: + m = g_strdup_printf("Malformed Exec key, " + "uses unknown %%%c", *c); + parse_error(m, parse, NULL); /* just a warning */ + g_free(m); + } + percent = FALSE; + } + else if (*c == '%') percent = TRUE; + } + break; + } + case OBT_DDPARSE_STRING: + v.value.string = parse_value_string(val, FALSE, FALSE, NULL, + parse, error); + g_assert(v.value.string); + break; + case OBT_DDPARSE_LOCALESTRING: + v.value.string = parse_value_string(val, TRUE, FALSE, NULL, + parse, error); + g_assert(v.value.string); + break; + case OBT_DDPARSE_STRINGS: + v.value.strings.a = parse_value_strings(val, FALSE, &v.value.strings.n, + parse, error); + g_assert(v.value.strings.a); + g_assert(v.value.strings.n); + break; + case OBT_DDPARSE_LOCALESTRINGS: + v.value.strings.a = parse_value_strings(val, TRUE, &v.value.strings.n, + parse, error); + g_assert(v.value.strings.a); + g_assert(v.value.strings.n); + break; + case OBT_DDPARSE_BOOLEAN: + v.value.boolean = parse_value_boolean(val, parse, error); + break; + case OBT_DDPARSE_NUMERIC: + v.value.numeric = parse_value_numeric(val, parse, error); + break; + case OBT_DDPARSE_ENUM_TYPE: + if (val[0] == 'A' && strcmp(val+1, "pplication") == 0) { + v.value.enumerable = OBT_LINK_TYPE_APPLICATION; + parse->flags |= DE_TYPE_APPLICATION; + } + else if (val[0] == 'L' && strcmp(val+1, "ink") == 0) { + v.value.enumerable = OBT_LINK_TYPE_URL; + parse->flags |= DE_TYPE_LINK; + } + else if (val[0] == 'D' && strcmp(val+1, "irectory") == 0) + v.value.enumerable = OBT_LINK_TYPE_DIRECTORY; + else { + parse_error("Unknown Type", parse, error); + return FALSE; + } + break; + case OBT_DDPARSE_ENVIRONMENTS: + v.value.environments = parse_value_environments(val, parse, error); + break; + default: + g_assert_not_reached(); + } + + pv = g_slice_new(ObtDDParseValue); + *pv = v; + g_hash_table_insert(parse->group->key_hash, key, pv); + return TRUE; +} + +GHashTable* obt_ddparse_file(const gchar *name, GSList *paths) +{ + ObtDDParse parse; + ObtDDParseGroup *desktop_entry; + GSList *it; + FILE *f; + gboolean success; + + parse.filename = NULL; + parse.lineno = 0; + parse.group = NULL; + parse.group_hash = g_hash_table_new_full(g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)parse_group_free); + + /* set up the groups (there's only one right now) */ + desktop_entry = parse_group_new(g_strdup("Desktop Entry"), + parse_desktop_entry_value); + g_hash_table_insert(parse.group_hash, desktop_entry->name, desktop_entry); + + success = FALSE; + for (it = paths; it && !success; it = g_slist_next(it)) { + gchar *path = g_strdup_printf("%s/%s", (char*)it->data, name); + if ((f = fopen(path, "r"))) { + parse.filename = path; + parse.lineno = 1; + parse.flags = 0; + if ((success = parse_file(f, &parse))) { + /* check that required keys exist */ + + if (!(parse.flags & DE_TYPE)) { + g_warning("Missing Type key in %s", path); + success = FALSE; + } + if (!(parse.flags & DE_NAME)) { + g_warning("Missing Name key in %s", path); + success = FALSE; + } + if (parse.flags & DE_TYPE_APPLICATION && + !(parse.flags & DE_EXEC)) + { + g_warning("Missing Exec key for Application in %s", + path); + success = FALSE; + } + else if (parse.flags & DE_TYPE_LINK && !(parse.flags & DE_URL)) + { + g_warning("Missing URL key for Link in %s", path); + success = FALSE; + } + } + fclose(f); + } + g_free(path); + } + if (!success) { + g_hash_table_destroy(parse.group_hash); + parse.group_hash = NULL; + } + return parse.group_hash; +} + +GHashTable* obt_ddparse_group_keys(ObtDDParseGroup *g) +{ + return g->key_hash; +} diff --git a/obt/ddparse.h b/obt/ddparse.h new file mode 100644 index 0000000..d261f5b --- /dev/null +++ b/obt/ddparse.h @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/ddparse.h for the Openbox window manager + Copyright (c) 2009 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <glib.h> + +typedef struct _ObtDDParseGroup ObtDDParseGroup; + +typedef enum { + OBT_DDPARSE_EXEC, + OBT_DDPARSE_STRING, + OBT_DDPARSE_LOCALESTRING, + OBT_DDPARSE_STRINGS, + OBT_DDPARSE_LOCALESTRINGS, + OBT_DDPARSE_BOOLEAN, + OBT_DDPARSE_NUMERIC, + OBT_DDPARSE_ENUM_TYPE, + OBT_DDPARSE_ENVIRONMENTS, + OBT_DDPARSE_NUM_VALUE_TYPES +} ObtDDParseValueType; + +typedef struct _ObtDDParseValue { + ObtDDParseValueType type; + union _ObtDDParseValueValue { + gchar *string; + struct _ObtDDParseValueStrings { + gchar **a; + gulong n; + } strings; + gboolean boolean; + gfloat numeric; + guint enumerable; + guint environments; /*!< A mask of flags from ObtLinkEnvMask */ + } value; +} ObtDDParseValue; + +/* Returns a hash table where the keys are groups, and the values are + ObtDDParseGroups */ +GHashTable* obt_ddparse_file(const gchar *name, GSList *paths); + +/* Returns a hash table where the keys are "keys" in the .desktop file, + and the values are "values" in the .desktop file, for the group @g. */ +GHashTable* obt_ddparse_group_keys(ObtDDParseGroup *g); diff --git a/obt/display.c b/obt/display.c new file mode 100644 index 0000000..8b06cbf --- /dev/null +++ b/obt/display.c @@ -0,0 +1,165 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/display.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/display.h" +#include "obt/prop.h" +#include "obt/internal.h" +#include "obt/keyboard.h" +#include "obt/xqueue.h" + +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +/* from xqueue.c */ +extern void xqueue_init(void); +extern void xqueue_destroy(void); + +Display* obt_display = NULL; + +gboolean obt_display_error_occured = FALSE; + +gboolean obt_display_extension_xkb = FALSE; +gint obt_display_extension_xkb_basep; +gboolean obt_display_extension_shape = FALSE; +gint obt_display_extension_shape_basep; +gboolean obt_display_extension_xinerama = FALSE; +gint obt_display_extension_xinerama_basep; +gboolean obt_display_extension_randr = FALSE; +gint obt_display_extension_randr_basep; +gboolean obt_display_extension_sync = FALSE; +gint obt_display_extension_sync_basep; + +static gint xerror_handler(Display *d, XErrorEvent *e); + +static gboolean xerror_ignore = FALSE; + +gboolean obt_display_open(const char *display_name) +{ + gchar *n; + Display *d = NULL; + + n = display_name ? g_strdup(display_name) : NULL; + obt_display = d = XOpenDisplay(n); + if (d) { + gint junk, major, minor; + (void)junk, (void)major, (void)minor; + + if (fcntl(ConnectionNumber(d), F_SETFD, 1) == -1) + g_message("Failed to set display as close-on-exec"); + XSetErrorHandler(xerror_handler); + + /* read what extensions are present */ +#ifdef XKB + major = XkbMajorVersion; + minor = XkbMinorVersion; + obt_display_extension_xkb = + XkbQueryExtension(d, &junk, + &obt_display_extension_xkb_basep, &junk, + &major, &minor); + if (!obt_display_extension_xkb) + g_message("XKB extension is not present on the server or too old"); +#endif + +#ifdef SHAPE + obt_display_extension_shape = + XShapeQueryExtension(d, &obt_display_extension_shape_basep, + &junk); + if (!obt_display_extension_shape) + g_message("X Shape extension is not present on the server"); +#endif + +#ifdef XINERAMA + obt_display_extension_xinerama = + XineramaQueryExtension(d, + &obt_display_extension_xinerama_basep, + &junk) && XineramaIsActive(d); + if (!obt_display_extension_xinerama) + g_message("Xinerama extension is not present on the server"); +#endif + +#ifdef XRANDR + obt_display_extension_randr = + XRRQueryExtension(d, &obt_display_extension_randr_basep, + &junk); + if (!obt_display_extension_randr) + g_message("XRandR extension is not present on the server"); +#endif + +#ifdef SYNC + obt_display_extension_sync = + XSyncQueryExtension(d, &obt_display_extension_sync_basep, + &junk) && XSyncInitialize(d, &junk, &junk); + if (!obt_display_extension_sync) + g_message("X Sync extension is not present on the server or is an " + "incompatible version"); +#endif + + obt_prop_startup(); + obt_keyboard_reload(); + } + g_free(n); + + if (obt_display) + xqueue_init(); + + return obt_display != NULL; +} + +void obt_display_close(void) +{ + obt_keyboard_shutdown(); + if (obt_display) { + xqueue_destroy(); + XCloseDisplay(obt_display); + } +} + +static gint xerror_handler(Display *d, XErrorEvent *e) +{ +#ifdef DEBUG + gchar errtxt[128]; + + XGetErrorText(d, e->error_code, errtxt, 127); + if (!xerror_ignore) { + if (e->error_code == BadWindow) + /*g_debug(_("X Error: %s\n"), errtxt)*/; + else + g_error("X Error: %s", errtxt); + } else + g_debug("Ignoring XError code %d '%s'", e->error_code, errtxt); +#else + (void)d; (void)e; +#endif + + obt_display_error_occured = TRUE; + return 0; +} + +void obt_display_ignore_errors(gboolean ignore) +{ + XSync(obt_display, FALSE); + xerror_ignore = ignore; + if (ignore) obt_display_error_occured = FALSE; +} diff --git a/obt/display.h b/obt/display.h new file mode 100644 index 0000000..b5816a9 --- /dev/null +++ b/obt/display.h @@ -0,0 +1,70 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/display.h for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_display_h +#define __obt_display_h + +#include <X11/Xlib.h> +#include <glib.h> + +#include <X11/Xutil.h> /* shape.h uses Region which is in here */ +#ifdef XKB +#include <X11/XKBlib.h> +#endif +#ifdef SHAPE +#include <X11/extensions/shape.h> +#endif +#ifdef XINERAMA +#include <X11/extensions/Xinerama.h> +#endif +#ifdef XRANDR +#include <X11/extensions/Xrandr.h> +#endif +#ifdef SYNC +#include <X11/extensions/sync.h> +#endif + +G_BEGIN_DECLS + +extern gboolean obt_display_error_occured; + +extern gboolean obt_display_extension_xkb; +extern gint obt_display_extension_xkb_basep; +extern gboolean obt_display_extension_shape; +extern gint obt_display_extension_shape_basep; +extern gboolean obt_display_extension_xinerama; +extern gint obt_display_extension_xinerama_basep; +extern gboolean obt_display_extension_randr; +extern gint obt_display_extension_randr_basep; +extern gboolean obt_display_extension_sync; +extern gint obt_display_extension_sync_basep; + +extern Display* obt_display; + +/*! Open the X display. You should call g_set_prgname() before calling this + function for X Input Methods to work correctly. */ +gboolean obt_display_open(const char *display_name); +void obt_display_close(void); + +void obt_display_ignore_errors(gboolean ignore); + +#define obt_root(screen) (RootWindow(obt_display, screen)) + +G_END_DECLS + +#endif /*__obt_display_h*/ diff --git a/obt/internal.h b/obt/internal.h new file mode 100644 index 0000000..818107d --- /dev/null +++ b/obt/internal.h @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/internal.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_internal_h +#define __obt_internal_h + +void obt_prop_startup(void); + +void obt_keyboard_shutdown(void); + +#endif /* __obt_internal_h */ diff --git a/obt/keyboard.c b/obt/keyboard.c new file mode 100644 index 0000000..ef2678b --- /dev/null +++ b/obt/keyboard.c @@ -0,0 +1,445 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/keyboard.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/display.h" +#include "obt/keyboard.h" + +#include <X11/Xlib.h> +#include <X11/keysym.h> + +struct _ObtIC +{ + guint ref; + XIC xic; + Window client; + Window focus; +}; + +/* These masks are constants and the modifier keys are bound to them as + anyone sees fit: + ShiftMask (1<<0), LockMask (1<<1), ControlMask (1<<2), Mod1Mask (1<<3), + Mod2Mask (1<<4), Mod3Mask (1<<5), Mod4Mask (1<<6), Mod5Mask (1<<7) +*/ +#define NUM_MASKS 8 +#define ALL_MASKS 0xff /* an or'ing of all 8 keyboard masks */ + +/* Get the bitflag for the n'th modifier mask */ +#define nth_mask(n) (1 << n) + +static void set_modkey_mask(guchar mask, KeySym sym); +static void xim_init(void); +void obt_keyboard_shutdown(); +void obt_keyboard_context_renew(ObtIC *ic); + +static XModifierKeymap *modmap; +static KeySym *keymap; +static gint min_keycode, max_keycode, keysyms_per_keycode; +/* This is a bitmask of the different masks for each modifier key */ +static guchar modkeys_keys[OBT_KEYBOARD_NUM_MODKEYS]; + +static gboolean alt_l = FALSE; +static gboolean meta_l = FALSE; +static gboolean super_l = FALSE; +static gboolean hyper_l = FALSE; + +static gboolean started = FALSE; + +static XIM xim = NULL; +static XIMStyle xim_style = 0; +static GSList *xic_all = NULL; + +void obt_keyboard_reload(void) +{ + gint i, j, k; + + if (started) obt_keyboard_shutdown(); /* free stuff */ + started = TRUE; + + xim_init(); + + /* reset the keys to not be bound to any masks */ + for (i = 0; i < OBT_KEYBOARD_NUM_MODKEYS; ++i) + modkeys_keys[i] = 0; + + modmap = XGetModifierMapping(obt_display); + /* note: modmap->max_keypermod can be 0 when there is no valid key layout + available */ + + XDisplayKeycodes(obt_display, &min_keycode, &max_keycode); + keymap = XGetKeyboardMapping(obt_display, min_keycode, + max_keycode - min_keycode + 1, + &keysyms_per_keycode); + + alt_l = meta_l = super_l = hyper_l = FALSE; + + /* go through each of the modifier masks (eg ShiftMask, CapsMask...) */ + for (i = 0; i < NUM_MASKS; ++i) { + /* go through each keycode that is bound to the mask */ + for (j = 0; j < modmap->max_keypermod; ++j) { + KeySym sym; + /* get a keycode that is bound to the mask (i) */ + KeyCode keycode = modmap->modifiermap[i*modmap->max_keypermod + j]; + if (keycode) { + /* go through each keysym bound to the given keycode */ + for (k = 0; k < keysyms_per_keycode; ++k) { + sym = keymap[(keycode-min_keycode) * keysyms_per_keycode + + k]; + if (sym != NoSymbol) { + /* bind the key to the mask (e.g. Alt_L => Mod1Mask) */ + set_modkey_mask(nth_mask(i), sym); + } + } + } + } + } + + /* CapsLock, Shift, and Control are special and hard-coded */ + modkeys_keys[OBT_KEYBOARD_MODKEY_CAPSLOCK] = LockMask; + modkeys_keys[OBT_KEYBOARD_MODKEY_SHIFT] = ShiftMask; + modkeys_keys[OBT_KEYBOARD_MODKEY_CONTROL] = ControlMask; +} + +void obt_keyboard_shutdown(void) +{ + GSList *it; + + XFreeModifiermap(modmap); + modmap = NULL; + XFree(keymap); + keymap = NULL; + for (it = xic_all; it; it = g_slist_next(it)) { + ObtIC* ic = it->data; + if (ic->xic) { + XDestroyIC(ic->xic); + ic->xic = NULL; + } + } + if (xim) XCloseIM(xim); + xim = NULL; + xim_style = 0; + started = FALSE; +} + +void xim_init(void) +{ + GSList *it; + gchar *aname, *aclass; + + aname = g_strdup(g_get_prgname()); + if (!aname) aname = g_strdup("obt"); + + aclass = g_strdup(aname); + if (g_ascii_islower(aclass[0])) + aclass[0] = g_ascii_toupper(aclass[0]); + + xim = XOpenIM(obt_display, NULL, aname, aclass); + + if (!xim) + g_message("Failed to open an Input Method"); + else { + XIMStyles *xim_styles = NULL; + char *r; + + /* get the input method styles */ + r = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); + if (r || !xim_styles) + g_message("Input Method does not support any styles"); + if (xim_styles) { + int i; + + /* find a style that doesnt need preedit or status */ + for (i = 0; i < xim_styles->count_styles; ++i) { + if (xim_styles->supported_styles[i] == + (XIMPreeditNothing | XIMStatusNothing)) + { + xim_style = xim_styles->supported_styles[i]; + break; + } + } + XFree(xim_styles); + } + + if (!xim_style) { + g_message("Input Method does not support a usable style"); + + XCloseIM(xim); + xim = NULL; + } + } + + /* any existing contexts need to be recreated for the new input method */ + for (it = xic_all; it; it = g_slist_next(it)) + obt_keyboard_context_renew(it->data); + + g_free(aclass); + g_free(aname); +} + +ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e) +{ + KeySym sym; + + g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease, + OBT_KEYBOARD_MODKEY_NONE); + + XLookupString(&e->xkey, NULL, 0, &sym, NULL); + + switch (sym) { + case XK_Num_Lock: return OBT_KEYBOARD_MODKEY_NUMLOCK; + case XK_Scroll_Lock: return OBT_KEYBOARD_MODKEY_SCROLLLOCK; + case XK_Caps_Lock: return OBT_KEYBOARD_MODKEY_SHIFT; + case XK_Alt_L: + case XK_Alt_R: return OBT_KEYBOARD_MODKEY_ALT; + case XK_Super_L: + case XK_Super_R: return OBT_KEYBOARD_MODKEY_SUPER; + case XK_Hyper_L: + case XK_Hyper_R: return OBT_KEYBOARD_MODKEY_HYPER; + case XK_Meta_L: + case XK_Meta_R: return OBT_KEYBOARD_MODKEY_META; + case XK_Control_L: + case XK_Control_R: return OBT_KEYBOARD_MODKEY_CONTROL; + case XK_Shift_L: + case XK_Shift_R: return OBT_KEYBOARD_MODKEY_SHIFT; + default: return OBT_KEYBOARD_MODKEY_NONE; + } +} + +guint obt_keyboard_keyevent_to_modmask(XEvent *e) +{ + g_return_val_if_fail(e->type == KeyPress || e->type == KeyRelease, 0); + + return obt_keyboard_modkey_to_modmask(obt_keyboard_keyevent_to_modkey(e)); +} + +guint obt_keyboard_only_modmasks(guint mask) +{ + mask &= ALL_MASKS; + /* strip off these lock keys. they shouldn't affect key bindings */ + mask &= ~LockMask; /* use the LockMask, not what capslock is bound to, + because you could bind it to something else and it + should work as that modifier then. i think capslock + is weird in xkb. */ + mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_NUMLOCK); + mask &= ~obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SCROLLLOCK); + return mask; +} + +guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key) +{ + return modkeys_keys[key]; +} + +static void set_modkey_mask(guchar mask, KeySym sym) +{ + /* find what key this is, and bind it to the mask */ + + if (sym == XK_Num_Lock) + modkeys_keys[OBT_KEYBOARD_MODKEY_NUMLOCK] |= mask; + else if (sym == XK_Scroll_Lock) + modkeys_keys[OBT_KEYBOARD_MODKEY_SCROLLLOCK] |= mask; + + else if (sym == XK_Super_L && super_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask; + else if (sym == XK_Super_L && !super_l) + /* left takes precident over right, so erase any masks the right + key may have set */ + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] = mask, super_l = TRUE; + else if (sym == XK_Super_R && !super_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_SUPER] |= mask; + + else if (sym == XK_Hyper_L && hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask; + else if (sym == XK_Hyper_L && !hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] = mask, hyper_l = TRUE; + else if (sym == XK_Hyper_R && !hyper_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_HYPER] |= mask; + + else if (sym == XK_Alt_L && alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask; + else if (sym == XK_Alt_L && !alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] = mask, alt_l = TRUE; + else if (sym == XK_Alt_R && !alt_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_ALT] |= mask; + + else if (sym == XK_Meta_L && meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask; + else if (sym == XK_Meta_L && !meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] = mask, meta_l = TRUE; + else if (sym == XK_Meta_R && !meta_l) + modkeys_keys[OBT_KEYBOARD_MODKEY_META] |= mask; + + /* CapsLock, Shift, and Control are special and hard-coded */ +} + +KeyCode* obt_keyboard_keysym_to_keycode(KeySym sym) +{ + KeyCode *ret; + gint i, j, n; + + ret = g_new(KeyCode, 1); + n = 0; + ret[n] = 0; + + /* go through each keycode and look for the keysym */ + for (i = min_keycode; i <= max_keycode; ++i) + for (j = 0; j < keysyms_per_keycode; ++j) + if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) { + ret = g_renew(KeyCode, ret, ++n + 1); + ret[n-1] = i; + ret[n] = 0; + } + return ret; +} + +gunichar obt_keyboard_keypress_to_unichar(ObtIC *ic, XEvent *ev) +{ + gunichar unikey = 0; + KeySym sym; + Status status; + gchar *buf, fixbuf[4]; /* 4 is enough for a utf8 char */ + gint len, bufsz; + gboolean got_string = FALSE; + + g_return_val_if_fail(ev->type == KeyPress, 0); + + if (!ic) + g_warning("Using obt_keyboard_keypress_to_unichar() without an " + "Input Context. No i18n support!"); + + if (ic && ic->xic) { + buf = fixbuf; + bufsz = sizeof(fixbuf); + +#ifdef X_HAVE_UTF8_STRING + len = Xutf8LookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, &status); +#else + len = XmbLookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, &status); +#endif + + if (status == XBufferOverflow) { + buf = g_new(char, len); + bufsz = len; + +#ifdef X_HAVE_UTF8_STRING + len = Xutf8LookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, + &status); +#else + len = XmbLookupString(ic->xic, &ev->xkey, buf, bufsz, &sym, + &status); +#endif + } + + if ((status == XLookupChars || status == XLookupBoth)) { + if ((guchar)buf[0] >= 32) { /* not an ascii control character */ +#ifndef X_HAVE_UTF8_STRING + /* convert to utf8 */ + gchar *buf2 = buf; + buf = g_locale_to_utf8(buf2, r, NULL, NULL, NULL); + g_free(buf2); +#endif + + got_string = TRUE; + } + } + else if (status == XLookupKeySym) + /* this key doesn't have a text representation, it is a command + key of some sort */; + else + g_message("Bad keycode lookup. Keysym 0x%x Status: %s\n", + (guint) sym, + (status == XBufferOverflow ? "BufferOverflow" : + status == XLookupNone ? "XLookupNone" : + status == XLookupKeySym ? "XLookupKeySym" : + "Unknown status")); + } + else { + buf = fixbuf; + bufsz = sizeof(fixbuf); + len = XLookupString(&ev->xkey, buf, bufsz, &sym, NULL); + if ((guchar)buf[0] >= 32) /* not an ascii control character */ + got_string = TRUE; + } + + if (got_string) { + gunichar u = g_utf8_get_char_validated(buf, len); + if (u && u != (gunichar)-1 && u != (gunichar)-2) + unikey = u; + } + + if (buf != fixbuf) g_free(buf); + + return unikey; +} + +KeySym obt_keyboard_keypress_to_keysym(XEvent *ev) +{ + KeySym sym; + + g_return_val_if_fail(ev->type == KeyPress, None); + + sym = None; + XLookupString(&ev->xkey, NULL, 0, &sym, NULL); + return sym; +} + +void obt_keyboard_context_renew(ObtIC *ic) +{ + if (xim) { + ic->xic = XCreateIC(xim, + XNInputStyle, xim_style, + XNClientWindow, ic->client, + XNFocusWindow, ic->focus, + NULL); + if (!ic->xic) + g_message("Error creating Input Context for window 0x%x 0x%x\n", + (guint)ic->client, (guint)ic->focus); + } +} + +ObtIC* obt_keyboard_context_new(Window client, Window focus) +{ + ObtIC *ic; + + g_return_val_if_fail(client != None && focus != None, NULL); + + ic = g_slice_new(ObtIC); + ic->ref = 1; + ic->client = client; + ic->focus = focus; + ic->xic = NULL; + + obt_keyboard_context_renew(ic); + xic_all = g_slist_prepend(xic_all, ic); + + return ic; +} + +void obt_keyboard_context_ref(ObtIC *ic) +{ + ++ic->ref; +} + +void obt_keyboard_context_unref(ObtIC *ic) +{ + if (--ic->ref < 1) { + xic_all = g_slist_remove(xic_all, ic); + if (ic->xic) + XDestroyIC(ic->xic); + g_slice_free(ObtIC, ic); + } +} diff --git a/obt/keyboard.h b/obt/keyboard.h new file mode 100644 index 0000000..868cccf --- /dev/null +++ b/obt/keyboard.h @@ -0,0 +1,86 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/keyboard.h for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_keyboard_h +#define __obt_keyboard_h + +#include <glib.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> + +G_BEGIN_DECLS + +/*! These keys are bound to the modifier masks in any fashion, + except for CapsLock, Shift, and Control. */ +typedef enum { + OBT_KEYBOARD_MODKEY_NONE, + OBT_KEYBOARD_MODKEY_CAPSLOCK, + OBT_KEYBOARD_MODKEY_NUMLOCK, + OBT_KEYBOARD_MODKEY_SCROLLLOCK, + OBT_KEYBOARD_MODKEY_SHIFT, + OBT_KEYBOARD_MODKEY_CONTROL, + OBT_KEYBOARD_MODKEY_SUPER, + OBT_KEYBOARD_MODKEY_HYPER, + OBT_KEYBOARD_MODKEY_META, + OBT_KEYBOARD_MODKEY_ALT, + + OBT_KEYBOARD_NUM_MODKEYS +} ObtModkeysKey; + +typedef struct _ObtIC ObtIC; + +void obt_keyboard_reload(void); + +/*! Get the modifier mask(s) for a keyboard event. + (eg. a keycode bound to Alt_L could return a mask of (Mod1Mask | Mask3Mask)) +*/ +guint obt_keyboard_keyevent_to_modmask(XEvent *e); + +/*! Strip off all modifiers except for the modifier keys. This strips stuff + like Button1Mask, and also LockMask, NumlockMask, and ScrolllockMask */ +guint obt_keyboard_only_modmasks(guint mask); + +/*! Get the modifier masks for a modifier key. This includes both the left and + right keys when there are both. */ +guint obt_keyboard_modkey_to_modmask(ObtModkeysKey key); + +/*! Get the modifier key which was pressed or released in a keyboard event */ +ObtModkeysKey obt_keyboard_keyevent_to_modkey(XEvent *e); + +/*! Convert a KeySym to all the KeyCodes which generate it. */ +KeyCode* obt_keyboard_keysym_to_keycode(KeySym sym); + +/*! Translate a KeyPress event to the unicode character it represents */ +gunichar obt_keyboard_keypress_to_unichar(ObtIC *ic, XEvent *ev); + +/*! Translate a KeyPress event to the KeySym that it represents. Use this + for control keys, not for getting text input! */ +KeySym obt_keyboard_keypress_to_keysym(XEvent *ev); + +/*! Create an input context for a window. + @client The top-level client window for the input context. + @focus The subwindow within the client for the input context. +*/ +ObtIC* obt_keyboard_context_new(Window client, Window focus); + +void obt_keyboard_context_ref(ObtIC *ic); +void obt_keyboard_context_unref(ObtIC *ic); + +G_END_DECLS + +#endif /* __obt_keyboard_h */ diff --git a/obt/link.c b/obt/link.c new file mode 100644 index 0000000..9cc2bac --- /dev/null +++ b/obt/link.c @@ -0,0 +1,238 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/link.c for the Openbox window manager + Copyright (c) 2009 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/link.h" +#include "obt/ddparse.h" +#include "obt/paths.h" +#include <glib.h> + +struct _ObtLink { + guint ref; + + ObtLinkType type; + gchar *name; /*!< Specific name for the object (eg Firefox) */ + gboolean display; /*<! When false, do not display this link in menus or + launchers, etc */ + gboolean deleted; /*<! When true, the Link could exist but is deleted + for the current user */ + gchar *generic; /*!< Generic name for the object (eg Web Browser) */ + gchar *comment; /*!< Comment/description to display for the object */ + gchar *icon; /*!< Name/path for an icon for the object */ + guint env_required; /*!< The environments that must be present to use this + link. */ + guint env_restricted; /*!< The environments that must _not_ be present to + use this link. */ + + union _ObtLinkData { + struct _ObtLinkApp { + gchar *exec; /*!< Executable to run for the app */ + gchar *wdir; /*!< Working dir to run the app in */ + gboolean term; /*!< Run the app in a terminal or not */ + ObtLinkAppOpen open; + + gchar **mime; /*!< Mime types the app can open */ + + GQuark *categories; /*!< Array of quarks representing the + application's categories */ + gulong n_categories; /*!< Number of categories for the app */ + + ObtLinkAppStartup startup; + gchar *startup_wmclass; + } app; + struct _ObtLinkLink { + gchar *addr; + } url; + struct _ObtLinkDir { + } dir; + } d; +}; + +ObtLink* obt_link_from_ddfile(const gchar *ddname, GSList *paths, + ObtPaths *p) +{ + ObtLink *link; + GHashTable *groups, *keys; + ObtDDParseGroup *g; + ObtDDParseValue *v; + + /* parse the file, and get a hash table of the groups */ + groups = obt_ddparse_file(ddname, paths); + if (!groups) return NULL; /* parsing failed */ + /* grab the Desktop Entry group */ + g = g_hash_table_lookup(groups, "Desktop Entry"); + g_assert(g != NULL); + /* grab the keys that appeared in the Desktop Entry group */ + keys = obt_ddparse_group_keys(g); + + /* build the ObtLink (we steal all strings from the parser) */ + link = g_slice_new0(ObtLink); + link->ref = 1; + link->display = TRUE; + + v = g_hash_table_lookup(keys, "Type"); + g_assert(v); + link->type = v->value.enumerable; + + if ((v = g_hash_table_lookup(keys, "Hidden"))) + link->deleted = v->value.boolean; + + if ((v = g_hash_table_lookup(keys, "NoDisplay"))) + link->display = !v->value.boolean; + + if ((v = g_hash_table_lookup(keys, "GenericName"))) + link->generic = v->value.string, v->value.string = NULL; + + if ((v = g_hash_table_lookup(keys, "Comment"))) + link->comment = v->value.string, v->value.string = NULL; + + if ((v = g_hash_table_lookup(keys, "Icon"))) + link->icon = v->value.string, v->value.string = NULL; + + if ((v = g_hash_table_lookup(keys, "OnlyShowIn"))) + link->env_required = v->value.environments; + else + link->env_required = 0; + + if ((v = g_hash_table_lookup(keys, "NotShowIn"))) + link->env_restricted = v->value.environments; + else + link->env_restricted = 0; + + /* type-specific keys */ + + if (link->type == OBT_LINK_TYPE_APPLICATION) { + gchar *c; + gboolean percent; + + v = g_hash_table_lookup(keys, "Exec"); + g_assert(v); + link->d.app.exec = v->value.string; + v->value.string = NULL; + + /* parse link->d.app.exec to determine link->d.app.open */ + percent = FALSE; + for (c = link->d.app.exec; *c; ++c) { + if (percent) { + switch (*c) { + case 'f': link->d.app.open = OBT_LINK_APP_SINGLE_LOCAL; break; + case 'F': link->d.app.open = OBT_LINK_APP_MULTI_LOCAL; break; + case 'u': link->d.app.open = OBT_LINK_APP_SINGLE_URL; break; + case 'U': link->d.app.open = OBT_LINK_APP_MULTI_URL; break; + default: percent = FALSE; + } + if (percent) break; /* found f/F/u/U */ + } + else if (*c == '%') percent = TRUE; + } + + if ((v = g_hash_table_lookup(keys, "TryExec"))) { + /* XXX spawn a thread to check TryExec? */ + link->display = link->display && + obt_paths_try_exec(p, v->value.string); + } + + if ((v = g_hash_table_lookup(keys, "Path"))) { + /* steal the string */ + link->d.app.wdir = v->value.string; + v->value.string = NULL; + } + + if ((v = g_hash_table_lookup(keys, "Terminal"))) + link->d.app.term = v->value.boolean; + + if ((v = g_hash_table_lookup(keys, "StartupNotify"))) + link->d.app.startup = v->value.boolean ? + OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT : + OBT_LINK_APP_STARTUP_NO_SUPPORT; + else { + link->d.app.startup = OBT_LINK_APP_STARTUP_LEGACY_SUPPORT; + if ((v = g_hash_table_lookup(keys, "StartupWMClass"))) { + /* steal the string */ + link->d.app.startup_wmclass = v->value.string; + v->value.string = NULL; + } + } + + if ((v = g_hash_table_lookup(keys, "Categories"))) { + gulong i; + gchar *end; + + link->d.app.categories = g_new(GQuark, v->value.strings.n); + link->d.app.n_categories = v->value.strings.n; + + for (i = 0; i < v->value.strings.n; ++i) { + link->d.app.categories[i] = + g_quark_from_string(v->value.strings.a[i]); + c = end = end+1; /* next */ + } + } + + if ((v = g_hash_table_lookup(keys, "MimeType"))) { + /* steal the string array */ + link->d.app.mime = v->value.strings.a; + v->value.strings.a = NULL; + v->value.strings.n = 0; + } + } + else if (link->type == OBT_LINK_TYPE_URL) { + v = g_hash_table_lookup(keys, "URL"); + g_assert(v); + link->d.url.addr = v->value.string; + v->value.string = NULL; + } + + /* destroy the parsing info */ + g_hash_table_destroy(groups); + + return link; +} + +void obt_link_ref(ObtLink *dd) +{ + ++dd->ref; +} + +void obt_link_unref(ObtLink *dd) +{ + if (--dd->ref < 1) { + g_free(dd->name); + g_free(dd->generic); + g_free(dd->comment); + g_free(dd->icon); + if (dd->type == OBT_LINK_TYPE_APPLICATION) { + g_free(dd->d.app.exec); + g_free(dd->d.app.wdir); + g_strfreev(dd->d.app.mime); + g_free(dd->d.app.categories); + g_free(dd->d.app.startup_wmclass); + } + else if (dd->type == OBT_LINK_TYPE_URL) + g_free(dd->d.url.addr); + g_slice_free(ObtLink, dd); + } +} + +const GQuark* obt_link_app_categories(ObtLink *e, gulong *n) +{ + g_return_val_if_fail(e != NULL, NULL); + g_return_val_if_fail(e->type == OBT_LINK_TYPE_APPLICATION, NULL); + g_return_val_if_fail(n != NULL, NULL); + + *n = e->d.app.n_categories; + return e->d.app.categories; +} diff --git a/obt/link.h b/obt/link.h new file mode 100644 index 0000000..9ad86cc --- /dev/null +++ b/obt/link.h @@ -0,0 +1,116 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/link.h for the Openbox window manager + Copyright (c) 2009 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_link_h +#define __obt_link_h + +#include <glib.h> + +G_BEGIN_DECLS + +struct _ObtPaths; + +typedef enum { + OBT_LINK_TYPE_APPLICATION = 1, + OBT_LINK_TYPE_URL = 2, + OBT_LINK_TYPE_DIRECTORY = 3 +} ObtLinkType; + +typedef enum { + OBT_LINK_APP_STARTUP_NO_SUPPORT, + OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT, + OBT_LINK_APP_STARTUP_LEGACY_SUPPORT +} ObtLinkAppStartup; + +/*! These bit flags are environments for links. Some links are used or not + used in various environments. */ +typedef enum { + OBT_LINK_ENV_OPENBOX = 1 << 0, + OBT_LINK_ENV_GNOME = 1 << 1, + OBT_LINK_ENV_KDE = 1 << 2, + OBT_LINK_ENV_LXDE = 1 << 3, + OBT_LINK_ENV_ROX = 1 << 4, + OBT_LINK_ENV_XFCE = 1 << 5, + OBT_LINK_ENV_OLD = 1 << 6 +} ObtLinkEnvFlags; + +typedef enum { + /*! The app can be launched with a single local file */ + OBT_LINK_APP_SINGLE_LOCAL = 1 << 0, + /*! The app can be launched with multiple local files */ + OBT_LINK_APP_MULTI_LOCAL = 1 << 1, + /*! The app can be launched with a single URL */ + OBT_LINK_APP_SINGLE_URL = 1 << 2, + /*! The app can be launched with multiple URLs */ + OBT_LINK_APP_MULTI_URL = 1 << 3 +} ObtLinkAppOpen; + +typedef struct _ObtLink ObtLink; + +ObtLink* obt_link_from_ddfile(const gchar *name, GSList *paths, + struct _ObtPaths *p); + +void obt_link_ref(ObtLink *e); +void obt_link_unref(ObtLink *e); + +/*! Returns TRUE if the file exists but says it should be ignored, with + the Hidden flag. No other functions can be used for the ObtLink + in this case. */ +gboolean obt_link_deleted (ObtLink *e); + +/*! Returns the type of object refered to by the .desktop file. */ +ObtLinkType obt_link_type (ObtLink *e); + +/*! Returns TRUE if the .desktop file should be displayed to users, given the + current environment. If FALSE, the .desktop file should not be showed. + This also uses the TryExec option if it is present. + @env A semicolon-deliminated list of environemnts. Can be one or more of: + GNOME, KDE, ROX, XFCE. Other environments not listed here may also + be supported. This can be null also if not listing any environment. */ +gboolean obt_link_display(ObtLink *e, const gchar *env); + +const gchar* obt_link_name (ObtLink *e); +const gchar* obt_link_generic_name (ObtLink *e); +const gchar* obt_link_comment (ObtLink *e); +/*! Returns the icon for the object referred to by the .desktop file. + Returns either an absolute path, or a string which can be used to find the + icon using the algorithm given by: + http://freedesktop.org/wiki/Specifications/icon-theme-spec?action=show&redirect=Standards/icon-theme-spec +*/ +const gchar* obt_link_icon (ObtLink *e); + +const gchar *obt_link_url_path(ObtLink *e); + +const gchar* obt_link_app_executable (ObtLink *e); +/*! Returns the path in which the application should be run */ +const gchar* obt_link_app_path (ObtLink *e); +gboolean obt_link_app_run_in_terminal (ObtLink *e); +const gchar*const* obt_link_app_mime_types (ObtLink *e); +const GQuark* obt_link_app_categories (ObtLink *e, gulong *n); +/*! Returns a combination of values in the ObtLinkAppOpen enum, + specifying if the application can be launched to open one or more files + and URLs. */ +ObtLinkAppOpen obt_link_app_open(ObtLink *e); + +ObtLinkAppStartup obt_link_app_startup_notify(ObtLink *e); +const gchar* obt_link_app_startup_wmclass(ObtLink *e); + + +G_END_DECLS + +#endif diff --git a/obt/obt-3.5.pc.in b/obt/obt-3.5.pc.in new file mode 100644 index 0000000..8dae581 --- /dev/null +++ b/obt/obt-3.5.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +xcflags=@X_CFLAGS@ +xlibs=@X_LIBS@ + +Name: Obt +Description: Openbox Toolkit Library +Version: @OBT_VERSION@ +Requires: glib-2.0 libxml-2.0 +Libs: -L${libdir} -lobt ${xlibs} +Cflags: -I${includedir}/openbox/@OBT_VERSION@ ${xcflags} diff --git a/obt/paths.c b/obt/paths.c new file mode 100644 index 0000000..d2e230d --- /dev/null +++ b/obt/paths.c @@ -0,0 +1,368 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/paths.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/bsearch.h" +#include "obt/paths.h" +#include "obt/util.h" + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_GRP_H +# include <grp.h> +#endif +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif + +struct _ObtPaths +{ + gint ref; + gchar *config_home; + gchar *data_home; + gchar *cache_home; + GSList *config_dirs; + GSList *data_dirs; + GSList *autostart_dirs; + GSList *exec_dirs; + + uid_t uid; + gid_t *gid; + guint n_gid; +}; + +static gint slist_path_cmp(const gchar *a, const gchar *b) +{ + return strcmp(a, b); +} + +typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data); + +static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func) +{ + g_assert(func); + + if (!data) + return list; + + if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp)) + list = func(list, data); + else + g_free(data); + + return list; +} + +static GSList* split_paths(const gchar *paths) +{ + GSList *list = NULL; + gchar **spl, **it; + + if (!paths) + return NULL; + spl = g_strsplit(paths, ":", -1); + for (it = spl; *it; ++it) + list = slist_path_add(list, *it, (GSListFunc) g_slist_append); + g_free(spl); + return list; +} + +int gid_cmp(const void *va, const void *vb) +{ + const gid_t a = *(const gid_t*)va, b = *(const gid_t*)vb; + return a>b ? 1 : (a == b ? 0 : -1); +} + +static void find_uid_gid(uid_t *u, gid_t **g, guint *n) +{ + struct passwd *pw; + const gchar *name; + struct group *gr; + + *u = getuid(); + pw = getpwuid(*u); + name = pw->pw_name; + + *g = g_new(gid_t, *n=1); + (*g)[0] = getgid(); + + while ((gr = getgrent())) { + if (gr->gr_gid != (*g)[0]) { /* skip the main group */ + gchar **c; + for (c = gr->gr_mem; *c; ++c) + if (strcmp(*c, name) == 0) { + *g = g_renew(gid_t, *g, ++(*n)); /* save the group */ + (*g)[*n-1] = gr->gr_gid; + break; + } + } + } + endgrent(); + + qsort(*g, *n, sizeof(gid_t), gid_cmp); +} + +ObtPaths* obt_paths_new(void) +{ + ObtPaths *p; + const gchar *path; + GSList *it; + + p = g_slice_new0(ObtPaths); + p->ref = 1; + + find_uid_gid(&p->uid, &p->gid, &p->n_gid); + + path = g_getenv("XDG_CONFIG_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->config_home = g_build_filename(path, NULL); + else + p->config_home = g_build_filename(g_get_home_dir(), ".config", NULL); + + path = g_getenv("XDG_DATA_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->data_home = g_build_filename(path, NULL); + else + p->data_home = g_build_filename(g_get_home_dir(), ".local", + "share", NULL); + + path = g_getenv("XDG_CACHE_HOME"); + if (path && path[0] != '\0') /* not unset or empty */ + p->cache_home = g_build_filename(path, NULL); + else + p->cache_home = g_build_filename(g_get_home_dir(), ".cache", NULL); + + path = g_getenv("XDG_CONFIG_DIRS"); + if (path && path[0] != '\0') /* not unset or empty */ + p->config_dirs = split_paths(path); + else { + p->config_dirs = slist_path_add(p->config_dirs, + g_strdup(CONFIGDIR), + (GSListFunc) g_slist_append); + p->config_dirs = slist_path_add(p->config_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "etc", "xdg", NULL), + (GSListFunc) g_slist_append); + } + p->config_dirs = slist_path_add(p->config_dirs, + g_strdup(p->config_home), + (GSListFunc) g_slist_prepend); + + for (it = p->config_dirs; it; it = g_slist_next(it)) { + gchar *const s = g_strdup_printf("%s/autostart", (gchar*)it->data); + p->autostart_dirs = g_slist_append(p->autostart_dirs, s); + } + + path = g_getenv("XDG_DATA_DIRS"); + if (path && path[0] != '\0') /* not unset or empty */ + p->data_dirs = split_paths(path); + else { + p->data_dirs = slist_path_add(p->data_dirs, + g_strdup(DATADIR), + (GSListFunc) g_slist_append); + p->data_dirs = slist_path_add(p->data_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "usr", "local", "share", NULL), + (GSListFunc) g_slist_append); + p->data_dirs = slist_path_add(p->data_dirs, + g_build_filename + (G_DIR_SEPARATOR_S, + "usr", "share", NULL), + (GSListFunc) g_slist_append); + } + p->data_dirs = slist_path_add(p->data_dirs, + g_strdup(p->data_home), + (GSListFunc) g_slist_prepend); + + path = g_getenv("PATH"); + if (path && path[0] != '\0') /* not unset or empty */ + p->exec_dirs = split_paths(path); + else + p->exec_dirs = NULL; + + return p; +} + +void obt_paths_ref(ObtPaths *p) +{ + ++p->ref; +} + +void obt_paths_unref(ObtPaths *p) +{ + if (p && --p->ref == 0) { + GSList *it; + + for (it = p->config_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->config_dirs); + for (it = p->data_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->data_dirs); + for (it = p->autostart_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->autostart_dirs); + for (it = p->exec_dirs; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(p->exec_dirs); + g_free(p->config_home); + g_free(p->data_home); + g_free(p->cache_home); + g_free(p->gid); + + g_slice_free(ObtPaths, p); + } +} + +gchar *obt_paths_expand_tilde(const gchar *f) +{ + gchar *ret; + GRegex *regex; + + if (!f) + return NULL; + + regex = g_regex_new("(?:^|(?<=[ \\t]))~(?=[/ \\t$])", G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL); + ret = g_regex_replace_literal(regex, f, -1, 0, g_get_home_dir(), 0, NULL); + g_regex_unref(regex); + + return ret; +} + +gboolean obt_paths_mkdir(const gchar *path, gint mode) +{ + gboolean ret = TRUE; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(path[0] != '\0', FALSE); + + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) + if (mkdir(path, mode) == -1) + ret = FALSE; + + return ret; +} + +gboolean obt_paths_mkdir_path(const gchar *path, gint mode) +{ + gboolean ret = TRUE; + + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(path[0] == '/', FALSE); + + if (!g_file_test(path, G_FILE_TEST_IS_DIR)) { + gchar *c, *e; + + c = g_strdup(path); + e = c; + while ((e = strchr(e + 1, '/'))) { + *e = '\0'; + if (!(ret = obt_paths_mkdir(c, mode))) + goto parse_mkdir_path_end; + *e = '/'; + } + ret = obt_paths_mkdir(c, mode); + + parse_mkdir_path_end: + g_free(c); + } + + return ret; +} + +const gchar* obt_paths_config_home(ObtPaths *p) +{ + return p->config_home; +} + +const gchar* obt_paths_data_home(ObtPaths *p) +{ + return p->data_home; +} + +const gchar* obt_paths_cache_home(ObtPaths *p) +{ + return p->cache_home; +} + +GSList* obt_paths_config_dirs(ObtPaths *p) +{ + return p->config_dirs; +} + +GSList* obt_paths_data_dirs(ObtPaths *p) +{ + return p->data_dirs; +} + +GSList* obt_paths_autostart_dirs(ObtPaths *p) +{ + return p->autostart_dirs; +} + +static inline gboolean try_exec(const ObtPaths *const p, + const gchar *const path) +{ + struct stat st; + BSEARCH_SETUP(guint); + + if (stat(path, &st) != 0) + return FALSE; + + if (!S_ISREG(st.st_mode)) + return FALSE; + if (st.st_uid == p->uid) + return st.st_mode & S_IXUSR; + BSEARCH(guint, p->gid, 0, p->n_gid, st.st_gid); + if (BSEARCH_FOUND()) + return st.st_mode & S_IXGRP; + return st.st_mode & S_IXOTH; +} + +gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path) +{ + if (path[0] == '/') { + return try_exec(p, path); + } + else { + GSList *it; + + for (it = p->exec_dirs; it; it = g_slist_next(it)) { + gchar *f = g_strdup_printf(it->data, G_DIR_SEPARATOR_S, path); + gboolean e = try_exec(p, f); + g_free(f); + if (e) return TRUE; + } + } + + return FALSE; +} diff --git a/obt/paths.h b/obt/paths.h new file mode 100644 index 0000000..7c43682 --- /dev/null +++ b/obt/paths.h @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/paths.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_paths_h +#define __obt_paths_h + +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _ObtPaths ObtPaths; + +ObtPaths* obt_paths_new(void); +void obt_paths_ref(ObtPaths *p); +void obt_paths_unref(ObtPaths *p); + +const gchar* obt_paths_config_home(ObtPaths *p); +const gchar* obt_paths_data_home(ObtPaths *p); +const gchar* obt_paths_cache_home(ObtPaths *p); +GSList* obt_paths_config_dirs(ObtPaths *p); +GSList* obt_paths_data_dirs(ObtPaths *p); +GSList* obt_paths_autostart_dirs(ObtPaths *p); + +gchar *obt_paths_expand_tilde(const gchar *f); +gboolean obt_paths_mkdir(const gchar *path, gint mode); +gboolean obt_paths_mkdir_path(const gchar *path, gint mode); + +/*! Returns TRUE if the @path points to an executable file. + If the @path is not an absolute path, then it is searched for in $PATH. +*/ +gboolean obt_paths_try_exec(ObtPaths *p, const gchar *path); + +G_END_DECLS + +#endif diff --git a/obt/prop.c b/obt/prop.c new file mode 100644 index 0000000..638373f --- /dev/null +++ b/obt/prop.c @@ -0,0 +1,584 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/prop.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/prop.h" +#include "obt/display.h" + +#include <X11/Xatom.h> +#ifdef HAVE_STRING_H +# include <string.h> +#endif + +Atom prop_atoms[OBT_PROP_NUM_ATOMS]; +gboolean prop_started = FALSE; + +#define CREATE_NAME(var, name) (prop_atoms[OBT_PROP_##var] = \ + XInternAtom((obt_display), (name), FALSE)) +#define CREATE(var) CREATE_NAME(var, #var) +#define CREATE_(var) CREATE_NAME(var, "_" #var) + +void obt_prop_startup(void) +{ + if (prop_started) return; + prop_started = TRUE; + + g_assert(obt_display); + + CREATE(CARDINAL); + CREATE(WINDOW); + CREATE(PIXMAP); + CREATE(ATOM); + CREATE(STRING); + CREATE(COMPOUND_TEXT); + CREATE(UTF8_STRING); + + CREATE(MANAGER); + + CREATE(WM_COLORMAP_WINDOWS); + CREATE(WM_PROTOCOLS); + CREATE(WM_STATE); + CREATE(WM_CHANGE_STATE); + CREATE(WM_DELETE_WINDOW); + CREATE(WM_TAKE_FOCUS); + CREATE(WM_NAME); + CREATE(WM_ICON_NAME); + CREATE(WM_CLASS); + CREATE(WM_WINDOW_ROLE); + CREATE(WM_CLIENT_MACHINE); + CREATE(WM_COMMAND); + CREATE(WM_CLIENT_LEADER); + CREATE(WM_TRANSIENT_FOR); + CREATE_(MOTIF_WM_HINTS); + CREATE_(MOTIF_WM_INFO); + + CREATE(SM_CLIENT_ID); + + CREATE_(NET_WM_FULL_PLACEMENT); + + CREATE_(NET_SUPPORTED); + CREATE_(NET_CLIENT_LIST); + CREATE_(NET_CLIENT_LIST_STACKING); + CREATE_(NET_NUMBER_OF_DESKTOPS); + CREATE_(NET_DESKTOP_GEOMETRY); + CREATE_(NET_DESKTOP_VIEWPORT); + CREATE_(NET_CURRENT_DESKTOP); + CREATE_(NET_DESKTOP_NAMES); + CREATE_(NET_ACTIVE_WINDOW); +/* CREATE_(NET_RESTACK_WINDOW);*/ + CREATE_(NET_WORKAREA); + CREATE_(NET_SUPPORTING_WM_CHECK); + CREATE_(NET_DESKTOP_LAYOUT); + CREATE_(NET_SHOWING_DESKTOP); + + CREATE_(NET_CLOSE_WINDOW); + CREATE_(NET_WM_MOVERESIZE); + CREATE_(NET_MOVERESIZE_WINDOW); + CREATE_(NET_REQUEST_FRAME_EXTENTS); + CREATE_(NET_RESTACK_WINDOW); + + CREATE_(NET_STARTUP_ID); + + CREATE_(NET_WM_NAME); + CREATE_(NET_WM_VISIBLE_NAME); + CREATE_(NET_WM_ICON_NAME); + CREATE_(NET_WM_VISIBLE_ICON_NAME); + CREATE_(NET_WM_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE); + CREATE_(NET_WM_STATE); + CREATE_(NET_WM_STRUT); + CREATE_(NET_WM_STRUT_PARTIAL); + CREATE_(NET_WM_ICON); + CREATE_(NET_WM_ICON_GEOMETRY); + CREATE_(NET_WM_PID); + CREATE_(NET_WM_ALLOWED_ACTIONS); + CREATE_(NET_WM_WINDOW_OPACITY); + CREATE_(NET_WM_USER_TIME); +/* CREATE_(NET_WM_USER_TIME_WINDOW); */ + CREATE_(KDE_NET_WM_FRAME_STRUT); + CREATE_(NET_FRAME_EXTENTS); + + CREATE_(NET_WM_PING); +#ifdef SYNC + CREATE_(NET_WM_SYNC_REQUEST); + CREATE_(NET_WM_SYNC_REQUEST_COUNTER); +#endif + + CREATE_(NET_WM_WINDOW_TYPE_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE_DOCK); + CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR); + CREATE_(NET_WM_WINDOW_TYPE_MENU); + CREATE_(NET_WM_WINDOW_TYPE_UTILITY); + CREATE_(NET_WM_WINDOW_TYPE_SPLASH); + CREATE_(NET_WM_WINDOW_TYPE_DIALOG); + CREATE_(NET_WM_WINDOW_TYPE_NORMAL); + CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU); + + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT] = 0; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP] = 1; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT] = 2; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT] = 3; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT] = 4; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM] = 5; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT] = 6; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT] = 7; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE] = 8; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD] = 9; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD] = 10; + prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_CANCEL] = 11; + + CREATE_(NET_WM_ACTION_MOVE); + CREATE_(NET_WM_ACTION_RESIZE); + CREATE_(NET_WM_ACTION_MINIMIZE); + CREATE_(NET_WM_ACTION_SHADE); + CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ); + CREATE_(NET_WM_ACTION_MAXIMIZE_VERT); + CREATE_(NET_WM_ACTION_FULLSCREEN); + CREATE_(NET_WM_ACTION_CHANGE_DESKTOP); + CREATE_(NET_WM_ACTION_CLOSE); + CREATE_(NET_WM_ACTION_ABOVE); + CREATE_(NET_WM_ACTION_BELOW); + + CREATE_(NET_WM_STATE_MODAL); +/* CREATE_(NET_WM_STATE_STICKY);*/ + CREATE_(NET_WM_STATE_MAXIMIZED_VERT); + CREATE_(NET_WM_STATE_MAXIMIZED_HORZ); + CREATE_(NET_WM_STATE_SHADED); + CREATE_(NET_WM_STATE_SKIP_TASKBAR); + CREATE_(NET_WM_STATE_SKIP_PAGER); + CREATE_(NET_WM_STATE_HIDDEN); + CREATE_(NET_WM_STATE_FULLSCREEN); + CREATE_(NET_WM_STATE_ABOVE); + CREATE_(NET_WM_STATE_BELOW); + CREATE_(NET_WM_STATE_DEMANDS_ATTENTION); + + prop_atoms[OBT_PROP_NET_WM_STATE_ADD] = 1; + prop_atoms[OBT_PROP_NET_WM_STATE_REMOVE] = 0; + prop_atoms[OBT_PROP_NET_WM_STATE_TOGGLE] = 2; + + prop_atoms[OBT_PROP_NET_WM_ORIENTATION_HORZ] = 0; + prop_atoms[OBT_PROP_NET_WM_ORIENTATION_VERT] = 1; + prop_atoms[OBT_PROP_NET_WM_TOPLEFT] = 0; + prop_atoms[OBT_PROP_NET_WM_TOPRIGHT] = 1; + prop_atoms[OBT_PROP_NET_WM_BOTTOMRIGHT] = 2; + prop_atoms[OBT_PROP_NET_WM_BOTTOMLEFT] = 3; + + CREATE_(KDE_WM_CHANGE_STATE); + CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE); + +/* + CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID"); + CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID"); +*/ + + CREATE_(OPENBOX_PID); + CREATE_(OB_THEME); + CREATE_(OB_CONFIG_FILE); + CREATE_(OB_WM_ACTION_UNDECORATE); + CREATE_(OB_WM_STATE_UNDECORATED); + CREATE_(OB_CONTROL); + CREATE_(OB_VERSION); + CREATE_(OB_APP_ROLE); + CREATE_(OB_APP_TITLE); + CREATE_(OB_APP_NAME); + CREATE_(OB_APP_CLASS); + CREATE_(OB_APP_TYPE); +} + +Atom obt_prop_atom(ObtPropAtom a) +{ + g_assert(prop_started); + g_assert(a < OBT_PROP_NUM_ATOMS); + return prop_atoms[a]; +} + +static gboolean get_prealloc(Window win, Atom prop, Atom type, gint size, + guchar *data, gulong num) +{ + gboolean ret = FALSE; + gint res; + guchar *xdata = NULL; + Atom ret_type; + gint ret_size; + gulong ret_items, bytes_left; + glong num32 = 32 / size * num; /* num in 32-bit elements */ + + res = XGetWindowProperty(obt_display, win, prop, 0l, num32, + FALSE, type, &ret_type, &ret_size, + &ret_items, &bytes_left, &xdata); + if (res == Success && ret_items && xdata) { + if (ret_size == size && ret_items >= num) { + guint i; + for (i = 0; i < num; ++i) + switch (size) { + case 8: + data[i] = xdata[i]; + break; + case 16: + ((guint16*)data)[i] = ((gushort*)xdata)[i]; + break; + case 32: + ((guint32*)data)[i] = ((gulong*)xdata)[i]; + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + ret = TRUE; + } + XFree(xdata); + } + return ret; +} + +static gboolean get_all(Window win, Atom prop, Atom type, gint size, + guchar **data, guint *num) +{ + gboolean ret = FALSE; + gint res; + guchar *xdata = NULL; + Atom ret_type; + gint ret_size; + gulong ret_items, bytes_left; + + res = XGetWindowProperty(obt_display, win, prop, 0l, G_MAXLONG, + FALSE, type, &ret_type, &ret_size, + &ret_items, &bytes_left, &xdata); + if (res == Success) { + if (ret_size == size && ret_items > 0) { + guint i; + + *data = g_malloc(ret_items * (size / 8)); + for (i = 0; i < ret_items; ++i) + switch (size) { + case 8: + (*data)[i] = xdata[i]; + break; + case 16: + ((guint16*)*data)[i] = ((gushort*)xdata)[i]; + break; + case 32: + ((guint32*)*data)[i] = ((gulong*)xdata)[i]; + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + *num = ret_items; + ret = TRUE; + } + XFree(xdata); + } + return ret; +} + +/*! Get a text property from a window, and fill out the XTextProperty with it. + @param win The window to read the property from. + @param prop The atom of the property to read off the window. + @param tprop The XTextProperty to fill out. + @param type 0 to get text of any type, or a value from + ObtPropTextType to restrict the value to a specific type. + @return TRUE if the text was read and validated against the @type, and FALSE + otherwise. +*/ +static gboolean get_text_property(Window win, Atom prop, + XTextProperty *tprop, ObtPropTextType type) +{ + if (!(XGetTextProperty(obt_display, win, tprop, prop) && tprop->nitems)) + return FALSE; + if (!type) + return TRUE; /* no type checking */ + switch (type) { + case OBT_PROP_TEXT_STRING: + case OBT_PROP_TEXT_STRING_XPCS: + case OBT_PROP_TEXT_STRING_NO_CC: + return tprop->encoding == OBT_PROP_ATOM(STRING); + case OBT_PROP_TEXT_COMPOUND_TEXT: + return tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT); + case OBT_PROP_TEXT_UTF8_STRING: + return tprop->encoding == OBT_PROP_ATOM(UTF8_STRING); + default: + g_assert_not_reached(); + } +} + +/*! Returns one or more UTF-8 encoded strings from the text property. + @param tprop The XTextProperty to convert into UTF-8 string(s). + @param type The type which specifies the format that the text must meet, or + 0 to allow any valid characters that can be converted to UTF-8 through. + @param max The maximum number of strings to return. -1 to return them all. + @return If max is 1, then this returns a gchar* with the single string. + Otherwise, this returns a gchar** of no more than max strings (or all + strings read, if max is negative). If an error occurs, NULL is returned. + */ +static void* convert_text_property(XTextProperty *tprop, + ObtPropTextType type, gint max) +{ + enum { + LATIN1, + UTF8, + LOCALE + } encoding; + const gboolean return_single = (max == 1); + gboolean ok = FALSE; + gchar **strlist = NULL; + gchar *single[1] = { NULL }; + gchar **retlist = single; /* single is used when max == 1 */ + gint i, n_strs; + + /* Read each string in the text property and store a pointer to it in + retlist. These pointers point into the X data structures directly. + + Then we will convert them to UTF-8, and replace the retlist pointer with + a new one. + */ + if (tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT)) + { + encoding = LOCALE; + ok = (XmbTextPropertyToTextList( + obt_display, tprop, &strlist, &n_strs) == Success); + if (ok) { + if (max >= 0) + n_strs = MIN(max, n_strs); + if (!return_single) + retlist = g_new0(gchar*, n_strs+1); + if (retlist) + for (i = 0; i < n_strs; ++i) + retlist[i] = strlist[i]; + } + } + else if (tprop->encoding == OBT_PROP_ATOM(UTF8_STRING) || + tprop->encoding == OBT_PROP_ATOM(STRING)) + { + gchar *p; /* iterator */ + + if (tprop->encoding == OBT_PROP_ATOM(STRING)) + encoding = LATIN1; + else + encoding = UTF8; + ok = TRUE; + + /* First, count the number of strings. Then make a structure for them + and copy pointers to them into it. */ + p = (gchar*)tprop->value; + n_strs = 0; + while (p < (gchar*)tprop->value + tprop->nitems) { + p += strlen(p) + 1; /* next string */ + ++n_strs; + } + + if (max >= 0) + n_strs = MIN(max, n_strs); + if (!return_single) + retlist = g_new0(gchar*, n_strs+1); + if (retlist) { + p = (gchar*)tprop->value; + for (i = 0; i < n_strs; ++i) { + retlist[i] = p; + p += strlen(p) + 1; /* next string */ + } + } + } + + if (!(ok && retlist)) { + if (strlist) XFreeStringList(strlist); + return NULL; + } + + /* convert each element in retlist to UTF-8, and replace it. */ + for (i = 0; i < n_strs; ++i) { + if (encoding == UTF8) { + const gchar *end; /* the first byte past the valid data */ + + g_utf8_validate(retlist[i], -1, &end); + retlist[i] = g_strndup(retlist[i], end-retlist[i]); + } + else if (encoding == LOCALE) { + gsize nvalid; /* the number of valid bytes at the front of the + string */ + gchar *utf; /* the string converted into utf8 */ + + utf = g_locale_to_utf8(retlist[i], -1, &nvalid, NULL, NULL); + if (!utf) + utf = g_locale_to_utf8(retlist[i], nvalid, NULL, NULL, NULL); + g_assert(utf); + retlist[i] = utf; + } + else { /* encoding == LATIN1 */ + gsize nvalid; /* the number of valid bytes at the front of the + string */ + gchar *utf; /* the string converted into utf8 */ + gchar *p; /* iterator */ + + /* look for invalid characters */ + for (p = retlist[i], nvalid = 0; *p; ++p, ++nvalid) { + /* The only valid control characters are TAB(HT)=9 and + NEWLINE(LF)=10. + This is defined in ICCCM section 2: + http://tronche.com/gui/x/icccm/sec-2.html. + See a definition of the latin1 codepage here: + http://en.wikipedia.org/wiki/ISO/IEC_8859-1. + The above page includes control characters in the table, + which we must explicitly exclude, as the g_convert function + will happily take them. + */ + const register guchar c = (guchar)*p; /* unsigned value at p */ + if ((c < 32 && c != 9 && c != 10) || (c >= 127 && c <= 160)) + break; /* found a control character that isn't allowed */ + + if (type == OBT_PROP_TEXT_STRING_NO_CC && c < 32) + break; /* absolutely no control characters are allowed */ + + if (type == OBT_PROP_TEXT_STRING_XPCS) { + const gboolean valid = ( + (c >= 32 && c < 128) || c == 9 || c == 10); + if (!valid) + break; /* strict whitelisting for XPCS */ + } + } + /* look for invalid latin1 characters */ + utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1", + &nvalid, NULL, NULL); + if (!utf) + utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1", + NULL, NULL, NULL); + g_assert(utf); + retlist[i] = utf; + } + } + + if (strlist) XFreeStringList(strlist); + if (return_single) + return retlist[0]; + else + return retlist; +} + +gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret) +{ + return get_prealloc(win, prop, type, 32, (guchar*)ret, 1); +} + +gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, + guint *nret) +{ + return get_all(win, prop, type, 32, (guchar**)ret, nret); +} + +gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type, + gchar **ret_string) +{ + XTextProperty tprop; + gchar *str; + gboolean ret = FALSE; + + if (get_text_property(win, prop, &tprop, type)) { + str = (gchar*)convert_text_property(&tprop, type, 1); + + if (str) { + *ret_string = str; + ret = TRUE; + } + } + XFree(tprop.value); + return ret; +} + +gboolean obt_prop_get_array_text(Window win, Atom prop, + ObtPropTextType type, + gchar ***ret_strings) +{ + XTextProperty tprop; + gchar **strs; + gboolean ret = FALSE; + + if (get_text_property(win, prop, &tprop, type)) { + strs = (gchar**)convert_text_property(&tprop, type, -1); + + if (strs) { + *ret_strings = strs; + ret = TRUE; + } + } + XFree(tprop.value); + return ret; +} + +void obt_prop_set32(Window win, Atom prop, Atom type, gulong val) +{ + XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace, + (guchar*)&val, 1); +} + +void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val, + guint num) +{ + XChangeProperty(obt_display, win, prop, type, 32, PropModeReplace, + (guchar*)val, num); +} + +void obt_prop_set_text(Window win, Atom prop, const gchar *val) +{ + XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8, + PropModeReplace, (const guchar*)val, strlen(val)); +} + +void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs) +{ + GString *str; + gchar const *const *s; + + str = g_string_sized_new(0); + for (s = strs; *s; ++s) { + str = g_string_append(str, *s); + str = g_string_append_c(str, '\0'); + } + XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8, + PropModeReplace, (guchar*)str->str, str->len); + g_string_free(str, TRUE); +} + +void obt_prop_erase(Window win, Atom prop) +{ + XDeleteProperty(obt_display, win, prop); +} + +void obt_prop_message(gint screen, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask) +{ + obt_prop_message_to(obt_root(screen), about, messagetype, + data0, data1, data2, data3, data4, mask); +} + +void obt_prop_message_to(Window to, Window about, + Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask) +{ + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = messagetype; + ce.xclient.display = obt_display; + ce.xclient.window = about; + ce.xclient.format = 32; + ce.xclient.data.l[0] = data0; + ce.xclient.data.l[1] = data1; + ce.xclient.data.l[2] = data2; + ce.xclient.data.l[3] = data3; + ce.xclient.data.l[4] = data4; + XSendEvent(obt_display, to, FALSE, mask, &ce); +} diff --git a/obt/prop.h b/obt/prop.h new file mode 100644 index 0000000..b30232e --- /dev/null +++ b/obt/prop.h @@ -0,0 +1,322 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/prop.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_prop_h +#define __obt_prop_h + +#include <X11/Xlib.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef enum { + /* types */ + OBT_PROP_CARDINAL, /*!< The atom which represents the Cardinal data type */ + OBT_PROP_WINDOW, /*!< The atom which represents window ids */ + OBT_PROP_PIXMAP, /*!< The atom which represents pixmap ids */ + OBT_PROP_ATOM, /*!< The atom which represents atom values */ + OBT_PROP_STRING, /*!< The atom which represents latin1 strings */ + OBT_PROP_COMPOUND_TEXT, /*!< The atom which represents locale-encoded + strings */ + OBT_PROP_UTF8_STRING, /*!< The atom which represents utf8-encoded strings*/ + + /* selection stuff */ + OBT_PROP_MANAGER, + + /* window hints */ + OBT_PROP_WM_COLORMAP_WINDOWS, + OBT_PROP_WM_PROTOCOLS, + OBT_PROP_WM_STATE, + OBT_PROP_WM_DELETE_WINDOW, + OBT_PROP_WM_TAKE_FOCUS, + OBT_PROP_WM_CHANGE_STATE, + OBT_PROP_WM_NAME, + OBT_PROP_WM_ICON_NAME, + OBT_PROP_WM_CLASS, + OBT_PROP_WM_WINDOW_ROLE, + OBT_PROP_WM_CLIENT_MACHINE, + OBT_PROP_WM_COMMAND, + OBT_PROP_WM_CLIENT_LEADER, + OBT_PROP_WM_TRANSIENT_FOR, + OBT_PROP_MOTIF_WM_HINTS, + OBT_PROP_MOTIF_WM_INFO, + + /* SM atoms */ + OBT_PROP_SM_CLIENT_ID, + + /* NETWM atoms */ + + /* Atoms that are used inside messages - these don't go in net_supported */ + + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT, + OBT_PROP_NET_WM_MOVERESIZE_MOVE, + OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD, + OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD, + OBT_PROP_NET_WM_MOVERESIZE_CANCEL, + + OBT_PROP_NET_WM_STATE_ADD, + OBT_PROP_NET_WM_STATE_REMOVE, + OBT_PROP_NET_WM_STATE_TOGGLE, + + OBT_PROP_NET_WM_ORIENTATION_HORZ, + OBT_PROP_NET_WM_ORIENTATION_VERT, + OBT_PROP_NET_WM_TOPLEFT, + OBT_PROP_NET_WM_TOPRIGHT, + OBT_PROP_NET_WM_BOTTOMRIGHT, + OBT_PROP_NET_WM_BOTTOMLEFT, + + OBT_PROP_NET_WM_WINDOW_TYPE_POPUP_MENU, + + OBT_PROP_PRIVATE_PADDING1, + OBT_PROP_PRIVATE_PADDING2, + OBT_PROP_PRIVATE_PADDING3, + OBT_PROP_PRIVATE_PADDING4, + OBT_PROP_PRIVATE_PADDING5, + OBT_PROP_PRIVATE_PADDING6, + OBT_PROP_PRIVATE_PADDING7, + OBT_PROP_PRIVATE_PADDING8, + OBT_PROP_PRIVATE_PADDING9, + OBT_PROP_PRIVATE_PADDING10, + OBT_PROP_PRIVATE_PADDING11, + OBT_PROP_PRIVATE_PADDING12, + + /* Everything below here must go in net_supported on the root window */ + + /* root window properties */ + OBT_PROP_NET_SUPPORTED, + OBT_PROP_NET_CLIENT_LIST, + OBT_PROP_NET_CLIENT_LIST_STACKING, + OBT_PROP_NET_NUMBER_OF_DESKTOPS, + OBT_PROP_NET_DESKTOP_GEOMETRY, + OBT_PROP_NET_DESKTOP_VIEWPORT, + OBT_PROP_NET_CURRENT_DESKTOP, + OBT_PROP_NET_DESKTOP_NAMES, + OBT_PROP_NET_ACTIVE_WINDOW, +/* Atom net_restack_window;*/ + OBT_PROP_NET_WORKAREA, + OBT_PROP_NET_SUPPORTING_WM_CHECK, + OBT_PROP_NET_DESKTOP_LAYOUT, + OBT_PROP_NET_SHOWING_DESKTOP, + + /* root window messages */ + OBT_PROP_NET_CLOSE_WINDOW, + OBT_PROP_NET_WM_MOVERESIZE, + OBT_PROP_NET_MOVERESIZE_WINDOW, + OBT_PROP_NET_REQUEST_FRAME_EXTENTS, + OBT_PROP_NET_RESTACK_WINDOW, + + /* helpful hints to apps that aren't used for anything */ + OBT_PROP_NET_WM_FULL_PLACEMENT, + + /* startup-notification extension */ + OBT_PROP_NET_STARTUP_ID, + + /* application window properties */ + OBT_PROP_NET_WM_NAME, + OBT_PROP_NET_WM_VISIBLE_NAME, + OBT_PROP_NET_WM_ICON_NAME, + OBT_PROP_NET_WM_VISIBLE_ICON_NAME, + OBT_PROP_NET_WM_DESKTOP, + OBT_PROP_NET_WM_WINDOW_TYPE, + OBT_PROP_NET_WM_STATE, + OBT_PROP_NET_WM_STRUT, + OBT_PROP_NET_WM_STRUT_PARTIAL, + OBT_PROP_NET_WM_ICON, + OBT_PROP_NET_WM_ICON_GEOMETRY, + OBT_PROP_NET_WM_PID, + OBT_PROP_NET_WM_ALLOWED_ACTIONS, + OBT_PROP_NET_WM_WINDOW_OPACITY, + OBT_PROP_NET_WM_USER_TIME, +/* OBT_PROP_NET_WM_USER_TIME_WINDOW, */ + OBT_PROP_NET_FRAME_EXTENTS, + + /* application protocols */ + OBT_PROP_NET_WM_PING, +#ifdef SYNC + OBT_PROP_NET_WM_SYNC_REQUEST, + OBT_PROP_NET_WM_SYNC_REQUEST_COUNTER, +#endif + + OBT_PROP_NET_WM_WINDOW_TYPE_DESKTOP, + OBT_PROP_NET_WM_WINDOW_TYPE_DOCK, + OBT_PROP_NET_WM_WINDOW_TYPE_TOOLBAR, + OBT_PROP_NET_WM_WINDOW_TYPE_MENU, + OBT_PROP_NET_WM_WINDOW_TYPE_UTILITY, + OBT_PROP_NET_WM_WINDOW_TYPE_SPLASH, + OBT_PROP_NET_WM_WINDOW_TYPE_DIALOG, + OBT_PROP_NET_WM_WINDOW_TYPE_NORMAL, + + OBT_PROP_NET_WM_ACTION_MOVE, + OBT_PROP_NET_WM_ACTION_RESIZE, + OBT_PROP_NET_WM_ACTION_MINIMIZE, + OBT_PROP_NET_WM_ACTION_SHADE, +/* OBT_PROP_NET_WM_ACTION_STICK,*/ + OBT_PROP_NET_WM_ACTION_MAXIMIZE_HORZ, + OBT_PROP_NET_WM_ACTION_MAXIMIZE_VERT, + OBT_PROP_NET_WM_ACTION_FULLSCREEN, + OBT_PROP_NET_WM_ACTION_CHANGE_DESKTOP, + OBT_PROP_NET_WM_ACTION_CLOSE, + OBT_PROP_NET_WM_ACTION_ABOVE, + OBT_PROP_NET_WM_ACTION_BELOW, + + OBT_PROP_NET_WM_STATE_MODAL, +/* OBT_PROP_NET_WM_STATE_STICKY,*/ + OBT_PROP_NET_WM_STATE_MAXIMIZED_VERT, + OBT_PROP_NET_WM_STATE_MAXIMIZED_HORZ, + OBT_PROP_NET_WM_STATE_SHADED, + OBT_PROP_NET_WM_STATE_SKIP_TASKBAR, + OBT_PROP_NET_WM_STATE_SKIP_PAGER, + OBT_PROP_NET_WM_STATE_HIDDEN, + OBT_PROP_NET_WM_STATE_FULLSCREEN, + OBT_PROP_NET_WM_STATE_ABOVE, + OBT_PROP_NET_WM_STATE_BELOW, + OBT_PROP_NET_WM_STATE_DEMANDS_ATTENTION, + + /* KDE atoms */ + + OBT_PROP_KDE_WM_CHANGE_STATE, + OBT_PROP_KDE_NET_WM_FRAME_STRUT, + OBT_PROP_KDE_NET_WM_WINDOW_TYPE_OVERRIDE, + +/* + OBT_PROP_ROOTPMAPID, + OBT_PROP_ESETROOTID, +*/ + + /* Openbox specific atoms */ + + OBT_PROP_OB_WM_ACTION_UNDECORATE, + OBT_PROP_OB_WM_STATE_UNDECORATED, + OBT_PROP_OPENBOX_PID, /* this is depreecated in favour of ob_control */ + OBT_PROP_OB_THEME, + OBT_PROP_OB_CONFIG_FILE, + OBT_PROP_OB_CONTROL, + OBT_PROP_OB_VERSION, + OBT_PROP_OB_APP_ROLE, + OBT_PROP_OB_APP_TITLE, + OBT_PROP_OB_APP_NAME, + OBT_PROP_OB_APP_CLASS, + OBT_PROP_OB_APP_TYPE, + + OBT_PROP_NUM_ATOMS +} ObtPropAtom; + +Atom obt_prop_atom(ObtPropAtom a); + +typedef enum { + /*! STRING is latin1 encoded. It cannot contain control characters except + for tab and line-feed. */ + OBT_PROP_TEXT_STRING = 1, + /*! STRING text restricted to characters in the X Portable Character + Set, which is a subset of latin1. + http://static.cray-cyber.org/Documentation/NEC_SX_R10_1/G1AE02E/CHAP1.HTML + */ + OBT_PROP_TEXT_STRING_XPCS = 2, + /*! STRING text restricted to not allow any control characters to be + present. */ + OBT_PROP_TEXT_STRING_NO_CC = 3, + /* COMPOUND_TEXT is encoded in the current locale setting. */ + OBT_PROP_TEXT_COMPOUND_TEXT = 4, + /* UTF8_STRING is encoded as utf-8. */ + OBT_PROP_TEXT_UTF8_STRING = 5, +} ObtPropTextType; + +gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret); +gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, + guint *nret); + +gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type, + gchar **ret); +gboolean obt_prop_get_array_text(Window win, Atom prop, + ObtPropTextType type, + gchar ***ret); + +void obt_prop_set32(Window win, Atom prop, Atom type, gulong val); +void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val, + guint num); +void obt_prop_set_text(Window win, Atom prop, const gchar *str); +void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs); + +void obt_prop_erase(Window win, Atom prop); + +void obt_prop_message(gint screen, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask); +void obt_prop_message_to(Window to, Window about, Atom messagetype, + glong data0, glong data1, glong data2, glong data3, + glong data4, glong mask); + +#define OBT_PROP_ATOM(prop) obt_prop_atom(OBT_PROP_##prop) + +#define OBT_PROP_GET32(win, prop, type, ret) \ + (obt_prop_get32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), ret)) +#define OBT_PROP_GETA32(win, prop, type, ret, nret) \ + (obt_prop_get_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \ + ret, nret)) +#define OBT_PROP_GETS(win, prop, ret) \ + (obt_prop_get_text(win, OBT_PROP_ATOM(prop), 0, ret)) +#define OBT_PROP_GETSS(win, prop, ret) \ + (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), 0, ret)) + +#define OBT_PROP_GETS_TYPE(win, prop, type, ret) \ + (obt_prop_get_text(win, OBT_PROP_ATOM(prop), OBT_PROP_TEXT_##type, ret)) +#define OBT_PROP_GETSS_TYPE(win, prop, type, ret) \ + (obt_prop_get_array_text(win, OBT_PROP_ATOM(prop), \ + OBT_PROP_TEXT_##type, ret)) + +#define OBT_PROP_GETS_UTF8(win, prop, ret) \ + OBT_PROP_GETS_TYPE(win, prop, UTF8_STRING, ret) +#define OBT_PROP_GETSS_UTF8(win, prop, ret) \ + OBT_PROP_GETSS_TYPE(win, prop, UTF8_STRING, ret) +#define OBT_PROP_GETS_XPCS(win, prop, ret) \ + OBT_PROP_GETS_TYPE(win, prop, STRING_XPCS, ret) + +#define OBT_PROP_SET32(win, prop, type, val) \ + (obt_prop_set32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), val)) +#define OBT_PROP_SETA32(win, prop, type, val, num) \ + (obt_prop_set_array32(win, OBT_PROP_ATOM(prop), OBT_PROP_ATOM(type), \ + val, num)) +#define OBT_PROP_SETS(win, prop, val) \ + (obt_prop_set_text(win, OBT_PROP_ATOM(prop), val)) +#define OBT_PROP_SETSS(win, prop, strs) \ + (obt_prop_set_array_text(win, OBT_PROP_ATOM(prop), strs)) + +#define OBT_PROP_ERASE(win, prop) (obt_prop_erase(win, OBT_PROP_ATOM(prop))) + +#define OBT_PROP_MSG(screen, about, msgtype, data0, data1, data2, data3, \ + data4) \ + (obt_prop_message(screen, about, OBT_PROP_ATOM(msgtype), \ + data0, data1, data2, data3, data4, \ + SubstructureNotifyMask | SubstructureRedirectMask)) + +#define OBT_PROP_MSG_TO(to, about, msgtype, data0, data1, data2, data3, \ + data4, mask) \ + (obt_prop_message_to(to, about, OBT_PROP_ATOM(msgtype), \ + data0, data1, data2, data3, data4, mask)) + +G_END_DECLS + +#endif /* __obt_prop_h */ diff --git a/obt/signal.c b/obt/signal.c new file mode 100644 index 0000000..0e483b7 --- /dev/null +++ b/obt/signal.c @@ -0,0 +1,290 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/signal.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "signal.h" + +#ifdef HAVE_STDIO_H +#include <stdio.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +typedef struct _ObtSignalCallback ObtSignalCallback; + +struct _ObtSignalCallback +{ + ObtSignalHandler func; + gpointer data; +}; + +static gboolean signal_prepare(GSource *source, gint *timeout); +static gboolean signal_check(GSource *source); +static gboolean signal_occurred(GSource *source, GSourceFunc callback, + gpointer data); +static void sighandler(gint sig); + +/* this should be more than the number of possible signals on any + architecture... */ +#define NUM_SIGNALS 99 + +/* a set of all possible signals */ +static sigset_t all_signals_set; + +/* keep track of what signals have a signal handler installed, and remember + the action we replaced when installing it for when we clean up */ +static struct { + guint installed; /* a ref count */ + struct sigaction oldact; +} all_signals[NUM_SIGNALS]; + +/* signals which cause a core dump, these can't be used for callbacks */ +static const gint core_signals[] = +{ + SIGABRT, + SIGSEGV, + SIGFPE, + SIGILL, + SIGQUIT, + SIGTRAP, + SIGSYS, + SIGBUS, + SIGXCPU, + SIGXFSZ +}; +#define NUM_CORE_SIGNALS (gint)(sizeof(core_signals) / sizeof(core_signals[0])) + +static GSourceFuncs source_funcs = { + signal_prepare, + signal_check, + signal_occurred, + NULL +}; +static GSource *gsource = NULL; +static guint listeners = 0; /* a ref count for the signal listener */ +static gboolean signal_fired; +guint signals_fired[NUM_SIGNALS]; +GSList *callbacks[NUM_SIGNALS]; + +void obt_signal_listen(void) +{ + if (!listeners) { + guint i; + struct sigaction action; + sigset_t sigset; + + /* initialize the all_signals_set */ + sigfillset(&all_signals_set); + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + /* always grab all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + /* SIGABRT is curiously not grabbed here!! that's because when we + get one of the core_signals, we use abort() to dump the core. + And having the abort() only go back to our signal handler again + is less than optimal */ + if (core_signals[i] != SIGABRT) { + sigaction(core_signals[i], &action, + &all_signals[core_signals[i]].oldact); + all_signals[core_signals[i]].installed++; + } + } + + gsource = g_source_new(&source_funcs, sizeof(GSource)); + g_source_set_priority(gsource, G_PRIORITY_HIGH); + + g_source_attach(gsource, NULL); + } + + ++listeners; +} + +void obt_signal_stop(void) +{ + --listeners; + + if (!listeners) { + gint i; + GSList *it, *next; + + g_source_unref(gsource); + gsource = NULL; + + /* remove user defined signal handlers */ + for (i = 0; i < NUM_SIGNALS; ++i) + for (it = callbacks[i]; it; it = next) { + ObtSignalCallback *cb = it->data; + next = g_slist_next(it); + obt_signal_remove_callback(i, cb->func); + } + + /* release all the signals that cause core dumps */ + for (i = 0; i < NUM_CORE_SIGNALS; ++i) { + if (all_signals[core_signals[i]].installed) { + sigaction(core_signals[i], + &all_signals[core_signals[i]].oldact, NULL); + all_signals[core_signals[i]].installed--; + } + } + +#ifdef DEBUG + for (i = 0; i < NUM_SIGNALS; ++i) + g_assert(all_signals[i].installed == 0); +#endif + } +} + +void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data) +{ + ObtSignalCallback *cb; + gint i; + + g_return_if_fail(func != NULL); + g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS); + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + g_return_if_fail(sig != core_signals[i]); + + cb = g_slice_new(ObtSignalCallback); + cb->func = func; + cb->data = data; + callbacks[sig] = g_slist_prepend(callbacks[sig], cb); + + /* install the signal handler */ + if (!all_signals[sig].installed) { + struct sigaction action; + sigset_t sigset; + + sigemptyset(&sigset); + action.sa_handler = sighandler; + action.sa_mask = sigset; + action.sa_flags = SA_NOCLDSTOP; + + sigaction(sig, &action, &all_signals[sig].oldact); + } + + all_signals[sig].installed++; +} + +void obt_signal_remove_callback(gint sig, ObtSignalHandler func) +{ + GSList *it; + gint i; + + g_return_if_fail(func != NULL); + g_return_if_fail(sig >= 0 && sig <= NUM_SIGNALS); + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + g_return_if_fail(sig != core_signals[i]); + + for (it = callbacks[sig]; it; it = g_slist_next(it)) { + ObtSignalCallback *cb = it->data; + if (cb->func == func) { + g_assert(all_signals[sig].installed > 0); + + callbacks[sig] = g_slist_delete_link(callbacks[sig], it); + g_slice_free(ObtSignalCallback, cb); + + /* uninstall the signal handler */ + all_signals[sig].installed--; + if (!all_signals[sig].installed) + sigaction(sig, &all_signals[sig].oldact, NULL); + break; + } + } +} + +static gboolean signal_prepare(GSource *source, gint *timeout) +{ + *timeout = -1; + return signal_fired; +} + +static gboolean signal_check(GSource *source) +{ + return signal_fired; +} + +static gboolean signal_occurred(GSource *source, GSourceFunc callback, + gpointer data) +{ + guint i; + sigset_t oldset; + guint fired[NUM_SIGNALS]; + + /* block signals so that we can do this without the data changing + on us */ + sigprocmask(SIG_SETMASK, &all_signals_set, &oldset); + + /* make a copy of the signals that fired */ + for (i = 0; i < NUM_SIGNALS; ++i) { + fired[i] = signals_fired[i]; + signals_fired[i] = 0; + } + signal_fired = FALSE; + + sigprocmask(SIG_SETMASK, &oldset, NULL); + + /* call the signal callbacks for the signals */ + for (i = 0; i < NUM_SIGNALS; ++i) { + while (fired[i]) { + GSList *it; + for (it = callbacks[i]; it; it = g_slist_next(it)) { + const ObtSignalCallback *cb = it->data; + cb->func(i, cb->data); + } + --fired[i]; + } + } + + return TRUE; /* repeat */ +} + +static void sighandler(gint sig) +{ + guint i; + + g_return_if_fail(sig < NUM_SIGNALS); + + for (i = 0; i < NUM_CORE_SIGNALS; ++i) + if (sig == core_signals[i]) { + /* XXX special case for signals that default to core dump. + but throw some helpful output here... */ + + fprintf(stderr, "How are you gentlemen? All your base are" + " belong to us. (Openbox received signal %d)\n", sig); + + /* die with a core dump */ + abort(); + } + + signal_fired = TRUE; + ++signals_fired[sig]; + + /* i don't think we want to modify the GMainContext inside a signal + handler, so use a GSource instead of an idle func to call back + to the application */ +} diff --git a/obt/signal.h b/obt/signal.h new file mode 100644 index 0000000..ec712b2 --- /dev/null +++ b/obt/signal.h @@ -0,0 +1,46 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/signal.h for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_signal_h +#define __obt_signal_h + +#include <glib.h> + +G_BEGIN_DECLS + +typedef void (*ObtSignalHandler)(gint signal, gpointer data); + +/*! Listen for signals and report them through the default GMainContext within + main program thread (except signals that require immediate exit). + The app should not set its own signal handler function or it will interfere + with this one. */ +void obt_signal_listen(void); +/*! Stop listening to signals and clean up */ +void obt_signal_stop(void); + +/*! Adds a signal handler for a signal. The callback function @func will be + called when the signal @sig is fired. @sig must not be a signal that + would cause the core to dump as these are handled internally. + */ +void obt_signal_add_callback(gint sig, ObtSignalHandler func, gpointer data); +/*! Removes the most recently added callback with the given function. */ +void obt_signal_remove_callback(gint sig, ObtSignalHandler func); + +G_END_DECLS + +#endif diff --git a/obt/tests/bstest.c b/obt/tests/bstest.c new file mode 100755 index 0000000..7581855 --- /dev/null +++ b/obt/tests/bstest.c @@ -0,0 +1,58 @@ +#/* +#!/bin/sh +#*/ +#if 0 +gcc -O0 -o ./bstest `pkg-config --cflags --libs obt-3.5` bstest.c && \ +./bstest +exit +#endif + +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + bstest.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "../bsearch.h" +#include <stdio.h> + +int main() { + int ar[] = { + 2, 4, 5, 7, 12, 34, 45, 56, 57, 67, 67, 68, 68, 69, 70, 71, 89, 100 }; + int n = sizeof(ar)/sizeof(ar[0]); + BSEARCH_SETUP(int); + BSEARCH(int, ar, 0, n, 1); + g_assert(!!BSEARCH_FOUND() == FALSE); + BSEARCH(int, ar, 0, n, 0); + g_assert(!!BSEARCH_FOUND() == FALSE); + BSEARCH(int, ar, 0, n, 2); + g_assert(!!BSEARCH_FOUND() == TRUE); + g_assert(BSEARCH_AT() == 0); + BSEARCH(int, ar, 0, n, 58); + g_assert(!!BSEARCH_FOUND() == FALSE); + BSEARCH(int, ar, 0, n, 57); + g_assert(!!BSEARCH_FOUND() == TRUE); + g_assert(BSEARCH_AT() == 8); + BSEARCH(int, ar, 0, n, 55); + g_assert(!!BSEARCH_FOUND() == FALSE); + BSEARCH(int, ar, 0, n, 99); + g_assert(!!BSEARCH_FOUND() == FALSE); + BSEARCH(int, ar, 0, n, 100); + g_assert(!!BSEARCH_FOUND() == TRUE); + g_assert(BSEARCH_AT() == 17); + BSEARCH(int, ar, 0, n, 101); + g_assert(!!BSEARCH_FOUND() == FALSE); + g_print("ok\n"); +} diff --git a/obt/tests/ddtest.c b/obt/tests/ddtest.c new file mode 100755 index 0000000..69a9e1c --- /dev/null +++ b/obt/tests/ddtest.c @@ -0,0 +1,61 @@ +#/* +#!/bin/sh +#*/ +#if 0 +gcc -O0 -o ./ddtest `pkg-config --cflags --libs obt-3.5` ddtest.c && \ +./ddtest +exit +#endif + +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + ddtest.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "obt/paths.h" +#include "obt/link.h" +#include <glib.h> + +gint main(int argc, char **argv) +{ + ObtPaths *obtpaths; + ObtLink *dd; + gchar *id; + + if (argc < 2) { + g_print("pass path to .desktop\n"); + return 1; + } + + obtpaths = obt_paths_new(); + dd = obt_link_from_ddfile(argv[1], obtpaths, "et", NULL, NULL); + obt_paths_unref(obtpaths); + if (dd) { + g_print("Success\n"); + { + gulong i, n; + const GQuark *c = obt_link_app_categories(dd, &n); + for (i = 0; i < n; ++i) + g_print("Category: %s\n", + g_quark_to_string(c[i])); + } + obt_link_unref(dd); + } + return 0; +} diff --git a/obt/tests/ddtest.desktop b/obt/tests/ddtest.desktop new file mode 100644 index 0000000..16d76aa --- /dev/null +++ b/obt/tests/ddtest.desktop @@ -0,0 +1,15 @@ +[Desktop Entry] +test= +test2 +foo = +#hi +gewh= yuhself +a-r950 = tek;la; fi +hi=bye + +you=yeh +hi=double +Type=Application +Exec=foo +Name=myname +Categories=one;two;;three diff --git a/obt/tests/linktest.c b/obt/tests/linktest.c new file mode 100755 index 0000000..022ba35 --- /dev/null +++ b/obt/tests/linktest.c @@ -0,0 +1,48 @@ +#/* +#!/bin/sh +#*/ +#if 0 +gcc -O0 -o ./linktest `pkg-config --cflags --libs obt-3.5` linktest.c && \ +./linktest +exit +#endif + +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + linktest.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/linkbase.h" +#include "obt/paths.h" +#include <glib.h> +#include <locale.h> + +gint main() +{ + ObtLinkBase *base; + ObtPaths *paths; + GMainLoop *loop; + + paths = obt_paths_new(); + base = obt_linkbase_new(paths, setlocale(LC_MESSAGES, "")); + printf("done\n"); + return 0; + + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + + return 0; +} diff --git a/obt/tests/watchtest.c b/obt/tests/watchtest.c new file mode 100755 index 0000000..9ebdbae --- /dev/null +++ b/obt/tests/watchtest.c @@ -0,0 +1,50 @@ +#/* +#!/bin/sh +#*/ +#if 0 +gcc -O0 -o ./watchtest `pkg-config --cflags --libs obt-3.5` watchtest.c && \ +./watchtest +exit +#endif + +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + watchtest.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/watch.h" +#include <glib.h> + +void func(ObtWatch *w, const gchar *base_path, + const gchar *subpath, ObtWatchNotifyType type, + gpointer data) +{ + g_print("base path: %s subpath: %s type=%d\n", base_path, subpath, type); +} + +gint main() +{ + ObtWatch *watch; + GMainLoop *loop; + + watch = obt_watch_new(); + obt_watch_add(watch, "/tmp/a", FALSE, func, NULL); + + loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + + return 0; +} diff --git a/obt/util.h b/obt/util.h new file mode 100644 index 0000000..ff44d36 --- /dev/null +++ b/obt/util.h @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/util.h for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_util_h +#define __obt_util_h + +#include <glib.h> + +#ifdef HAVE_STRING_H +# include <string.h> /* for memset() */ +#endif + +G_BEGIN_DECLS + +/* Util funcs */ +#define obt_free g_free +#define obt_free0(p, type, num) memset((p), 0, sizeof(type) * (num)), g_free(p) + +G_END_DECLS + + +#endif /*__obt_util_h*/ diff --git a/obt/version.h.in b/obt/version.h.in new file mode 100644 index 0000000..8adfcf8 --- /dev/null +++ b/obt/version.h.in @@ -0,0 +1,15 @@ +#ifndef obt__version_h +#define obt__version_h + +#define OBT_MAJOR_VERSION @OBT_MAJOR_VERSION@ +#define OBT_MINOR_VERSION @OBT_MINOR_VERSION@ +#define OBT_MICRO_VERSION @OBT_MICRO_VERSION@ +#define OBT_VERSION OBT_MAJOR_VERSION.OBT_MINOR_VERSION.OBT_MICRO_VERSION + +#define OBT_CHECK_VERSION(major,minor,micro) \ + (OBT_MAJOR_VERSION > (major) || \ + (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION > (minor)) || \ + (OBT_MAJOR_VERSION == (major) && OBT_MINOR_VERSION == (minor) && \ + OBT_MICRO_VERSION >= (micro))) + +#endif diff --git a/obt/xml.c b/obt/xml.c new file mode 100644 index 0000000..c872912 --- /dev/null +++ b/obt/xml.c @@ -0,0 +1,436 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/xml.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/xml.h" +#include "obt/paths.h" + +#include <libxml/xinclude.h> +#include <glib.h> + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +struct Callback { + gchar *tag; + ObtXmlCallback func; + gpointer data; +}; + +struct _ObtXmlInst { + gint ref; + ObtPaths *xdg_paths; + GHashTable *callbacks; + xmlDocPtr doc; + xmlNodePtr root; + gchar *path; +}; + +static void destfunc(struct Callback *c) +{ + g_free(c->tag); + g_slice_free(struct Callback, c); +} + +ObtXmlInst* obt_xml_instance_new(void) +{ + ObtXmlInst *i = g_slice_new(ObtXmlInst); + i->ref = 1; + i->xdg_paths = obt_paths_new(); + i->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)destfunc); + i->doc = NULL; + i->root = NULL; + i->path = NULL; + return i; +} + +void obt_xml_instance_ref(ObtXmlInst *i) +{ + ++i->ref; +} + +void obt_xml_instance_unref(ObtXmlInst *i) +{ + if (i && --i->ref == 0) { + obt_paths_unref(i->xdg_paths); + g_hash_table_destroy(i->callbacks); + g_slice_free(ObtXmlInst, i); + } +} + +xmlDocPtr obt_xml_doc(ObtXmlInst *i) +{ + g_assert(i->doc); /* a doc is open? */ + return i->doc; +} + +xmlNodePtr obt_xml_root(ObtXmlInst *i) +{ + g_assert(i->doc); /* a doc is open? */ + return i->root; +} + +void obt_xml_register(ObtXmlInst *i, const gchar *tag, + ObtXmlCallback func, gpointer data) +{ + struct Callback *c; + + if (g_hash_table_lookup(i->callbacks, tag)) { + g_error("Tag '%s' already registered", tag); + return; + } + + c = g_slice_new(struct Callback); + c->tag = g_strdup(tag); + c->func = func; + c->data = data; + g_hash_table_insert(i->callbacks, c->tag, c); +} + +static gboolean load_file(ObtXmlInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node, + GSList *paths) +{ + GSList *it; + gboolean r = FALSE; + + g_assert(i->doc == NULL); /* another doc isn't open already? */ + + for (it = paths; !r && it; it = g_slist_next(it)) { + gchar *path; + struct stat s; + + if (!domain && !filename) /* given a full path to the file */ + path = g_strdup(it->data); + else + path = g_build_filename(it->data, domain, filename, NULL); + + if (stat(path, &s) >= 0) { + /* XML_PARSE_BLANKS is needed apparently, or the tree can end up + with extra nodes in it. */ + i->doc = xmlReadFile(path, NULL, (XML_PARSE_NOBLANKS | + XML_PARSE_RECOVER)); + xmlXIncludeProcessFlags(i->doc, (XML_PARSE_NOBLANKS | + XML_PARSE_RECOVER)); + if (i->doc) { + i->root = xmlDocGetRootElement(i->doc); + if (!i->root) { + xmlFreeDoc(i->doc); + i->doc = NULL; + g_message("%s is an empty XML document", path); + } + else if (xmlStrcmp(i->root->name, + (const xmlChar*)root_node)) { + xmlFreeDoc(i->doc); + i->doc = NULL; + i->root = NULL; + g_message("XML document %s is of wrong type. Root " + "node is not '%s'", path, root_node); + } + else { + i->path = g_strdup(path); + r = TRUE; /* ok! */ + } + } + } + + g_free(path); + } + + return r; +} + +gboolean obt_xml_load_file(ObtXmlInst *i, + const gchar *path, + const gchar *root_node) +{ + GSList *paths; + gboolean r; + + paths = g_slist_append(NULL, g_strdup(path)); + + r = load_file(i, NULL, NULL, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_xml_load_config_file(ObtXmlInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + for (it = obt_paths_config_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append(paths, g_strdup(it->data)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_xml_load_data_file(ObtXmlInst *i, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append(paths, g_strdup(it->data)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + +gboolean obt_xml_load_theme_file(ObtXmlInst *i, + const gchar *theme, + const gchar *domain, + const gchar *filename, + const gchar *root_node) +{ + GSList *it, *paths = NULL; + gboolean r; + + /* use ~/.themes for backwards compatibility */ + paths = g_slist_append + (paths, g_build_filename(g_get_home_dir(), ".themes", theme, NULL)); + + for (it = obt_paths_data_dirs(i->xdg_paths); it; it = g_slist_next(it)) + paths = g_slist_append + (paths, g_build_filename(it->data, "themes", theme, NULL)); + + r = load_file(i, domain, filename, root_node, paths); + + while (paths) { + g_free(paths->data); + paths = g_slist_delete_link(paths, paths); + } + return r; +} + + +gboolean obt_xml_load_mem(ObtXmlInst *i, + gpointer data, guint len, const gchar *root_node) +{ + gboolean r = FALSE; + + g_assert(i->doc == NULL); /* another doc isn't open already? */ + + i->doc = xmlParseMemory(data, len); + if (i) { + i->root = xmlDocGetRootElement(i->doc); + if (!i->root) { + xmlFreeDoc(i->doc); + i->doc = NULL; + g_message("Given memory is an empty document"); + } + else if (xmlStrcmp(i->root->name, (const xmlChar*)root_node)) { + xmlFreeDoc(i->doc); + i->doc = NULL; + i->root = NULL; + g_message("XML Document in given memory is of wrong " + "type. Root node is not '%s'\n", root_node); + } + else + r = TRUE; /* ok ! */ + } + return r; +} + +gboolean obt_xml_save_file(ObtXmlInst *inst, + const gchar *path, + gboolean pretty) +{ + return xmlSaveFormatFile(path, inst->doc, pretty) != -1; +} + +void obt_xml_close(ObtXmlInst *i) +{ + if (i && i->doc) { + xmlFreeDoc(i->doc); + g_free(i->path); + i->doc = NULL; + i->root = NULL; + i->path = NULL; + } +} + +void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node) +{ + g_assert(i->doc); /* a doc is open? */ + + while (node) { + if (node->name) { + struct Callback *c = g_hash_table_lookup(i->callbacks, node->name); + if (c) c->func(node, c->data); + } + node = node->next; + } +} + +void obt_xml_tree_from_root(ObtXmlInst *i) +{ + obt_xml_tree(i, i->root->children); +} + +gchar *obt_xml_node_string(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gchar *s; + if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */ + s = g_strdup(c ? (gchar*)c : ""); + xmlFree(c); + return s; +} + +gint obt_xml_node_int(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gint i; + if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */ + i = c ? atoi((gchar*)c) : 0; + xmlFree(c); + return i; +} + +gboolean obt_xml_node_bool(xmlNodePtr node) +{ + xmlChar *c = xmlNodeGetContent(node); + gboolean b = FALSE; + if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */ + if (c && !xmlStrcasecmp(c, (const xmlChar*) "true")) + b = TRUE; + else if (c && !xmlStrcasecmp(c, (const xmlChar*) "yes")) + b = TRUE; + else if (c && !xmlStrcasecmp(c, (const xmlChar*) "on")) + b = TRUE; + xmlFree(c); + return b; +} + +gboolean obt_xml_node_contains(xmlNodePtr node, const gchar *val) +{ + xmlChar *c = xmlNodeGetContent(node); + gboolean r; + if (c) g_strstrip((char*)c); /* strip leading/trailing whitespace */ + r = !xmlStrcasecmp(c, (const xmlChar*) val); + xmlFree(c); + return r; +} + +xmlNodePtr obt_xml_find_node(xmlNodePtr node, const gchar *tag) +{ + while (node) { + if (!xmlStrcmp(node->name, (const xmlChar*) tag)) + return node; + node = node->next; + } + return NULL; +} + +gboolean obt_xml_attr_bool(xmlNodePtr node, const gchar *name, + gboolean *value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + g_strstrip((char*)c); /* strip leading/trailing whitespace */ + if (!xmlStrcasecmp(c, (const xmlChar*) "true")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "yes")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "on")) + *value = TRUE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "false")) + *value = FALSE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "no")) + *value = FALSE, r = TRUE; + else if (!xmlStrcasecmp(c, (const xmlChar*) "off")) + *value = FALSE, r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_xml_attr_int(xmlNodePtr node, const gchar *name, gint *value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + g_strstrip((char*)c); /* strip leading/trailing whitespace */ + *value = atoi((gchar*)c); + r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_xml_attr_string(xmlNodePtr node, const gchar *name, + gchar **value) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + g_strstrip((char*)c); /* strip leading/trailing whitespace */ + *value = g_strdup((gchar*)c); + r = TRUE; + } + xmlFree(c); + return r; +} + +gboolean obt_xml_attr_contains(xmlNodePtr node, const gchar *name, + const gchar *val) +{ + xmlChar *c = xmlGetProp(node, (const xmlChar*) name); + gboolean r = FALSE; + if (c) { + g_strstrip((char*)c); /* strip leading/trailing whitespace */ + r = !xmlStrcasecmp(c, (const xmlChar*) val); + } + xmlFree(c); + return r; +} diff --git a/obt/xml.h b/obt/xml.h new file mode 100644 index 0000000..ac2dc57 --- /dev/null +++ b/obt/xml.h @@ -0,0 +1,89 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/xml.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_xml_h +#define __obt_xml_h + +#include <libxml/parser.h> +#include <glib.h> + +G_BEGIN_DECLS + +typedef struct _ObtXmlInst ObtXmlInst; + +typedef void (*ObtXmlCallback)(xmlNodePtr node, gpointer data); + +ObtXmlInst* obt_xml_instance_new(void); +void obt_xml_instance_ref(ObtXmlInst *inst); +void obt_xml_instance_unref(ObtXmlInst *inst); + +gboolean obt_xml_load_file(ObtXmlInst *inst, + const gchar *path, + const gchar *root_node); +gboolean obt_xml_load_config_file(ObtXmlInst *inst, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_xml_load_data_file(ObtXmlInst *inst, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_xml_load_theme_file(ObtXmlInst *inst, + const gchar *theme, + const gchar *domain, + const gchar *filename, + const gchar *root_node); +gboolean obt_xml_load_mem(ObtXmlInst *inst, + gpointer data, guint len, const gchar *root_node); + +gboolean obt_xml_save_file(ObtXmlInst *inst, + const gchar *path, + gboolean pretty); + +xmlDocPtr obt_xml_doc(ObtXmlInst *inst); +xmlNodePtr obt_xml_root(ObtXmlInst *inst); + +void obt_xml_close(ObtXmlInst *inst); + +void obt_xml_register(ObtXmlInst *inst, const gchar *tag, + ObtXmlCallback func, gpointer data); +void obt_xml_tree(ObtXmlInst *i, xmlNodePtr node); +void obt_xml_tree_from_root(ObtXmlInst *i); + + +/* helpers */ + +xmlNodePtr obt_xml_find_node (xmlNodePtr node, const gchar *name); + +gboolean obt_xml_node_contains (xmlNodePtr node, const gchar *val); +gchar *obt_xml_node_string (xmlNodePtr node); +gint obt_xml_node_int (xmlNodePtr node); +gboolean obt_xml_node_bool (xmlNodePtr node); + +gboolean obt_xml_attr_contains (xmlNodePtr node, const gchar *name, + const gchar *val); +gboolean obt_xml_attr_string (xmlNodePtr node, const gchar *name, + gchar **value); +gboolean obt_xml_attr_int (xmlNodePtr node, const gchar *name, + gint *value); +gboolean obt_xml_attr_bool (xmlNodePtr node, const gchar *name, + gboolean *value); + +G_END_DECLS + +#endif diff --git a/obt/xqueue.c b/obt/xqueue.c new file mode 100644 index 0000000..c04b226 --- /dev/null +++ b/obt/xqueue.c @@ -0,0 +1,411 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/display.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "obt/xqueue.h" +#include "obt/display.h" + +#define MINSZ 16 + +static XEvent *q = NULL; +static gulong qsz = 0; +static gulong qstart; /* the first event in the queue */ +static gulong qend; /* the last event in the queue */ +static gulong qnum = 0; + +static inline void shrink(void) { + if (qsz > MINSZ && qnum < qsz / 4) { + const gulong newsz = qsz/2; + gulong i; + + if (qnum == 0) { + qstart = 0; + qend = -1; + } + + /* all in the shinking part, move it to pos 0 */ + else if (qstart >= newsz && qend >= newsz) { + for (i = 0; i < qnum; ++i) + q[i] = q[qstart+i]; + qstart = 0; + qend = qnum - 1; + } + + /* it wraps around to 0 right now, move the part between newsz and qsz + to be before newsz */ + else if (qstart >= newsz) { + const gulong n = qsz - qstart; + for (i = 0; i < n; ++i) + q[newsz-n+i] = q[qstart+i]; + qstart = newsz-n; + } + + /* it needs to wrap around to 0, move the stuff after newsz to pos 0 */ + else if (qend >= newsz) { + const gulong n = qend + 1 - newsz; + for (i = 0; i < n; ++i) + q[i] = q[newsz+i]; + qend = n - 1; + } + + q = g_renew(XEvent, q, newsz); + qsz = newsz; + } +} + +static inline void grow(void) { + if (qnum == qsz) { + const gulong newsz = qsz*2; + gulong i; + + q = g_renew(XEvent, q, newsz); + + g_assert(qnum > 0); + + if (qend < qstart) { /* it wraps around to 0 right now */ + for (i = 0; i <= qend; ++i) + q[qsz+i] = q[i]; + qend = qsz + qend; + } + + qsz = newsz; + } +} + +/* Grab all pending X events */ +static gboolean read_events(gboolean block) +{ + gint sth, n; + + n = XEventsQueued(obt_display, QueuedAfterFlush) > 0; + sth = FALSE; + + while ((block && !sth) || n > 0) { + XEvent e; + + if (XNextEvent(obt_display, &e) != Success) + return FALSE; + + grow(); /* make sure there is room */ + + ++qnum; + qend = (qend + 1) % qsz; /* move the end */ + q[qend] = e; /* stick the event at the end */ + + --n; + sth = TRUE; + } + + return sth; /* return if we read anything */ +} + +static void pop(const gulong p) +{ + /* remove the event */ + --qnum; + if (qnum == 0) { + qstart = 0; + qend = -1; + } + else if (p == qstart) + qstart = (qstart + 1) % qsz; + else { + gulong pi; + + /* is it cheaper to move the start or the end ? */ + if ((p >= qstart && p < qstart + qnum/2) || + (p < qstart && p < (qstart + qnum/2) % qsz)) + { + /* move the start */ + pi = p; + while (pi != qstart) { + const gulong pi_next = (pi == 0 ? qsz-1 : pi-1); + + q[pi] = q[pi_next]; + pi = pi_next; + } + qstart = (qstart + 1) % qsz; + } + else { + /* move the end */ + pi = p; + while (pi != qend) { + const gulong pi_next = (pi + 1) % qsz; + + q[pi] = q[pi_next]; + pi = pi_next; + } + qend = (qend == 0 ? qsz-1 : qend-1); + } + } + + shrink(); /* shrink the q if too little in it */ +} + +void xqueue_init(void) +{ + if (q != NULL) return; + qsz = MINSZ; + q = g_new(XEvent, qsz); + qstart = 0; + qend = -1; +} + +void xqueue_destroy(void) +{ + if (q == NULL) return; + g_free(q); + q = NULL; + qsz = 0; +} + +gboolean xqueue_match_window(XEvent *e, gpointer data) +{ + const Window w = *(Window*)data; + return e->xany.window == w; +} + +gboolean xqueue_match_type(XEvent *e, gpointer data) +{ + return e->type == GPOINTER_TO_INT(data); +} + +gboolean xqueue_match_window_type(XEvent *e, gpointer data) +{ + const ObtXQueueWindowType x = *(ObtXQueueWindowType*)data; + return e->xany.window == x.window && e->type == x.type; +} + +gboolean xqueue_match_window_message(XEvent *e, gpointer data) +{ + const ObtXQueueWindowMessage x = *(ObtXQueueWindowMessage*)data; + return e->xany.window == x.window && e->type == ClientMessage && + e->xclient.message_type == x.message; +} + +gboolean xqueue_peek(XEvent *event_return) +{ + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(event_return != NULL, FALSE); + + if (!qnum) read_events(TRUE); + if (!qnum) return FALSE; + *event_return = q[qstart]; /* get the head */ + return TRUE; +} + +gboolean xqueue_peek_local(XEvent *event_return) +{ + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(event_return != NULL, FALSE); + + if (!qnum) read_events(FALSE); + if (!qnum) return FALSE; + *event_return = q[qstart]; /* get the head */ + return TRUE; +} + +gboolean xqueue_next(XEvent *event_return) +{ + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(event_return != NULL, FALSE); + + if (!qnum) read_events(TRUE); + if (qnum) { + *event_return = q[qstart]; /* get the head */ + pop(qstart); + return TRUE; + } + + return FALSE; +} + +gboolean xqueue_next_local(XEvent *event_return) +{ + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(event_return != NULL, FALSE); + + if (!qnum) read_events(FALSE); + if (qnum) { + *event_return = q[qstart]; /* get the head */ + pop(qstart); + return TRUE; + } + + return FALSE; +} + +gboolean xqueue_exists(xqueue_match_func match, gpointer data) +{ + gulong i, checked; + + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(match != NULL, FALSE); + + checked = 0; + while (TRUE) { + for (i = checked; i < qnum; ++i, ++checked) { + const gulong p = (qstart + i) % qsz; + if (match(&q[p], data)) + return TRUE; + } + if (!read_events(TRUE)) break; /* error */ + } + return FALSE; +} + +gboolean xqueue_exists_local(xqueue_match_func match, gpointer data) +{ + gulong i, checked; + + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(match != NULL, FALSE); + + checked = 0; + while (TRUE) { + for (i = checked; i < qnum; ++i, ++checked) { + const gulong p = (qstart + i) % qsz; + if (match(&q[p], data)) + return TRUE; + } + if (!read_events(FALSE)) break; + } + return FALSE; +} + +gboolean xqueue_remove_local(XEvent *event_return, + xqueue_match_func match, gpointer data) +{ + gulong i, checked; + + g_return_val_if_fail(q != NULL, FALSE); + g_return_val_if_fail(event_return != NULL, FALSE); + g_return_val_if_fail(match != NULL, FALSE); + + checked = 0; + while (TRUE) { + for (i = checked; i < qnum; ++i, ++checked) { + const gulong p = (qstart + i) % qsz; + if (match(&q[p], data)) { + *event_return = q[p]; + pop(p); + return TRUE; + } + } + if (!read_events(FALSE)) break; + } + return FALSE; +} + +gboolean xqueue_pending_local(void) +{ + g_return_val_if_fail(q != NULL, FALSE); + + if (!qnum) read_events(FALSE); + return qnum != 0; +} + +typedef struct _ObtXQueueCB { + ObtXQueueFunc func; + gpointer data; +} ObtXQueueCB; + +static ObtXQueueCB *callbacks = NULL; +static guint n_callbacks = 0; + +static gboolean event_read(GSource *source, GSourceFunc callback, + gpointer data) +{ + XEvent ev; + + while (xqueue_next_local(&ev)) { + guint i; + for (i = 0; i < n_callbacks; ++i) + callbacks[i].func(&ev, callbacks[i].data); + } + + return TRUE; /* repeat */ +} + +static gboolean x_source_prepare(GSource *source, gint *timeout) +{ + *timeout = -1; + return XPending(obt_display); +} + +static gboolean x_source_check(GSource *source) +{ + return XPending(obt_display); +} + +struct x_source { + GSource source; + + GPollFD pfd; +}; + +static GSourceFuncs x_source_funcs = { + x_source_prepare, + x_source_check, + event_read, + NULL +}; + +void xqueue_listen(void) +{ + GSource *source = g_source_new(&x_source_funcs, sizeof(struct x_source)); + struct x_source *x_source = (struct x_source *)source; + GPollFD *pfd = &x_source->pfd; + + *pfd = (GPollFD){ ConnectionNumber(obt_display), G_IO_IN, G_IO_IN }; + g_source_add_poll(source, pfd); + g_source_attach(source, NULL); +} + +void xqueue_add_callback(ObtXQueueFunc f, gpointer data) +{ + guint i; + + g_return_if_fail(f != NULL); + + for (i = 0; i < n_callbacks; ++i) + if (callbacks[i].func == f && callbacks[i].data == data) + return; + + callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks + 1); + callbacks[n_callbacks].func = f; + callbacks[n_callbacks].data = data; + ++n_callbacks; +} + +void xqueue_remove_callback(ObtXQueueFunc f, gpointer data) +{ + guint i; + + g_return_if_fail(f != NULL); + + for (i = 0; i < n_callbacks; ++i) { + if (callbacks[i].func == f && callbacks[i].data == data) { + /* remove it */ + for (; i < n_callbacks - 1; ++i) + callbacks[i] = callbacks[i+1]; + callbacks = g_renew(ObtXQueueCB, callbacks, n_callbacks - 1); + --n_callbacks; + break; + } + } +} diff --git a/obt/xqueue.h b/obt/xqueue.h new file mode 100644 index 0000000..11cd2bc --- /dev/null +++ b/obt/xqueue.h @@ -0,0 +1,101 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + obt/xqueue.h for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __obt_xqueue_h +#define __obt_xqueue_h + +#include <glib.h> +#include <X11/Xlib.h> + +G_BEGIN_DECLS + +typedef struct _ObtXQueueWindowType { + Window window; + int type; +} ObtXQueueWindowType; + +typedef struct _ObtXQueueWindowMessage { + Window window; + Atom message; +} ObtXQueueWindowMessage; + +typedef gboolean (*xqueue_match_func)(XEvent *e, gpointer data); + +/*! Returns TRUE if the event matches the window pointed to by @data */ +gboolean xqueue_match_window(XEvent *e, gpointer data); + +/*! Returns TRUE if the event matches the type contained in the value of @data */ +gboolean xqueue_match_type(XEvent *e, gpointer data); + +/*! Returns TRUE if the event matches the type and window in the + ObtXQueueWindowType pointed to by @data */ +gboolean xqueue_match_window_type(XEvent *e, gpointer data); + +/*! Returns TRUE if a ClientMessage event matches the message and window in the + ObtXQueueWindowMessage pointed to by @data */ +gboolean xqueue_match_window_message(XEvent *e, gpointer data); + +/*! Returns TRUE and passes the next event in the queue and removes it from + the queue. On error, returns FALSE */ +gboolean xqueue_next(XEvent *event_return); + +/*! Returns TRUE and passes the next event in the local queue and removes it + from the queue. If no event is in the local queue, it returns FALSE. */ +gboolean xqueue_next_local(XEvent *event_return); + +/*! Returns TRUE if there is anything in the local event queue, and FALSE + otherwise. */ +gboolean xqueue_pending_local(void); + +/*! Returns TRUE and passes the next event in the queue, or FALSE if there + is an error */ +gboolean xqueue_peek(XEvent *event_return); + +/*! Returns TRUE and passes the next event in the queue, if there is one, + and returns FALSE otherwise. */ +gboolean xqueue_peek_local(XEvent *event_return); + +/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the + current event queue or in the stream of events from the server, + and passes the matching event without removing it from the queue. + This blocks until an event is found or an error occurs. */ +gboolean xqueue_exists(xqueue_match_func match, gpointer data); + +/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the + current event queue, and passes the matching event without removing it + from the queue. */ +gboolean xqueue_exists_local(xqueue_match_func match, gpointer data); + +/*! Returns TRUE if xqueue_match_func returns TRUE for some event in the + current event queue, and passes the matching event while removing it + from the queue. */ +gboolean xqueue_remove_local(XEvent *event_return, + xqueue_match_func match, gpointer data); + +typedef void (*ObtXQueueFunc)(const XEvent *ev, gpointer data); + +/*! Begin listening for X events in the default GMainContext, and feed them + to the registered callback functions, added with xqueue_add_callback(). */ +void xqueue_listen(void); + +void xqueue_add_callback(ObtXQueueFunc f, gpointer data); +void xqueue_remove_callback(ObtXQueueFunc f, gpointer data); + +G_END_DECLS + +#endif diff --git a/openbox/Makefile b/openbox/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/openbox/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/openbox/actions.c b/openbox/actions.c new file mode 100644 index 0000000..ee9d55f --- /dev/null +++ b/openbox/actions.c @@ -0,0 +1,463 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + actions.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "actions.h" +#include "gettext.h" +#include "grab.h" +#include "screen.h" +#include "event.h" +#include "config.h" +#include "client.h" +#include "focus.h" +#include "openbox.h" +#include "debug.h" + +#include "actions/all.h" + +static void actions_definition_ref(ObActionsDefinition *def); +static void actions_definition_unref(ObActionsDefinition *def); +static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state); +static void actions_interactive_end_act(); +static ObActionsAct* actions_build_act_from_string(const gchar *name); + +static ObActionsAct *interactive_act = NULL; +static guint interactive_initial_state = 0; + +struct _ObActionsDefinition { + guint ref; + + gchar *name; + + gboolean canbeinteractive; + union { + ObActionsIDataSetupFunc i; + ObActionsDataSetupFunc n; + } setup; + ObActionsDataFreeFunc free; + ObActionsRunFunc run; + ObActionsShutdownFunc shutdown; +}; + +struct _ObActionsAct { + guint ref; + + ObActionsDefinition *def; + ObActionsIPreFunc i_pre; + ObActionsIInputFunc i_input; + ObActionsICancelFunc i_cancel; + ObActionsIPostFunc i_post; + gpointer options; +}; + +static GSList *registered = NULL; + +void actions_startup(gboolean reconfig) +{ + if (reconfig) return; + + action_all_startup(); +} + +void actions_shutdown(gboolean reconfig) +{ + actions_interactive_cancel_act(); + + if (reconfig) return; + + /* free all the registered actions */ + while (registered) { + ObActionsDefinition *d = registered->data; + if (d->shutdown) d->shutdown(); + actions_definition_unref(d); + registered = g_slist_delete_link(registered, registered); + } +} + +ObActionsDefinition* do_register(const gchar *name, + ObActionsDataFreeFunc free, + ObActionsRunFunc run) +{ + GSList *it; + ObActionsDefinition *def; + + g_assert(run != NULL); + + for (it = registered; it; it = g_slist_next(it)) { + def = it->data; + if (!g_ascii_strcasecmp(name, def->name)) /* already registered */ + return NULL; + } + + def = g_slice_new(ObActionsDefinition); + def->ref = 1; + def->name = g_strdup(name); + def->free = free; + def->run = run; + def->shutdown = NULL; + + registered = g_slist_prepend(registered, def); + return def; +} + +gboolean actions_register_i(const gchar *name, + ObActionsIDataSetupFunc setup, + ObActionsDataFreeFunc free, + ObActionsRunFunc run) +{ + ObActionsDefinition *def = do_register(name, free, run); + if (def) { + def->canbeinteractive = TRUE; + def->setup.i = setup; + } + return def != NULL; +} + +gboolean actions_register(const gchar *name, + ObActionsDataSetupFunc setup, + ObActionsDataFreeFunc free, + ObActionsRunFunc run) +{ + ObActionsDefinition *def = do_register(name, free, run); + if (def) { + def->canbeinteractive = FALSE; + def->setup.n = setup; + } + return def != NULL; +} + +gboolean actions_set_shutdown(const gchar *name, + ObActionsShutdownFunc shutdown) +{ + GSList *it; + ObActionsDefinition *def; + + for (it = registered; it; it = g_slist_next(it)) { + def = it->data; + if (!g_ascii_strcasecmp(name, def->name)) { + def->shutdown = shutdown; + return TRUE; + } + } + return FALSE; +} + +static void actions_definition_ref(ObActionsDefinition *def) +{ + ++def->ref; +} + +static void actions_definition_unref(ObActionsDefinition *def) +{ + if (def && --def->ref == 0) { + g_free(def->name); + g_slice_free(ObActionsDefinition, def); + } +} + +static ObActionsAct* actions_build_act_from_string(const gchar *name) +{ + GSList *it; + ObActionsDefinition *def = NULL; + ObActionsAct *act = NULL; + + /* find the requested action */ + for (it = registered; it; it = g_slist_next(it)) { + def = it->data; + if (!g_ascii_strcasecmp(name, def->name)) + break; + def = NULL; + } + + /* if we found the action */ + if (def) { + act = g_slice_new(ObActionsAct); + act->ref = 1; + act->def = def; + actions_definition_ref(act->def); + act->i_pre = NULL; + act->i_input = NULL; + act->i_cancel = NULL; + act->i_post = NULL; + act->options = NULL; + } else + g_message(_("Invalid action \"%s\" requested. No such action exists."), + name); + + return act; +} + +ObActionsAct* actions_parse_string(const gchar *name) +{ + ObActionsAct *act = NULL; + + if ((act = actions_build_act_from_string(name))) { + if (act->def->canbeinteractive) { + if (act->def->setup.i) + act->options = act->def->setup.i(NULL, + &act->i_pre, + &act->i_input, + &act->i_cancel, + &act->i_post); + } + else { + if (act->def->setup.n) + act->options = act->def->setup.n(NULL); + } + } + + + return act; +} + +ObActionsAct* actions_parse(xmlNodePtr node) +{ + gchar *name; + ObActionsAct *act = NULL; + + if (obt_xml_attr_string(node, "name", &name)) { + if ((act = actions_build_act_from_string(name))) { + /* there is more stuff to parse here */ + if (act->def->canbeinteractive) { + if (act->def->setup.i) + act->options = act->def->setup.i(node->children, + &act->i_pre, + &act->i_input, + &act->i_cancel, + &act->i_post); + } + else { + if (act->def->setup.n) + act->options = act->def->setup.n(node->children); + } + } + g_free(name); + } + + return act; +} + +gboolean actions_act_is_interactive(ObActionsAct *act) +{ + return act->i_input != NULL; +} + +void actions_act_ref(ObActionsAct *act) +{ + ++act->ref; +} + +void actions_act_unref(ObActionsAct *act) +{ + if (act && --act->ref == 0) { + /* free the action specific options */ + if (act->def->free) + act->def->free(act->options); + /* unref the definition */ + actions_definition_unref(act->def); + g_slice_free(ObActionsAct, act); + } +} + +static void actions_setup_data(ObActionsData *data, + ObUserAction uact, + guint state, + gint x, + gint y, + gint button, + ObFrameContext con, + struct _ObClient *client) +{ + data->uact = uact; + data->state = state; + data->x = x; + data->y = y; + data->button = button; + data->context = con; + data->client = client; +} + +void actions_run_acts(GSList *acts, + ObUserAction uact, + guint state, + gint x, + gint y, + gint button, + ObFrameContext con, + struct _ObClient *client) +{ + GSList *it; + gboolean update_user_time; + + /* Don't allow saving the initial state when running things from the + menu */ + if (uact == OB_USER_ACTION_MENU_SELECTION) + state = 0; + /* If x and y are < 0 then use the current pointer position */ + if (x < 0 && y < 0) + screen_pointer_pos(&x, &y); + + update_user_time = FALSE; + for (it = acts; it; it = g_slist_next(it)) { + ObActionsData data; + ObActionsAct *act = it->data; + gboolean ok = TRUE; + + actions_setup_data(&data, uact, state, x, y, button, con, client); + + /* if they have the same run function, then we'll assume they are + cooperating and not cancel eachother out */ + if (!interactive_act || interactive_act->def->run != act->def->run) { + if (actions_act_is_interactive(act)) { + /* cancel the old one */ + if (interactive_act) + actions_interactive_cancel_act(); + if (act->i_pre) + if (!act->i_pre(state, act->options)) + act->i_input = NULL; /* remove the interactivity */ + } + /* check again cuz it might have been cancelled */ + if (actions_act_is_interactive(act)) + ok = actions_interactive_begin_act(act, state); + } + + /* fire the action's run function with this data */ + if (ok) { + if (!act->def->run(&data, act->options)) { + if (actions_act_is_interactive(act)) + actions_interactive_end_act(); + if (client && client == focus_client) + update_user_time = TRUE; + } else { + /* make sure its interactive if it returned TRUE */ + g_assert(act->i_input); + + /* no actions are run after the interactive one */ + break; + } + } + } + if (update_user_time) + event_update_user_time(); +} + +gboolean actions_interactive_act_running(void) +{ + return interactive_act != NULL; +} + +void actions_interactive_cancel_act(void) +{ + if (interactive_act) { + if (interactive_act->i_cancel) + interactive_act->i_cancel(interactive_act->options); + actions_interactive_end_act(); + } +} + +static gboolean actions_interactive_begin_act(ObActionsAct *act, guint state) +{ + if (grab_keyboard()) { + interactive_act = act; + actions_act_ref(interactive_act); + + interactive_initial_state = obt_keyboard_only_modmasks(state); + + /* if using focus_delay, stop the timer now so that focus doesn't go + moving on us, which would kill the action */ + event_halt_focus_delay(); + + return TRUE; + } + else + return FALSE; +} + +static void actions_interactive_end_act(void) +{ + if (interactive_act) { + ObActionsAct *ia = interactive_act; + + /* set this to NULL first so the i_post() function can't cause this to + get called again (if it decides it wants to cancel any ongoing + interactive action). */ + interactive_act = NULL; + + ungrab_keyboard(); + + if (ia->i_post) + ia->i_post(ia->options); + + actions_act_unref(ia); + } +} + +gboolean actions_interactive_input_event(XEvent *e) +{ + gboolean used = FALSE; + if (interactive_act) { + if (!interactive_act->i_input(interactive_initial_state, e, + grab_input_context(), + interactive_act->options, &used)) + { + used = TRUE; /* if it cancelled the action then it has to of + been used */ + actions_interactive_end_act(); + } + } + return used; +} + +void actions_client_move(ObActionsData *data, gboolean start) +{ + static gulong ignore_start = 0; + if (start) + ignore_start = event_start_ignore_all_enters(); + else if (config_focus_follow && + data->context != OB_FRAME_CONTEXT_CLIENT) + { + if (data->uact == OB_USER_ACTION_MOUSE_PRESS) { + struct _ObClient *c; + + /* usually this is sorta redundant, but with a press action + that moves windows our from under the cursor, the enter + event will come as a GrabNotify which is ignored, so this + makes a fake enter event + + don't do this if there is a grab on the pointer. enter events + are ignored during a grab, so don't force fake ones when they + should be ignored + */ + if (!grab_on_pointer()) { + if ((c = client_under_pointer()) && c != data->client) { + ob_debug_type(OB_DEBUG_FOCUS, + "Generating fake enter because we did a " + "mouse-event action"); + event_enter_client(c); + } + else if (!c && c != data->client) { + ob_debug_type(OB_DEBUG_FOCUS, + "Generating fake leave because we did a " + "mouse-event action"); + event_enter_client(data->client); + } + } + } + else if (!data->button && !config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + } +} diff --git a/openbox/actions.h b/openbox/actions.h new file mode 100644 index 0000000..e03bc57 --- /dev/null +++ b/openbox/actions.h @@ -0,0 +1,119 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + actions.h for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "misc.h" +#include "frame.h" +#include "obt/xml.h" +#include "obt/keyboard.h" + +#include <glib.h> +#include <X11/Xlib.h> + +typedef struct _ObActionsDefinition ObActionsDefinition; +typedef struct _ObActionsAct ObActionsAct; +typedef struct _ObActionsData ObActionsData; +typedef struct _ObActionsAnyData ObActionsAnyData; +typedef struct _ObActionsGlobalData ObActionsGlobalData; +typedef struct _ObActionsClientData ObActionsClientData; +typedef struct _ObActionsSelectorData ObActionsSelectorData; + +typedef void (*ObActionsDataFreeFunc)(gpointer options); +typedef gboolean (*ObActionsRunFunc)(ObActionsData *data, + gpointer options); +typedef gpointer (*ObActionsDataSetupFunc)(xmlNodePtr node); +typedef void (*ObActionsShutdownFunc)(void); + +/* functions for interactive actions */ +/* return TRUE if the action is going to be interactive, or false to change + your mind and make it not */ +typedef gboolean (*ObActionsIPreFunc)(guint initial_state, gpointer options); +typedef void (*ObActionsIPostFunc)(gpointer options); +typedef gboolean (*ObActionsIInputFunc)(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used); +typedef void (*ObActionsICancelFunc)(gpointer options); +typedef gpointer (*ObActionsIDataSetupFunc)(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); + +struct _ObActionsData { + ObUserAction uact; + guint state; + gint x; + gint y; + gint button; + + struct _ObClient *client; + ObFrameContext context; +}; + +void actions_startup(gboolean reconfigure); +void actions_shutdown(gboolean reconfigure); + +/*! Use this if the actions created from this name may be interactive */ +gboolean actions_register_i(const gchar *name, + ObActionsIDataSetupFunc setup, + ObActionsDataFreeFunc free, + ObActionsRunFunc run); + +gboolean actions_register(const gchar *name, + ObActionsDataSetupFunc setup, + ObActionsDataFreeFunc free, + ObActionsRunFunc run); + +gboolean actions_set_shutdown(const gchar *name, + ObActionsShutdownFunc shutdown); + +ObActionsAct* actions_parse(xmlNodePtr node); +ObActionsAct* actions_parse_string(const gchar *name); + +gboolean actions_act_is_interactive(ObActionsAct *act); + +void actions_act_ref(ObActionsAct *act); +void actions_act_unref(ObActionsAct *act); + +/*! When this is true, an XAllowEvents with ReplayPointer will be called + if an action is going to maybe try moving windows around on screen (or + map/unmap windows) +*/ +void actions_set_need_pointer_replay_before_move(gboolean replay); +/*! Returns if a ReplayPointer is still needed. If it was called while running + actions then this will be false */ +gboolean actions_get_need_pointer_replay_before_move(void); + +/*! Pass in a GSList of ObActionsAct's to be run. */ +void actions_run_acts(GSList *acts, + ObUserAction uact, + guint state, + gint x, + gint y, + gint button, + ObFrameContext con, + struct _ObClient *client); + +gboolean actions_interactive_act_running(void); +void actions_interactive_cancel_act(void); + +gboolean actions_interactive_input_event(XEvent *e); + +/*! Function for actions to call when they are moving a client around */ +void actions_client_move(ObActionsData *data, gboolean start); diff --git a/openbox/actions/Makefile b/openbox/actions/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/openbox/actions/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/openbox/actions/addremovedesktop.c b/openbox/actions/addremovedesktop.c new file mode 100644 index 0000000..ff6767e --- /dev/null +++ b/openbox/actions/addremovedesktop.c @@ -0,0 +1,119 @@ +#include "openbox/actions.h" +#include "openbox/screen.h" +#include <glib.h> + +typedef struct { + gboolean current; + gboolean add; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static gpointer setup_add_func(xmlNodePtr node); +static gpointer setup_remove_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_addcurrent_func(xmlNodePtr node); +static gpointer setup_addlast_func(xmlNodePtr node); +static gpointer setup_removecurrent_func(xmlNodePtr node); +static gpointer setup_removelast_func(xmlNodePtr node); + +void action_addremovedesktop_startup(void) +{ + actions_register("AddDesktop", setup_add_func, free_func, run_func); + actions_register("RemoveDesktop", setup_remove_func, free_func, run_func); + + /* 3.4-compatibility */ + actions_register("AddDesktopLast", setup_addlast_func, + free_func, run_func); + actions_register("RemoveDesktopLast", setup_removelast_func, + free_func, run_func); + actions_register("AddDesktopCurrent", setup_addcurrent_func, + free_func, run_func); + actions_register("RemoveDesktopCurrent", setup_removecurrent_func, + free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "where"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "last")) + o->current = FALSE; + else if (!g_ascii_strcasecmp(s, "current")) + o->current = TRUE; + g_free(s); + } + + return o; +} + +static gpointer setup_add_func(xmlNodePtr node) +{ + Options *o = setup_func(node); + o->add = TRUE; + return o; +} + +static gpointer setup_remove_func(xmlNodePtr node) +{ + Options *o = setup_func(node); + o->add = FALSE; + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + actions_client_move(data, TRUE); + + if (o->add) + screen_add_desktop(o->current); + else + screen_remove_desktop(o->current); + + actions_client_move(data, FALSE); + + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_addcurrent_func(xmlNodePtr node) +{ + Options *o = setup_add_func(node); + o->current = TRUE; + return o; +} + +static gpointer setup_addlast_func(xmlNodePtr node) +{ + Options *o = setup_add_func(node); + o->current = FALSE; + return o; +} + +static gpointer setup_removecurrent_func(xmlNodePtr node) +{ + Options *o = setup_remove_func(node); + o->current = TRUE; + return o; +} + +static gpointer setup_removelast_func(xmlNodePtr node) +{ + Options *o = setup_remove_func(node); + o->current = FALSE; + return o; +} diff --git a/openbox/actions/all.c b/openbox/actions/all.c new file mode 100644 index 0000000..332e79c --- /dev/null +++ b/openbox/actions/all.c @@ -0,0 +1,44 @@ +#include "all.h" + +void action_all_startup(void) +{ + action_execute_startup(); + action_debug_startup(); + action_showmenu_startup(); + action_showdesktop_startup(); + action_reconfigure_startup(); + action_exit_startup(); + action_restart_startup(); + action_cyclewindows_startup(); + action_breakchroot_startup(); + action_close_startup(); + action_move_startup(); + action_focus_startup(); + action_raise_startup(); + action_lower_startup(); + action_raiselower_startup(); + action_unfocus_startup(); + action_iconify_startup(); + action_fullscreen_startup(); + action_maximize_startup(); + action_moveresizeto_startup(); + action_moverelative_startup(); + action_shade_startup(); + action_kill_startup(); + action_omnipresent_startup(); + action_directionalwindows_startup(); + action_resize_startup(); + action_decorations_startup(); + action_desktop_startup(); + action_dock_startup(); + action_resizerelative_startup(); + action_addremovedesktop_startup(); + action_dockautohide_startup(); + action_layer_startup(); + action_movetoedge_startup(); + action_growtoedge_startup(); + action_if_startup(); + action_focustobottom_startup(); + /* 3.4-compatibility */ + action_shadelowerraise_startup(); +} diff --git a/openbox/actions/all.h b/openbox/actions/all.h new file mode 100644 index 0000000..54d6319 --- /dev/null +++ b/openbox/actions/all.h @@ -0,0 +1,46 @@ +#ifndef __actions_all_h +#define __actions_all_h + +void action_all_startup(void); + +void action_execute_startup(void); +void action_debug_startup(void); +void action_showmenu_startup(void); +void action_showdesktop_startup(void); +void action_reconfigure_startup(void); +void action_exit_startup(void); +void action_restart_startup(void); +void action_cyclewindows_startup(void); +void action_breakchroot_startup(void); +void action_close_startup(void); +void action_move_startup(void); +void action_focus_startup(void); +void action_raise_startup(void); +void action_lower_startup(void); +void action_raiselower_startup(void); +void action_unfocus_startup(void); +void action_iconify_startup(void); +void action_fullscreen_startup(void); +void action_maximize_startup(void); +void action_moveresizeto_startup(void); +void action_moverelative_startup(void); +void action_shade_startup(void); +void action_kill_startup(void); +void action_omnipresent_startup(void); +void action_directionalwindows_startup(void); +void action_resize_startup(void); +void action_decorations_startup(void); +void action_desktop_startup(void); +void action_dock_startup(void); +void action_resizerelative_startup(void); +void action_addremovedesktop_startup(void); +void action_dockautohide_startup(void); +void action_layer_startup(void); +void action_movetoedge_startup(void); +void action_growtoedge_startup(void); +void action_if_startup(void); +void action_focustobottom_startup(void); +/* 3.4-compatibility */ +void action_shadelowerraise_startup(void); + +#endif diff --git a/openbox/actions/breakchroot.c b/openbox/actions/breakchroot.c new file mode 100644 index 0000000..8c00458 --- /dev/null +++ b/openbox/actions/breakchroot.c @@ -0,0 +1,20 @@ +#include "openbox/actions.h" +#include "openbox/keyboard.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_breakchroot_startup(void) +{ + actions_register("BreakChroot", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + /* break out of one chroot */ + keyboard_reset_chains(1); + + return FALSE; +} diff --git a/openbox/actions/close.c b/openbox/actions/close.c new file mode 100644 index 0000000..d2bc96c --- /dev/null +++ b/openbox/actions/close.c @@ -0,0 +1,19 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_close_startup(void) +{ + actions_register("Close", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) client_close(data->client); + + return FALSE; +} diff --git a/openbox/actions/cyclewindows.c b/openbox/actions/cyclewindows.c new file mode 100644 index 0000000..5f0db27 --- /dev/null +++ b/openbox/actions/cyclewindows.c @@ -0,0 +1,249 @@ +#include "openbox/actions.h" +#include "openbox/stacking.h" +#include "openbox/window.h" +#include "openbox/event.h" +#include "openbox/focus_cycle.h" +#include "openbox/openbox.h" +#include "gettext.h" +#include "obt/keyboard.h" + +typedef struct { + gboolean linear; + gboolean dock_windows; + gboolean desktop_windows; + gboolean only_hilite_windows; + gboolean all_desktops; + gboolean forward; + gboolean bar; + gboolean raise; + ObFocusCyclePopupMode dialog_mode; + GSList *actions; + + + /* options for after we're done */ + gboolean cancel; /* did the user cancel or not */ + guint state; /* keyboard state when finished */ +} Options; + +static gpointer setup_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_forward_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_backward_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used); +static void i_cancel_func(gpointer options); +static void i_post_func(gpointer options); + +void action_cyclewindows_startup(void) +{ + actions_register_i("NextWindow", setup_forward_func, free_func, run_func); + actions_register_i("PreviousWindow", setup_backward_func, free_func, + run_func); +} + +static gpointer setup_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->bar = TRUE; + o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_LIST; + + if ((n = obt_xml_find_node(node, "linear"))) + o->linear = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "dialog"))) { + if (obt_xml_node_contains(n, "none")) + o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_NONE; + else if (obt_xml_node_contains(n, "no")) + o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_NONE; + else if (obt_xml_node_contains(n, "icons")) + o->dialog_mode = OB_FOCUS_CYCLE_POPUP_MODE_ICONS; + } + if ((n = obt_xml_find_node(node, "bar"))) + o->bar = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "raise"))) + o->raise = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "panels"))) + o->dock_windows = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "hilite"))) + o->only_hilite_windows = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "desktop"))) + o->desktop_windows = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "allDesktops"))) + o->all_desktops = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(node, "finalactions"))) { + xmlNodePtr m; + + m = obt_xml_find_node(n->children, "action"); + while (m) { + ObActionsAct *action = actions_parse(m); + if (action) o->actions = g_slist_append(o->actions, action); + m = obt_xml_find_node(m->next, "action"); + } + } + else { + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Focus")); + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Raise")); + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Unshade")); + } + + *input = i_input_func; + *cancel = i_cancel_func; + *post = i_post_func; + return o; +} + +static gpointer setup_forward_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_func(node, pre, input, cancel, post); + o->forward = TRUE; + return o; +} + +static gpointer setup_backward_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_func(node, pre, input, cancel, post); + o->forward = FALSE; + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + + while (o->actions) { + actions_act_unref(o->actions->data); + o->actions = g_slist_delete_link(o->actions, o->actions); + } + + g_slice_free(Options, o); +} + +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + struct _ObClient *ft; + + ft = focus_cycle(o->forward, + o->all_desktops, + !o->only_hilite_windows, + o->dock_windows, + o->desktop_windows, + o->linear, + TRUE, + o->bar, + o->dialog_mode, + FALSE, FALSE); + + stacking_restore(); + if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft)); + + return TRUE; +} + +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used) +{ + Options *o = options; + guint mods; + + mods = obt_keyboard_only_modmasks(e->xkey.state); + if (e->type == KeyRelease) { + /* remove from the state the mask of the modifier key being + released, if it is a modifier key being released that is */ + mods &= ~obt_keyboard_keyevent_to_modmask(e); + } + + if (e->type == KeyPress) { + KeySym sym = obt_keyboard_keypress_to_keysym(e); + + /* Escape cancels no matter what */ + if (sym == XK_Escape) { + o->cancel = TRUE; + o->state = e->xkey.state; + return FALSE; + } + + /* There were no modifiers and they pressed enter */ + else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) { + o->cancel = FALSE; + o->state = e->xkey.state; + return FALSE; + } + } + /* They released the modifiers */ + else if (e->type == KeyRelease && initial_state && !(mods & initial_state)) + { + o->cancel = FALSE; + o->state = e->xkey.state; + return FALSE; + } + + return TRUE; +} + +static void i_cancel_func(gpointer options) +{ + Options *o = options; + o->cancel = TRUE; + o->state = 0; +} + +static void i_post_func(gpointer options) +{ + Options *o = options; + struct _ObClient *ft; + + ft = focus_cycle(o->forward, + o->all_desktops, + !o->only_hilite_windows, + o->dock_windows, + o->desktop_windows, + o->linear, + TRUE, + o->bar, + o->dialog_mode, + TRUE, o->cancel); + + if (ft) + actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY, + o->state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft); + + stacking_restore(); +} diff --git a/openbox/actions/debug.c b/openbox/actions/debug.c new file mode 100644 index 0000000..99446bc --- /dev/null +++ b/openbox/actions/debug.c @@ -0,0 +1,44 @@ +#include "openbox/actions.h" +#include <glib.h> + +typedef struct { + gchar *str; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_debug_startup(void) +{ + actions_register("Debug", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "string"))) + o->str = obt_xml_node_string(n); + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + g_free(o->str); + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (o->str) g_print("%s\n", o->str); + + return FALSE; +} diff --git a/openbox/actions/decorations.c b/openbox/actions/decorations.c new file mode 100644 index 0000000..f6fd2cb --- /dev/null +++ b/openbox/actions/decorations.c @@ -0,0 +1,46 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func_on(ObActionsData *data, gpointer options); +static gboolean run_func_off(ObActionsData *data, gpointer options); +static gboolean run_func_toggle(ObActionsData *data, gpointer options); + +void action_decorations_startup(void) +{ + actions_register("Decorate", NULL, NULL, run_func_on); + actions_register("Undecorate", NULL, NULL, run_func_off); + actions_register("ToggleDecorations", NULL, NULL, run_func_toggle); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_on(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_set_undecorated(data->client, FALSE); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_off(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_set_undecorated(data->client, TRUE); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_toggle(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_set_undecorated(data->client, !data->client->undecorated); + actions_client_move(data, FALSE); + } + return FALSE; +} diff --git a/openbox/actions/desktop.c b/openbox/actions/desktop.c new file mode 100644 index 0000000..a3a1f6b --- /dev/null +++ b/openbox/actions/desktop.c @@ -0,0 +1,567 @@ +#include "openbox/actions.h" +#include "openbox/screen.h" +#include "openbox/client.h" +#include "openbox/openbox.h" +#include "obt/keyboard.h" + +typedef enum { + LAST, + CURRENT, + RELATIVE, + ABSOLUTE +} SwitchType; + +typedef struct { + SwitchType type; + union { + struct { + guint desktop; + } abs; + + struct { + gboolean linear; + gboolean wrap; + ObDirection dir; + } rel; + } u; + gboolean send; + gboolean follow; + gboolean interactive; +} Options; + +static gpointer setup_go_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); + +static gboolean i_pre_func(guint state, gpointer options); +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used); +static void i_post_func(gpointer options); + +/* 3.4-compatibility */ +static gpointer setup_go_last_func(xmlNodePtr node); +static gpointer setup_send_last_func(xmlNodePtr node); +static gpointer setup_go_abs_func(xmlNodePtr node); +static gpointer setup_go_next_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_next_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_go_prev_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_prev_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_go_left_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_left_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_go_right_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_right_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_go_up_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_up_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_go_down_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_send_down_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); + +void action_desktop_startup(void) +{ + actions_register_i("GoToDesktop", setup_go_func, free_func, run_func); + actions_register_i("SendToDesktop", setup_send_func, free_func, run_func); + /* 3.4-compatibility */ + actions_register("DesktopLast", setup_go_last_func, free_func, run_func); + actions_register("SendToDesktopLast", setup_send_last_func, + free_func, run_func); + actions_register("Desktop", setup_go_abs_func, free_func, run_func); + actions_register_i("DesktopNext", setup_go_next_func, free_func, run_func); + actions_register_i("SendToDesktopNext", setup_send_next_func, + free_func, run_func); + actions_register_i("DesktopPrevious", setup_go_prev_func, + free_func, run_func); + actions_register_i("SendToDesktopPrevious", setup_send_prev_func, + free_func, run_func); + actions_register_i("DesktopLeft", setup_go_left_func, free_func, run_func); + actions_register_i("SendToDesktopLeft", setup_send_left_func, + free_func, run_func); + actions_register_i("DesktopRight", setup_go_right_func, + free_func, run_func); + actions_register_i("SendToDesktopRight", setup_send_right_func, + free_func, run_func); + actions_register_i("DesktopUp", setup_go_up_func, free_func, run_func); + actions_register_i("SendToDesktopUp", setup_send_up_func, + free_func, run_func); + actions_register_i("DesktopDown", setup_go_down_func, free_func, run_func); + actions_register_i("SendToDesktopDown", setup_send_down_func, + free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + /* don't go anywhere if there are no options given */ + o->type = ABSOLUTE; + o->u.abs.desktop = screen_desktop; + /* wrap by default - it's handy! */ + o->u.rel.wrap = TRUE; + + if ((n = obt_xml_find_node(node, "to"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "last")) + o->type = LAST; + else if (!g_ascii_strcasecmp(s, "current")) + o->type = CURRENT; + else if (!g_ascii_strcasecmp(s, "next")) { + o->type = RELATIVE; + o->u.rel.linear = TRUE; + o->u.rel.dir = OB_DIRECTION_EAST; + } + else if (!g_ascii_strcasecmp(s, "previous")) { + o->type = RELATIVE; + o->u.rel.linear = TRUE; + o->u.rel.dir = OB_DIRECTION_WEST; + } + else if (!g_ascii_strcasecmp(s, "north") || + !g_ascii_strcasecmp(s, "up")) { + o->type = RELATIVE; + o->u.rel.dir = OB_DIRECTION_NORTH; + } + else if (!g_ascii_strcasecmp(s, "south") || + !g_ascii_strcasecmp(s, "down")) { + o->type = RELATIVE; + o->u.rel.dir = OB_DIRECTION_SOUTH; + } + else if (!g_ascii_strcasecmp(s, "west") || + !g_ascii_strcasecmp(s, "left")) { + o->type = RELATIVE; + o->u.rel.dir = OB_DIRECTION_WEST; + } + else if (!g_ascii_strcasecmp(s, "east") || + !g_ascii_strcasecmp(s, "right")) { + o->type = RELATIVE; + o->u.rel.dir = OB_DIRECTION_EAST; + } + else { + o->type = ABSOLUTE; + o->u.abs.desktop = atoi(s) - 1; + } + g_free(s); + } + + if ((n = obt_xml_find_node(node, "wrap"))) + o->u.rel.wrap = obt_xml_node_bool(n); + + return o; +} + + +static gpointer setup_go_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o; + + o = setup_func(node, pre, input, cancel, post); + if (o->type == RELATIVE) { + o->interactive = TRUE; + *pre = i_pre_func; + *input = i_input_func; + *post = i_post_func; + } + + return o; +} + +static gpointer setup_send_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + xmlNodePtr n; + Options *o; + + o = setup_func(node, pre, input, cancel, post); + if ((n = obt_xml_find_node(node, "desktop"))) { + /* 3.4 compatibility */ + o->u.abs.desktop = obt_xml_node_int(n) - 1; + o->type = ABSOLUTE; + } + o->send = TRUE; + o->follow = TRUE; + + if ((n = obt_xml_find_node(node, "follow"))) + o->follow = obt_xml_node_bool(n); + + if (o->type == RELATIVE && o->follow) { + o->interactive = TRUE; + *pre = i_pre_func; + *input = i_input_func; + *post = i_post_func; + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + guint d; + + switch (o->type) { + case LAST: + d = screen_last_desktop; + break; + case CURRENT: + d = screen_desktop; + break; + case ABSOLUTE: + d = o->u.abs.desktop; + break; + case RELATIVE: + d = screen_find_desktop(screen_desktop, + o->u.rel.dir, o->u.rel.wrap, o->u.rel.linear); + break; + default: + g_assert_not_reached(); + } + + if (d < screen_num_desktops && + (d != screen_desktop || + (data->client && data->client->desktop != screen_desktop))) { + gboolean go = TRUE; + + actions_client_move(data, TRUE); + if (o->send && data->client && client_normal(data->client)) { + client_set_desktop(data->client, d, o->follow, FALSE); + go = o->follow; + } + + if (go) { + screen_set_desktop(d, TRUE); + if (data->client) + client_bring_helper_windows(data->client); + } + + actions_client_move(data, FALSE); + } + + return o->interactive; +} + +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used) +{ + guint mods; + + mods = obt_keyboard_only_modmasks(e->xkey.state); + if (e->type == KeyRelease) { + /* remove from the state the mask of the modifier key being + released, if it is a modifier key being released that is */ + mods &= ~obt_keyboard_keyevent_to_modmask(e); + } + + if (e->type == KeyPress) { + KeySym sym = obt_keyboard_keypress_to_keysym(e); + + /* Escape cancels no matter what */ + if (sym == XK_Escape) + return FALSE; + + /* There were no modifiers and they pressed enter */ + else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) + return FALSE; + } + /* They released the modifiers */ + else if (e->type == KeyRelease && initial_state && !(mods & initial_state)) + { + return FALSE; + } + + return TRUE; +} + +static gboolean i_pre_func(guint initial_state, gpointer options) +{ + if (!initial_state) { + Options *o = options; + o->interactive = FALSE; + return FALSE; + } + else { + screen_show_desktop_popup(screen_desktop, TRUE); + return TRUE; + } +} + +static void i_post_func(gpointer options) +{ + screen_hide_desktop_popup(); +} + +/* 3.4-compatilibity */ +static gpointer setup_follow(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o = g_slice_new0(Options); + o->send = TRUE; + o->follow = TRUE; + if ((n = obt_xml_find_node(node, "follow"))) + o->follow = obt_xml_node_bool(n); + return o; +} + +static gpointer setup_go_last_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->type = LAST; + return o; +} + +static gpointer setup_send_last_func(xmlNodePtr node) +{ + Options *o = setup_follow(node); + o->type = LAST; + return o; +} + +static gpointer setup_go_abs_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o = g_slice_new0(Options); + o->type = ABSOLUTE; + if ((n = obt_xml_find_node(node, "desktop"))) + o->u.abs.desktop = obt_xml_node_int(n) - 1; + else + o->u.abs.desktop = screen_desktop; + return o; +} + +static void setup_rel(Options *o, xmlNodePtr node, gboolean lin, + ObDirection dir, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsIPostFunc *post) +{ + xmlNodePtr n; + + o->type = RELATIVE; + o->u.rel.linear = lin; + o->u.rel.dir = dir; + o->u.rel.wrap = TRUE; + + if ((n = obt_xml_find_node(node, "wrap"))) + o->u.rel.wrap = obt_xml_node_bool(n); + + if (input) { + o->interactive = TRUE; + *pre = i_pre_func; + *input = i_input_func; + *post = i_post_func; + } +} + +static gpointer setup_go_next_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, TRUE, OB_DIRECTION_EAST, pre, input, post); + return o; +} + +static gpointer setup_send_next_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, TRUE, OB_DIRECTION_EAST, + pre, (o->follow ? input : NULL), post); + return o; +} + +static gpointer setup_go_prev_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, TRUE, OB_DIRECTION_WEST, pre, input, post); + return o; +} + +static gpointer setup_send_prev_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, TRUE, OB_DIRECTION_WEST, + pre, (o->follow ? input : NULL), post); + return o; +} + +static gpointer setup_go_left_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, FALSE, OB_DIRECTION_WEST, pre, input, post); + return o; +} + +static gpointer setup_send_left_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, FALSE, OB_DIRECTION_WEST, + pre, (o->follow ? input : NULL), post); + return o; +} + +static gpointer setup_go_right_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, FALSE, OB_DIRECTION_EAST, pre, input, post); + return o; +} + +static gpointer setup_send_right_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, FALSE, OB_DIRECTION_EAST, + pre, (o->follow ? input : NULL), post); + return o; +} + +static gpointer setup_go_up_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, pre, input, post); + return o; +} + +static gpointer setup_send_up_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, FALSE, OB_DIRECTION_NORTH, + pre, (o->follow ? input : NULL), post); + return o; +} + +static gpointer setup_go_down_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = g_slice_new0(Options); + setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, pre, input, post); + return o; +} + +static gpointer setup_send_down_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_follow(node); + setup_rel(o, node, FALSE, OB_DIRECTION_SOUTH, + pre, (o->follow ? input : NULL), post); + return o; +} diff --git a/openbox/actions/directionalwindows.c b/openbox/actions/directionalwindows.c new file mode 100644 index 0000000..602e7ed --- /dev/null +++ b/openbox/actions/directionalwindows.c @@ -0,0 +1,468 @@ +#include "openbox/actions.h" +#include "openbox/event.h" +#include "openbox/stacking.h" +#include "openbox/window.h" +#include "openbox/focus_cycle.h" +#include "openbox/openbox.h" +#include "openbox/misc.h" +#include "gettext.h" +#include "obt/keyboard.h" + +typedef struct { + gboolean interactive; + gboolean dialog; + gboolean dock_windows; + gboolean desktop_windows; + ObDirection direction; + gboolean bar; + gboolean raise; + GSList *actions; +} Options; + +static gboolean cycling = FALSE; + +static gpointer setup_func(xmlNodePtr node); +static gpointer setup_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post); +static gpointer setup_target_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used); +static void i_cancel_func(gpointer options); + +static void end_cycle(gboolean cancel, guint state, Options *o); + +/* 3.4-compatibility */ +static gpointer setup_north_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_south_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_east_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_west_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_northwest_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_northeast_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_southwest_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_southeast_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *in, + ObActionsICancelFunc *c, + ObActionsIPostFunc *post); +static gpointer setup_north_target_func(xmlNodePtr node); +static gpointer setup_south_target_func(xmlNodePtr node); +static gpointer setup_east_target_func(xmlNodePtr node); +static gpointer setup_west_target_func(xmlNodePtr node); +static gpointer setup_northwest_target_func(xmlNodePtr node); +static gpointer setup_northeast_target_func(xmlNodePtr node); +static gpointer setup_southwest_target_func(xmlNodePtr node); +static gpointer setup_southeast_target_func(xmlNodePtr node); + +void action_directionalwindows_startup(void) +{ + actions_register_i("DirectionalCycleWindows", setup_cycle_func, free_func, + run_func); + actions_register("DirectionalTargetWindow", setup_target_func, free_func, + run_func); + /* 3.4-compatibility */ + actions_register_i("DirectionalFocusNorth", setup_north_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusSouth", setup_south_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusWest", setup_west_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusEast", setup_east_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusNorthWest", setup_northwest_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusNorthEast", setup_northeast_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusSouthWest", setup_southwest_cycle_func, + free_func, run_func); + actions_register_i("DirectionalFocusSouthEast", setup_southeast_cycle_func, + free_func, run_func); + actions_register("DirectionalTargetNorth", setup_north_target_func, + free_func, run_func); + actions_register("DirectionalTargetSouth", setup_south_target_func, + free_func, run_func); + actions_register("DirectionalTargetWest", setup_west_target_func, + free_func, run_func); + actions_register("DirectionalTargetEast", setup_east_target_func, + free_func, run_func); + actions_register("DirectionalTargetNorthWest", setup_northwest_target_func, + free_func, run_func); + actions_register("DirectionalTargetNorthEast", setup_northeast_target_func, + free_func, run_func); + actions_register("DirectionalTargetSouthWest", setup_southwest_target_func, + free_func, run_func); + actions_register("DirectionalTargetSouthEast", setup_southeast_target_func, + free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->dialog = TRUE; + o->bar = TRUE; + + if ((n = obt_xml_find_node(node, "dialog"))) + o->dialog = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "bar"))) + o->bar = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "raise"))) + o->raise = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "panels"))) + o->dock_windows = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "desktop"))) + o->desktop_windows = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "direction"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "north") || + !g_ascii_strcasecmp(s, "up")) + o->direction = OB_DIRECTION_NORTH; + else if (!g_ascii_strcasecmp(s, "northwest")) + o->direction = OB_DIRECTION_NORTHWEST; + else if (!g_ascii_strcasecmp(s, "northeast")) + o->direction = OB_DIRECTION_NORTHEAST; + else if (!g_ascii_strcasecmp(s, "west") || + !g_ascii_strcasecmp(s, "left")) + o->direction = OB_DIRECTION_WEST; + else if (!g_ascii_strcasecmp(s, "east") || + !g_ascii_strcasecmp(s, "right")) + o->direction = OB_DIRECTION_EAST; + else if (!g_ascii_strcasecmp(s, "south") || + !g_ascii_strcasecmp(s, "down")) + o->direction = OB_DIRECTION_SOUTH; + else if (!g_ascii_strcasecmp(s, "southwest")) + o->direction = OB_DIRECTION_SOUTHWEST; + else if (!g_ascii_strcasecmp(s, "southeast")) + o->direction = OB_DIRECTION_SOUTHEAST; + g_free(s); + } + + if ((n = obt_xml_find_node(node, "finalactions"))) { + xmlNodePtr m; + + m = obt_xml_find_node(n->children, "action"); + while (m) { + ObActionsAct *action = actions_parse(m); + if (action) o->actions = g_slist_append(o->actions, action); + m = obt_xml_find_node(m->next, "action"); + } + } + else { + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Focus")); + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Raise")); + o->actions = g_slist_prepend(o->actions, + actions_parse_string("Unshade")); + } + + return o; +} + +static gpointer setup_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_func(node); + o->interactive = TRUE; + *input = i_input_func; + *cancel = i_cancel_func; + return o; +} + +static gpointer setup_target_func(xmlNodePtr node) +{ + Options *o = setup_func(node); + o->interactive = FALSE; + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + + while (o->actions) { + actions_act_unref(o->actions->data); + o->actions = g_slist_delete_link(o->actions, o->actions); + } + + g_slice_free(Options, o); +} + +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (!o->interactive) + end_cycle(FALSE, data->state, o); + else { + struct _ObClient *ft; + + ft = focus_directional_cycle(o->direction, + o->dock_windows, + o->desktop_windows, + TRUE, + o->bar, + o->dialog, + FALSE, FALSE); + cycling = TRUE; + + stacking_restore(); + if (o->raise && ft) stacking_temp_raise(CLIENT_AS_WINDOW(ft)); + } + + return o->interactive; +} + +static gboolean i_input_func(guint initial_state, + XEvent *e, + ObtIC *ic, + gpointer options, + gboolean *used) +{ + guint mods; + + mods = obt_keyboard_only_modmasks(e->xkey.state); + if (e->type == KeyRelease) { + /* remove from the state the mask of the modifier key being + released, if it is a modifier key being released that is */ + mods &= ~obt_keyboard_keyevent_to_modmask(e); + } + + if (e->type == KeyPress) { + KeySym sym = obt_keyboard_keypress_to_keysym(e); + + /* Escape cancels no matter what */ + if (sym == XK_Escape) { + end_cycle(TRUE, e->xkey.state, options); + return FALSE; + } + + /* There were no modifiers and they pressed enter */ + else if ((sym == XK_Return || sym == XK_KP_Enter) && !initial_state) { + end_cycle(FALSE, e->xkey.state, options); + return FALSE; + } + } + /* They released the modifiers */ + else if (e->type == KeyRelease && initial_state && !(mods & initial_state)) + { + end_cycle(FALSE, e->xkey.state, options); + return FALSE; + } + + return TRUE; +} + +static void i_cancel_func(gpointer options) +{ + /* we get cancelled when we move focus, but we're not cycling anymore, so + just ignore that */ + if (cycling) + end_cycle(TRUE, 0, options); +} + +static void end_cycle(gboolean cancel, guint state, Options *o) +{ + struct _ObClient *ft; + + ft = focus_directional_cycle(o->direction, + o->dock_windows, + o->desktop_windows, + o->interactive, + o->bar, + o->dialog, + TRUE, cancel); + cycling = FALSE; + + if (ft) + actions_run_acts(o->actions, OB_USER_ACTION_KEYBOARD_KEY, + state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, ft); + + stacking_restore(); +} + +/* 3.4-compatibility */ +static gpointer setup_north_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_NORTH; + return o; +} + +static gpointer setup_south_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_SOUTH; + return o; +} + +static gpointer setup_east_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_EAST; + return o; +} + +static gpointer setup_west_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_WEST; + return o; +} + +static gpointer setup_northwest_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_NORTHWEST; + return o; +} + +static gpointer setup_northeast_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_EAST; + return o; +} + +static gpointer setup_southwest_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_SOUTHWEST; + return o; +} + +static gpointer setup_southeast_cycle_func(xmlNodePtr node, + ObActionsIPreFunc *pre, + ObActionsIInputFunc *input, + ObActionsICancelFunc *cancel, + ObActionsIPostFunc *post) +{ + Options *o = setup_cycle_func(node, pre, input, cancel, post); + o->direction = OB_DIRECTION_SOUTHEAST; + return o; +} + +static gpointer setup_north_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_NORTH; + return o; +} + +static gpointer setup_south_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_SOUTH; + return o; +} + +static gpointer setup_east_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_EAST; + return o; +} + +static gpointer setup_west_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_WEST; + return o; +} + +static gpointer setup_northwest_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_NORTHWEST; + return o; +} + +static gpointer setup_northeast_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_NORTHEAST; + return o; +} + +static gpointer setup_southwest_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_SOUTHWEST; + return o; +} + +static gpointer setup_southeast_target_func(xmlNodePtr node) +{ + Options *o = setup_target_func(node); + o->direction = OB_DIRECTION_SOUTHEAST; + return o; +} + diff --git a/openbox/actions/dock.c b/openbox/actions/dock.c new file mode 100644 index 0000000..a1f6837 --- /dev/null +++ b/openbox/actions/dock.c @@ -0,0 +1,38 @@ +#include "openbox/actions.h" +#include "openbox/stacking.h" +#include "openbox/window.h" +#include "openbox/dock.h" + +static gboolean raise_func(ObActionsData *data, gpointer options); +static gboolean lower_func(ObActionsData *data, gpointer options); + +void action_dock_startup(void) +{ + actions_register("RaiseDock", + NULL, NULL, + raise_func); + actions_register("LowerDock", + NULL, NULL, + lower_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean raise_func(ObActionsData *data, gpointer options) +{ + actions_client_move(data, TRUE); + dock_raise_dock(); + actions_client_move(data, FALSE); + + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean lower_func(ObActionsData *data, gpointer options) +{ + actions_client_move(data, TRUE); + dock_lower_dock(); + actions_client_move(data, FALSE); + + return FALSE; +} + diff --git a/openbox/actions/dockautohide.c b/openbox/actions/dockautohide.c new file mode 100644 index 0000000..4a750b2 --- /dev/null +++ b/openbox/actions/dockautohide.c @@ -0,0 +1,21 @@ +#include "openbox/actions.h" +#include "openbox/dock.h" +#include "openbox/config.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_dockautohide_startup(void) +{ + actions_register("ToggleDockAutoHide", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + config_dock_hide = !config_dock_hide; + dock_configure(); + + return FALSE; +} diff --git a/openbox/actions/execute.c b/openbox/actions/execute.c new file mode 100644 index 0000000..380ffa0 --- /dev/null +++ b/openbox/actions/execute.c @@ -0,0 +1,273 @@ +#include "openbox/actions.h" +#include "openbox/event.h" +#include "openbox/startupnotify.h" +#include "openbox/client.h" +#include "openbox/prompt.h" +#include "openbox/screen.h" +#include "obt/paths.h" +#include "gettext.h" + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +typedef struct { + gchar *cmd; + gboolean sn; + gchar *sn_name; + gchar *sn_icon; + gchar *sn_wmclass; + gchar *prompt; + ObActionsData *data; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); +static void shutdown_func(void); +static void client_dest(ObClient *client, gpointer data); + +static GSList *prompt_opts = NULL; + +void action_execute_startup(void) +{ + actions_register("Execute", setup_func, free_func, run_func); + actions_set_shutdown("Execute", shutdown_func); + + client_add_destroy_notify(client_dest, NULL); +} + +static void client_dest(ObClient *client, gpointer data) +{ + GSList *it; + + for (it = prompt_opts; it; it = g_slist_next(it)) { + Options *o = it->data; + if (o->data->client == client) + o->data->client = NULL; + } +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "command")) || + (n = obt_xml_find_node(node, "execute"))) + { + gchar *s = obt_xml_node_string(n); + o->cmd = obt_paths_expand_tilde(s); + g_free(s); + } + + if ((n = obt_xml_find_node(node, "prompt"))) + o->prompt = obt_xml_node_string(n); + + if ((n = obt_xml_find_node(node, "startupnotify"))) { + xmlNodePtr m; + if ((m = obt_xml_find_node(n->children, "enabled"))) + o->sn = obt_xml_node_bool(m); + if ((m = obt_xml_find_node(n->children, "name"))) + o->sn_name = obt_xml_node_string(m); + if ((m = obt_xml_find_node(n->children, "icon"))) + o->sn_icon = obt_xml_node_string(m); + if ((m = obt_xml_find_node(n->children, "wmclass"))) + o->sn_wmclass = obt_xml_node_string(m); + } + return o; +} + +static void shutdown_func(void) +{ + client_remove_destroy_notify(client_dest); +} + +static void free_func(gpointer options) +{ + Options *o = options; + + if (o) { + prompt_opts = g_slist_remove(prompt_opts, o); + + g_free(o->cmd); + g_free(o->sn_name); + g_free(o->sn_icon); + g_free(o->sn_wmclass); + g_free(o->prompt); + if (o->data) g_slice_free(ObActionsData, o->data); + g_slice_free(Options, o); + } +} + +static Options* dup_options(Options *in, ObActionsData *data) +{ + Options *o = g_slice_new(Options); + o->cmd = g_strdup(in->cmd); + o->sn = in->sn; + o->sn_name = g_strdup(in->sn_name); + o->sn_icon = g_strdup(in->sn_icon); + o->sn_wmclass = g_strdup(in->sn_wmclass); + o->prompt = NULL; + o->data = g_slice_new(ObActionsData); + memcpy(o->data, data, sizeof(ObActionsData)); + return o; +} + +static gboolean prompt_cb(ObPrompt *p, gint result, gpointer options) +{ + Options *o = options; + if (result) + run_func(o->data, o); + return TRUE; /* call the cleanup func */ +} + +static void prompt_cleanup(ObPrompt *p, gpointer options) +{ + prompt_unref(p); + free_func(options); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + GError *e; + gchar **argv = NULL; + gchar *cmd; + Options *o = options; + + if (!o->cmd) return FALSE; + + if (o->prompt) { + ObPrompt *p; + Options *ocp; + ObPromptAnswer answers[] = { + { _("No"), 0 }, + { _("Yes"), 1 } + }; + + ocp = dup_options(options, data); + p = prompt_new(o->prompt, _("Execute"), answers, 2, 0, 0, + prompt_cb, prompt_cleanup, ocp); + prompt_show(p, NULL, FALSE); + + return FALSE; + } + + cmd = g_filename_from_utf8(o->cmd, -1, NULL, NULL, NULL); + if (!cmd) { + g_message(_("Failed to convert the path \"%s\" from utf8"), o->cmd); + return FALSE; + } + + if (data->client) { + gchar *c, *before, *expand; + + /* replace occurrences of $pid and $wid */ + + expand = NULL; + before = cmd; + + while ((c = strchr(before, '$'))) { + if ((c[1] == 'p' || c[1] == 'P') && + (c[2] == 'i' || c[2] == 'I') && + (c[3] == 'd' || c[3] == 'D') && + !g_ascii_isalnum(c[4])) + { + /* found $pid */ + gchar *tmp; + + *c = '\0'; + tmp = expand; + expand = g_strdup_printf("%s%s%u", + (expand ? expand : ""), + before, + data->client->pid); + g_free(tmp); + + before = c + 4; /* 4 = strlen("$pid") */ + } + else if ((c[1] == 'w' || c[1] == 'W') && + (c[2] == 'i' || c[2] == 'I') && + (c[3] == 'd' || c[3] == 'D') && + !g_ascii_isalnum(c[4])) + { + /* found $wid */ + gchar *tmp; + + *c = '\0'; + tmp = expand; + expand = g_strdup_printf("%s%s%lu", + (expand ? expand : ""), + before, + data->client->window); + g_free(tmp); + + before = c + 4; /* 4 = strlen("$wid") */ + } + else + before = c + 1; /* no infinite loops plz */ + } + + if (expand) { + gchar *tmp; + + /* add on the end of the string after the last replacement */ + tmp = expand; + expand = g_strconcat(expand, before, NULL); + g_free(tmp); + + /* replace the command with the expanded one */ + g_free(cmd); + cmd = expand; + } + } + + /* If there is a keyboard grab going on then we need to cancel + it so the application can grab things */ + if (data->uact != OB_USER_ACTION_MENU_SELECTION) + event_cancel_all_key_grabs(); + + e = NULL; + if (!g_shell_parse_argv(cmd, NULL, &argv, &e)) { + g_message("%s", e->message); + g_error_free(e); + } + else { + gchar *program = NULL; + gboolean ok; + + if (o->sn) { + program = g_path_get_basename(argv[0]); + /* sets up the environment */ + sn_setup_spawn_environment(program, o->sn_name, o->sn_icon, + o->sn_wmclass, + /* launch it on the current desktop */ + screen_desktop); + } + + e = NULL; + ok = g_spawn_async(NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, NULL, &e); + if (!ok) { + g_message("%s", e->message); + g_error_free(e); + } + + if (o->sn) { + if (!ok) sn_spawn_cancel(); + g_unsetenv("DESKTOP_STARTUP_ID"); + } + + g_free(program); + g_strfreev(argv); + } + + g_free(cmd); + + return FALSE; +} diff --git a/openbox/actions/exit.c b/openbox/actions/exit.c new file mode 100644 index 0000000..2d9fc63 --- /dev/null +++ b/openbox/actions/exit.c @@ -0,0 +1,88 @@ +#include "openbox/actions.h" +#include "openbox/openbox.h" +#include "openbox/prompt.h" +#include "openbox/session.h" +#include "gettext.h" + +typedef struct { + gboolean prompt; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_exit_startup(void) +{ + actions_register("Exit", setup_func, free_func, run_func); + actions_register("SessionLogout", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->prompt = TRUE; + + if ((n = obt_xml_find_node(node, "prompt"))) + o->prompt = obt_xml_node_bool(n); + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +static void do_exit(void) +{ + if (session_connected()) + session_request_logout(FALSE); + else + ob_exit(0); +} + +static gboolean prompt_cb(ObPrompt *p, gint result, gpointer data) +{ + if (result) + do_exit(); + return TRUE; /* call the cleanup func */ +} + +static void prompt_cleanup(ObPrompt *p, gpointer data) +{ + prompt_unref(p); +} + + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (o->prompt) { + ObPrompt *p; + ObPromptAnswer answers[] = { + { _("Cancel"), 0 }, + { _("Exit"), 1 } + }; + + if (session_connected()) + p = prompt_new(_("Are you sure you want to log out?"), + _("Log Out"), + answers, 2, 0, 0, prompt_cb, prompt_cleanup, NULL); + else + p = prompt_new(_("Are you sure you want to exit Openbox?"), + _("Exit Openbox"), + answers, 2, 0, 0, prompt_cb, prompt_cleanup, NULL); + + prompt_show(p, NULL, FALSE); + } + else + do_exit(); + + return FALSE; +} diff --git a/openbox/actions/focus.c b/openbox/actions/focus.c new file mode 100644 index 0000000..6c8957c --- /dev/null +++ b/openbox/actions/focus.c @@ -0,0 +1,74 @@ +#include "openbox/actions.h" +#include "openbox/event.h" +#include "openbox/client.h" +#include "openbox/focus.h" +#include "openbox/screen.h" + +typedef struct { + gboolean here; + gboolean stop_int; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_focus_startup(void) +{ + actions_register("Focus", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->stop_int = TRUE; + + if ((n = obt_xml_find_node(node, "here"))) + o->here = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "stopInteractive"))) + o->stop_int = obt_xml_node_bool(n); + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { +/* + ob_debug("button %d focusable %d context %d %d %d\n", + data->button, client_mouse_focusable(data->client), + data->context, + OB_FRAME_CONTEXT_CLIENT, OB_FRAME_CONTEXT_FRAME); +*/ + if (data->button == 0 || client_mouse_focusable(data->client) || + (data->context != OB_FRAME_CONTEXT_CLIENT && + data->context != OB_FRAME_CONTEXT_FRAME)) + { + if (o->stop_int) + actions_interactive_cancel_act(); + + actions_client_move(data, TRUE); + client_activate(data->client, TRUE, o->here, FALSE, FALSE, TRUE); + actions_client_move(data, FALSE); + } + } else if (data->context == OB_FRAME_CONTEXT_DESKTOP) { + if (o->stop_int) + actions_interactive_cancel_act(); + + /* focus action on the root window. make keybindings work for this + openbox instance, but don't focus any specific client */ + focus_nothing(); + } + + return FALSE; +} diff --git a/openbox/actions/focustobottom.c b/openbox/actions/focustobottom.c new file mode 100644 index 0000000..a3e5b5a --- /dev/null +++ b/openbox/actions/focustobottom.c @@ -0,0 +1,17 @@ +#include "openbox/actions.h" +#include "openbox/focus.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_focustobottom_startup(void) +{ + actions_register("FocusToBottom", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) + focus_order_to_bottom(data->client); + return FALSE; +} diff --git a/openbox/actions/fullscreen.c b/openbox/actions/fullscreen.c new file mode 100644 index 0000000..e1fdf23 --- /dev/null +++ b/openbox/actions/fullscreen.c @@ -0,0 +1,20 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func_toggle(ObActionsData *data, gpointer options); + +void action_fullscreen_startup(void) +{ + actions_register("ToggleFullscreen", NULL, NULL, run_func_toggle); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_toggle(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_fullscreen(data->client, !data->client->fullscreen); + actions_client_move(data, FALSE); + } + return FALSE; +} diff --git a/openbox/actions/growtoedge.c b/openbox/actions/growtoedge.c new file mode 100644 index 0000000..d5a7bfd --- /dev/null +++ b/openbox/actions/growtoedge.c @@ -0,0 +1,200 @@ +#include "openbox/actions.h" +#include "openbox/misc.h" +#include "openbox/client.h" +#include "openbox/frame.h" +#include "openbox/screen.h" +#include <glib.h> + +typedef struct { + ObDirection dir; + gboolean shrink; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static gpointer setup_shrink_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_north_func(xmlNodePtr node); +static gpointer setup_south_func(xmlNodePtr node); +static gpointer setup_east_func(xmlNodePtr node); +static gpointer setup_west_func(xmlNodePtr node); + +void action_growtoedge_startup(void) +{ + actions_register("GrowToEdge", setup_func, + free_func, run_func); + actions_register("ShrinkToEdge", setup_shrink_func, + free_func, run_func); + /* 3.4-compatibility */ + actions_register("GrowToEdgeNorth", setup_north_func, free_func, run_func); + actions_register("GrowToEdgeSouth", setup_south_func, free_func, run_func); + actions_register("GrowToEdgeEast", setup_east_func, free_func, run_func); + actions_register("GrowToEdgeWest", setup_west_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->dir = OB_DIRECTION_NORTH; + o->shrink = FALSE; + + if ((n = obt_xml_find_node(node, "direction"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "north") || + !g_ascii_strcasecmp(s, "up")) + o->dir = OB_DIRECTION_NORTH; + else if (!g_ascii_strcasecmp(s, "south") || + !g_ascii_strcasecmp(s, "down")) + o->dir = OB_DIRECTION_SOUTH; + else if (!g_ascii_strcasecmp(s, "west") || + !g_ascii_strcasecmp(s, "left")) + o->dir = OB_DIRECTION_WEST; + else if (!g_ascii_strcasecmp(s, "east") || + !g_ascii_strcasecmp(s, "right")) + o->dir = OB_DIRECTION_EAST; + g_free(s); + } + + return o; +} + +static gpointer setup_shrink_func(xmlNodePtr node) +{ + Options *o; + + o = setup_func(node); + o->shrink = TRUE; + + return o; +} + +static gboolean do_grow(ObActionsData *data, gint x, gint y, gint w, gint h) +{ + gint realw, realh, lw, lh; + + realw = w; + realh = h; + client_try_configure(data->client, &x, &y, &realw, &realh, + &lw, &lh, TRUE); + /* if it's going to be resized smaller than it intended, don't + move the window over */ + if (x != data->client->area.x) x += w - realw; + if (y != data->client->area.y) y += h - realh; + + if (x != data->client->area.x || y != data->client->area.y || + realw != data->client->area.width || + realh != data->client->area.height) + { + actions_client_move(data, TRUE); + client_move_resize(data->client, x, y, realw, realh); + actions_client_move(data, FALSE); + return TRUE; + } + return FALSE; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + gint x, y, w, h; + ObDirection opp; + gint half; + + if (!data->client || + /* don't allow vertical resize if shaded */ + ((o->dir == OB_DIRECTION_NORTH || o->dir == OB_DIRECTION_SOUTH) && + data->client->shaded)) + { + return FALSE; + } + + if (!o->shrink) { + /* try grow */ + client_find_resize_directional(data->client, o->dir, TRUE, + &x, &y, &w, &h); + if (do_grow(data, x, y, w, h)) + return FALSE; + } + + /* we couldn't grow, so try shrink! */ + opp = (o->dir == OB_DIRECTION_NORTH ? OB_DIRECTION_SOUTH : + (o->dir == OB_DIRECTION_SOUTH ? OB_DIRECTION_NORTH : + (o->dir == OB_DIRECTION_EAST ? OB_DIRECTION_WEST : + OB_DIRECTION_EAST))); + client_find_resize_directional(data->client, opp, FALSE, + &x, &y, &w, &h); + switch (opp) { + case OB_DIRECTION_NORTH: + half = data->client->area.y + data->client->area.height / 2; + if (y > half) { + h += y - half; + y = half; + } + break; + case OB_DIRECTION_SOUTH: + half = data->client->area.height / 2; + if (h < half) + h = half; + break; + case OB_DIRECTION_WEST: + half = data->client->area.x + data->client->area.width / 2; + if (x > half) { + w += x - half; + x = half; + } + break; + case OB_DIRECTION_EAST: + half = data->client->area.width / 2; + if (w < half) + w = half; + break; + default: g_assert_not_reached(); + } + if (do_grow(data, x, y, w, h)) + return FALSE; + + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_north_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->shrink = FALSE; + o->dir = OB_DIRECTION_NORTH; + return o; +} + +static gpointer setup_south_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->shrink = FALSE; + o->dir = OB_DIRECTION_SOUTH; + return o; +} + +static gpointer setup_east_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->shrink = FALSE; + o->dir = OB_DIRECTION_EAST; + return o; +} + +static gpointer setup_west_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->shrink = FALSE; + o->dir = OB_DIRECTION_WEST; + return o; +} diff --git a/openbox/actions/iconify.c b/openbox/actions/iconify.c new file mode 100644 index 0000000..e6bdbb7 --- /dev/null +++ b/openbox/actions/iconify.c @@ -0,0 +1,23 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_iconify_startup(void) +{ + actions_register("Iconify", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_iconify(data->client, TRUE, TRUE, FALSE); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/if.c b/openbox/actions/if.c new file mode 100644 index 0000000..0e055a9 --- /dev/null +++ b/openbox/actions/if.c @@ -0,0 +1,213 @@ +#include "openbox/actions.h" +#include "openbox/misc.h" +#include "openbox/client.h" +#include "openbox/frame.h" +#include "openbox/screen.h" +#include "openbox/focus.h" +#include <glib.h> + +typedef struct { + gboolean shaded_on; + gboolean shaded_off; + gboolean maxvert_on; + gboolean maxvert_off; + gboolean maxhorz_on; + gboolean maxhorz_off; + gboolean maxfull_on; + gboolean maxfull_off; + gboolean iconic_on; + gboolean iconic_off; + gboolean focused; + gboolean unfocused; + gboolean urgent_on; + gboolean urgent_off; + gboolean decor_off; + gboolean decor_on; + gboolean omnipresent_on; + gboolean omnipresent_off; + gboolean desktop_current; + gboolean desktop_other; + guint desktop_number; + GPatternSpec *matchtitle; + GSList *thenacts; + GSList *elseacts; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_if_startup(void) +{ + actions_register("If", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "shaded"))) { + if (obt_xml_node_bool(n)) + o->shaded_on = TRUE; + else + o->shaded_off = TRUE; + } + if ((n = obt_xml_find_node(node, "maximized"))) { + if (obt_xml_node_bool(n)) + o->maxfull_on = TRUE; + else + o->maxfull_off = TRUE; + } + if ((n = obt_xml_find_node(node, "maximizedhorizontal"))) { + if (obt_xml_node_bool(n)) + o->maxhorz_on = TRUE; + else + o->maxhorz_off = TRUE; + } + if ((n = obt_xml_find_node(node, "maximizedvertical"))) { + if (obt_xml_node_bool(n)) + o->maxvert_on = TRUE; + else + o->maxvert_off = TRUE; + } + if ((n = obt_xml_find_node(node, "iconified"))) { + if (obt_xml_node_bool(n)) + o->iconic_on = TRUE; + else + o->iconic_off = TRUE; + } + if ((n = obt_xml_find_node(node, "focused"))) { + if (obt_xml_node_bool(n)) + o->focused = TRUE; + else + o->unfocused = TRUE; + } + if ((n = obt_xml_find_node(node, "urgent"))) { + if (obt_xml_node_bool(n)) + o->urgent_on = TRUE; + else + o->urgent_off = TRUE; + } + if ((n = obt_xml_find_node(node, "undecorated"))) { + if (obt_xml_node_bool(n)) + o->decor_off = TRUE; + else + o->decor_on = TRUE; + } + if ((n = obt_xml_find_node(node, "desktop"))) { + gchar *s; + if ((s = obt_xml_node_string(n))) { + if (!g_ascii_strcasecmp(s, "current")) + o->desktop_current = TRUE; + if (!g_ascii_strcasecmp(s, "other")) + o->desktop_other = TRUE; + else + o->desktop_number = atoi(s); + g_free(s); + } + } + if ((n = obt_xml_find_node(node, "omnipresent"))) { + if (obt_xml_node_bool(n)) + o->omnipresent_on = TRUE; + else + o->omnipresent_off = TRUE; + } + if ((n = obt_xml_find_node(node, "title"))) { + gchar *s; + if ((s = obt_xml_node_string(n))) { + o->matchtitle = g_pattern_spec_new(s); + g_free(s); + } + } + + if ((n = obt_xml_find_node(node, "then"))) { + xmlNodePtr m; + + m = obt_xml_find_node(n->children, "action"); + while (m) { + ObActionsAct *action = actions_parse(m); + if (action) o->thenacts = g_slist_append(o->thenacts, action); + m = obt_xml_find_node(m->next, "action"); + } + } + if ((n = obt_xml_find_node(node, "else"))) { + xmlNodePtr m; + + m = obt_xml_find_node(n->children, "action"); + while (m) { + ObActionsAct *action = actions_parse(m); + if (action) o->elseacts = g_slist_append(o->elseacts, action); + m = obt_xml_find_node(m->next, "action"); + } + } + + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + + while (o->thenacts) { + actions_act_unref(o->thenacts->data); + o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts); + } + while (o->elseacts) { + actions_act_unref(o->elseacts->data); + o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts); + } + if (o->matchtitle) + g_pattern_spec_free(o->matchtitle); + + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + GSList *acts; + ObClient *c = data->client; + + if (c && + (!o->shaded_on || c->shaded) && + (!o->shaded_off || !c->shaded) && + (!o->iconic_on || c->iconic) && + (!o->iconic_off || !c->iconic) && + (!o->maxhorz_on || c->max_horz) && + (!o->maxhorz_off || !c->max_horz) && + (!o->maxvert_on || c->max_vert) && + (!o->maxvert_off || !c->max_vert) && + (!o->maxfull_on || (c->max_vert && c->max_horz)) && + (!o->maxfull_off || !(c->max_vert && c->max_horz)) && + (!o->focused || (c == focus_client)) && + (!o->unfocused || !(c == focus_client)) && + (!o->urgent_on || (c->urgent || c->demands_attention)) && + (!o->urgent_off || !(c->urgent || c->demands_attention)) && + (!o->decor_off || (c->undecorated || !(c->decorations & OB_FRAME_DECOR_TITLEBAR))) && + (!o->decor_on || (!c->undecorated && (c->decorations & OB_FRAME_DECOR_TITLEBAR))) && + (!o->omnipresent_on || (c->desktop == DESKTOP_ALL)) && + (!o->omnipresent_off || (c->desktop != DESKTOP_ALL)) && + (!o->desktop_current || ((c->desktop == screen_desktop) || + (c->desktop == DESKTOP_ALL))) && + (!o->desktop_other || ((c->desktop != screen_desktop) && + (c->desktop != DESKTOP_ALL))) && + (!o->desktop_number || ((c->desktop == o->desktop_number - 1) || + (c->desktop == DESKTOP_ALL))) && + (!o->matchtitle || + (g_pattern_match_string(o->matchtitle, c->original_title)))) + { + acts = o->thenacts; + } + else + acts = o->elseacts; + + actions_run_acts(acts, data->uact, data->state, + data->x, data->y, data->button, + data->context, data->client); + + return FALSE; +} diff --git a/openbox/actions/kill.c b/openbox/actions/kill.c new file mode 100644 index 0000000..b7d547b --- /dev/null +++ b/openbox/actions/kill.c @@ -0,0 +1,20 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_kill_startup(void) +{ + actions_register("Kill", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) + client_kill(data->client); + + return FALSE; +} diff --git a/openbox/actions/layer.c b/openbox/actions/layer.c new file mode 100644 index 0000000..ed1eeed --- /dev/null +++ b/openbox/actions/layer.c @@ -0,0 +1,132 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +typedef struct { + gint layer; /*!< -1 for below, 0 for normal, and 1 for above */ + gboolean toggle; +} Options; + +static gpointer setup_func_top(xmlNodePtr node); +static gpointer setup_func_bottom(xmlNodePtr node); +static gpointer setup_func_send(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_sendtop_func(xmlNodePtr node); +static gpointer setup_sendbottom_func(xmlNodePtr node); +static gpointer setup_sendnormal_func(xmlNodePtr node); + +void action_layer_startup(void) +{ + actions_register("ToggleAlwaysOnTop", setup_func_top, free_func, + run_func); + actions_register("ToggleAlwaysOnBottom", setup_func_bottom, free_func, + run_func); + actions_register("SendToLayer", setup_func_send, free_func, + run_func); + /* 3.4-compatibility */ + actions_register("SendToTopLayer", setup_sendtop_func, free_func, + run_func); + actions_register("SendToBottomLayer", setup_sendbottom_func, free_func, + run_func); + actions_register("SendToNormalLayer", setup_sendnormal_func, free_func, + run_func); +} + +static gpointer setup_func_top(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->layer = 1; + o->toggle = TRUE; + return o; +} + +static gpointer setup_func_bottom(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->layer = -1; + o->toggle = TRUE; + return o; +} + +static gpointer setup_func_send(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "layer"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "above") || + !g_ascii_strcasecmp(s, "top")) + o->layer = 1; + else if (!g_ascii_strcasecmp(s, "below") || + !g_ascii_strcasecmp(s, "bottom")) + o->layer = -1; + else if (!g_ascii_strcasecmp(s, "normal") || + !g_ascii_strcasecmp(s, "middle")) + o->layer = 0; + g_free(s); + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + ObClient *c = data->client; + + actions_client_move(data, TRUE); + + if (o->layer < 0) { + if (o->toggle || !c->below) + client_set_layer(c, c->below ? 0 : -1); + } + else if (o->layer > 0) { + if (o->toggle || !c->above) + client_set_layer(c, c->above ? 0 : 1); + } + else if (c->above || c->below) + client_set_layer(c, 0); + + actions_client_move(data, FALSE); + } + + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_sendtop_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->layer = 1; + o->toggle = FALSE; + return o; +} + +static gpointer setup_sendbottom_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->layer = -1; + o->toggle = FALSE; + return o; +} + +static gpointer setup_sendnormal_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->layer = 0; + o->toggle = FALSE; + return o; +} + diff --git a/openbox/actions/lower.c b/openbox/actions/lower.c new file mode 100644 index 0000000..80ca6b8 --- /dev/null +++ b/openbox/actions/lower.c @@ -0,0 +1,24 @@ +#include "openbox/actions.h" +#include "openbox/stacking.h" +#include "openbox/window.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_lower_startup(void) +{ + actions_register("Lower", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + stacking_lower(CLIENT_AS_WINDOW(data->client)); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/maximize.c b/openbox/actions/maximize.c new file mode 100644 index 0000000..db7c36b --- /dev/null +++ b/openbox/actions/maximize.c @@ -0,0 +1,140 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +/* These match the values for client_maximize */ +typedef enum { + BOTH = 0, + HORZ = 1, + VERT = 2 +} MaxDirection; + +typedef struct { + MaxDirection dir; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func_on(ObActionsData *data, gpointer options); +static gboolean run_func_off(ObActionsData *data, gpointer options); +static gboolean run_func_toggle(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_both_func(xmlNodePtr node); +static gpointer setup_horz_func(xmlNodePtr node); +static gpointer setup_vert_func(xmlNodePtr node); + +void action_maximize_startup(void) +{ + actions_register("Maximize", setup_func, free_func, run_func_on); + actions_register("Unmaximize", setup_func, free_func, run_func_off); + actions_register("ToggleMaximize", setup_func, free_func, run_func_toggle); + /* 3.4-compatibility */ + actions_register("MaximizeFull", setup_both_func, free_func, + run_func_on); + actions_register("UnmaximizeFull", setup_both_func, free_func, + run_func_off); + actions_register("ToggleMaximizeFull", setup_both_func, free_func, + run_func_toggle); + actions_register("MaximizeHorz", setup_horz_func, free_func, + run_func_on); + actions_register("UnmaximizeHorz", setup_horz_func, free_func, + run_func_off); + actions_register("ToggleMaximizeHorz", setup_horz_func, free_func, + run_func_toggle); + actions_register("MaximizeVert", setup_vert_func, free_func, + run_func_on); + actions_register("UnmaximizeVert", setup_vert_func, free_func, + run_func_off); + actions_register("ToggleMaximizeVert", setup_vert_func, free_func, + run_func_toggle); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->dir = BOTH; + + if ((n = obt_xml_find_node(node, "direction"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "vertical") || + !g_ascii_strcasecmp(s, "vert")) + o->dir = VERT; + else if (!g_ascii_strcasecmp(s, "horizontal") || + !g_ascii_strcasecmp(s, "horz")) + o->dir = HORZ; + g_free(s); + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_on(ObActionsData *data, gpointer options) +{ + Options *o = options; + if (data->client) { + actions_client_move(data, TRUE); + client_maximize(data->client, TRUE, o->dir); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_off(ObActionsData *data, gpointer options) +{ + Options *o = options; + if (data->client) { + actions_client_move(data, TRUE); + client_maximize(data->client, FALSE, o->dir); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_toggle(ObActionsData *data, gpointer options) +{ + Options *o = options; + if (data->client) { + gboolean toggle; + actions_client_move(data, TRUE); + toggle = ((o->dir == HORZ && !data->client->max_horz) || + (o->dir == VERT && !data->client->max_vert) || + (o->dir == BOTH && + !(data->client->max_horz && data->client->max_vert))); + client_maximize(data->client, toggle, o->dir); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_both_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = BOTH; + return o; +} + +static gpointer setup_horz_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = HORZ; + return o; +} + +static gpointer setup_vert_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = VERT; + return o; +} + diff --git a/openbox/actions/move.c b/openbox/actions/move.c new file mode 100644 index 0000000..ba8372a --- /dev/null +++ b/openbox/actions/move.c @@ -0,0 +1,28 @@ +#include "openbox/actions.h" +#include "openbox/moveresize.h" +#include "obt/prop.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_move_startup(void) +{ + actions_register("Move", + NULL, NULL, + run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) { + guint32 corner; + + corner = data->button != 0 ? + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) : + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD); + + moveresize_start(data->client, data->x, data->y, data->button, corner); + } + + return FALSE; +} diff --git a/openbox/actions/moverelative.c b/openbox/actions/moverelative.c new file mode 100644 index 0000000..b67b5cf --- /dev/null +++ b/openbox/actions/moverelative.c @@ -0,0 +1,84 @@ +#include "openbox/actions.h" +#include "openbox/client.h" +#include "openbox/screen.h" +#include "openbox/frame.h" +#include "openbox/config.h" + +typedef struct { + gint x; + gint x_denom; + gint y; + gint y_denom; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_moverelative_startup(void) +{ + actions_register("MoveRelative", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + gchar *s; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "x"))) { + s = obt_xml_node_string(n); + config_parse_relative_number(s, &o->x, &o->x_denom); + g_free(s); + } + if ((n = obt_xml_find_node(node, "y"))) { + s = obt_xml_node_string(n); + config_parse_relative_number(s, &o->y, &o->y_denom); + g_free(s); + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + ObClient *c; + gint x, y, lw, lh, w, h; + + c = data->client; + x = o->x; + y = o->y; + if (o->x_denom || o->y_denom) { + const Rect *carea; + + carea = screen_area(c->desktop, client_monitor(c), NULL); + if (o->x_denom) + x = (x * carea->width) / o->x_denom; + if (o->y_denom) + y = (y * carea->height) / o->y_denom; + } + x = c->area.x + x; + y = c->area.y + y; + w = c->area.width; + h = c->area.height; + client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE); + client_find_onscreen(c, &x, &y, w, h, FALSE); + + actions_client_move(data, TRUE); + client_configure(c, x, y, w, h, TRUE, TRUE, FALSE); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/moveresizeto.c b/openbox/actions/moveresizeto.c new file mode 100644 index 0000000..c23661c --- /dev/null +++ b/openbox/actions/moveresizeto.c @@ -0,0 +1,189 @@ +#include "openbox/actions.h" +#include "openbox/client.h" +#include "openbox/screen.h" +#include "openbox/frame.h" +#include "openbox/config.h" + +enum { + CURRENT_MONITOR = -1, + ALL_MONITORS = -2, + NEXT_MONITOR = -3, + PREV_MONITOR = -4 +}; + +typedef struct { + GravityCoord x; + GravityCoord y; + gint w; + gint w_denom; + gint h; + gint h_denom; + gint monitor; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_center_func(xmlNodePtr node); + +void action_moveresizeto_startup(void) +{ + actions_register("MoveResizeTo", setup_func, free_func, run_func); + /* 3.4-compatibility */ + actions_register("MoveToCenter", setup_center_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->x.pos = G_MININT; + o->y.pos = G_MININT; + o->w = G_MININT; + o->h = G_MININT; + o->monitor = CURRENT_MONITOR; + + if ((n = obt_xml_find_node(node, "x"))) + config_parse_gravity_coord(n, &o->x); + + if ((n = obt_xml_find_node(node, "y"))) + config_parse_gravity_coord(n, &o->y); + + if ((n = obt_xml_find_node(node, "width"))) { + gchar *s = obt_xml_node_string(n); + if (g_ascii_strcasecmp(s, "current") != 0) + config_parse_relative_number(s, &o->w, &o->w_denom); + g_free(s); + } + if ((n = obt_xml_find_node(node, "height"))) { + gchar *s = obt_xml_node_string(n); + if (g_ascii_strcasecmp(s, "current") != 0) + config_parse_relative_number(s, &o->h, &o->h_denom); + g_free(s); + } + + if ((n = obt_xml_find_node(node, "monitor"))) { + gchar *s = obt_xml_node_string(n); + if (g_ascii_strcasecmp(s, "current") != 0) { + if (!g_ascii_strcasecmp(s, "all")) + o->monitor = ALL_MONITORS; + else if(!g_ascii_strcasecmp(s, "next")) + o->monitor = NEXT_MONITOR; + else if(!g_ascii_strcasecmp(s, "prev")) + o->monitor = PREV_MONITOR; + else + o->monitor = obt_xml_node_int(n) - 1; + } + g_free(s); + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + Rect *area, *carea; + ObClient *c; + guint mon, cmon; + gint x, y, lw, lh, w, h; + + c = data->client; + mon = o->monitor; + cmon = client_monitor(c); + switch (mon) { + case CURRENT_MONITOR: + mon = cmon; break; + case ALL_MONITORS: + mon = SCREEN_AREA_ALL_MONITORS; break; + case NEXT_MONITOR: + mon = (cmon + 1 > screen_num_monitors - 1) ? 0 : (cmon + 1); break; + case PREV_MONITOR: + mon = (cmon == 0) ? (screen_num_monitors - 1) : (cmon - 1); break; + default: + g_assert_not_reached(); + } + + area = screen_area(c->desktop, mon, NULL); + carea = screen_area(c->desktop, cmon, NULL); + + w = o->w; + if (w == G_MININT) w = c->area.width; + else if (o->w_denom) w = (w * area->width) / o->w_denom; + + h = o->h; + if (h == G_MININT) h = c->area.height; + else if (o->h_denom) h = (h * area->height) / o->h_denom; + + /* it might not be able to resize how they requested, so find out what + it will actually be resized to */ + x = c->area.x; + y = c->area.y; + client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE); + + /* get the frame's size */ + w += c->frame->size.left + c->frame->size.right; + h += c->frame->size.top + c->frame->size.bottom; + + x = o->x.pos; + if (o->x.denom) + x = (x * area->width) / o->x.denom; + if (o->x.center) x = (area->width - w) / 2; + else if (x == G_MININT) x = c->frame->area.x - carea->x; + else if (o->x.opposite) x = area->width - w - x; + x += area->x; + + y = o->y.pos; + if (o->y.denom) + y = (y * area->height) / o->y.denom; + if (o->y.center) y = (area->height - h) / 2; + else if (y == G_MININT) y = c->frame->area.y - carea->y; + else if (o->y.opposite) y = area->height - h - y; + y += area->y; + + /* get the client's size back */ + w -= c->frame->size.left + c->frame->size.right; + h -= c->frame->size.top + c->frame->size.bottom; + + frame_frame_gravity(c->frame, &x, &y); /* get the client coords */ + client_try_configure(c, &x, &y, &w, &h, &lw, &lh, TRUE); + /* force it on screen if its moving to another monitor */ + client_find_onscreen(c, &x, &y, w, h, mon != cmon); + + actions_client_move(data, TRUE); + client_configure(c, x, y, w, h, TRUE, TRUE, FALSE); + actions_client_move(data, FALSE); + + g_slice_free(Rect, area); + g_slice_free(Rect, carea); + } + + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_center_func(xmlNodePtr node) +{ + Options *o; + + o = g_slice_new0(Options); + o->x.pos = G_MININT; + o->y.pos = G_MININT; + o->w = G_MININT; + o->h = G_MININT; + o->monitor = -1; + o->x.center = TRUE; + o->y.center = TRUE; + return o; +} diff --git a/openbox/actions/movetoedge.c b/openbox/actions/movetoedge.c new file mode 100644 index 0000000..ef5b692 --- /dev/null +++ b/openbox/actions/movetoedge.c @@ -0,0 +1,111 @@ +#include "openbox/actions.h" +#include "openbox/misc.h" +#include "openbox/client.h" +#include "openbox/frame.h" +#include "openbox/geom.h" +#include <glib.h> + +typedef struct { + ObDirection dir; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); +/* 3.4-compatibility */ +static gpointer setup_north_func(xmlNodePtr node); +static gpointer setup_south_func(xmlNodePtr node); +static gpointer setup_east_func(xmlNodePtr node); +static gpointer setup_west_func(xmlNodePtr node); + +void action_movetoedge_startup(void) +{ + actions_register("MoveToEdge", setup_func, free_func, run_func); + /* 3.4-compatibility */ + actions_register("MoveToEdgeNorth", setup_north_func, free_func, run_func); + actions_register("MoveToEdgeSouth", setup_south_func, free_func, run_func); + actions_register("MoveToEdgeEast", setup_east_func, free_func, run_func); + actions_register("MoveToEdgeWest", setup_west_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + o->dir = OB_DIRECTION_NORTH; + + if ((n = obt_xml_find_node(node, "direction"))) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "north") || + !g_ascii_strcasecmp(s, "up")) + o->dir = OB_DIRECTION_NORTH; + else if (!g_ascii_strcasecmp(s, "south") || + !g_ascii_strcasecmp(s, "down")) + o->dir = OB_DIRECTION_SOUTH; + else if (!g_ascii_strcasecmp(s, "west") || + !g_ascii_strcasecmp(s, "left")) + o->dir = OB_DIRECTION_WEST; + else if (!g_ascii_strcasecmp(s, "east") || + !g_ascii_strcasecmp(s, "right")) + o->dir = OB_DIRECTION_EAST; + g_free(s); + } + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + gint x, y; + + client_find_move_directional(data->client, o->dir, &x, &y); + if (x != data->client->area.x || y != data->client->area.y) { + actions_client_move(data, TRUE); + client_move(data->client, x, y); + actions_client_move(data, FALSE); + } + } + + return FALSE; +} + +/* 3.4-compatibility */ +static gpointer setup_north_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = OB_DIRECTION_NORTH; + return o; +} + +static gpointer setup_south_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = OB_DIRECTION_SOUTH; + return o; +} + +static gpointer setup_east_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = OB_DIRECTION_EAST; + return o; +} + +static gpointer setup_west_func(xmlNodePtr node) +{ + Options *o = g_slice_new0(Options); + o->dir = OB_DIRECTION_WEST; + return o; +} + diff --git a/openbox/actions/omnipresent.c b/openbox/actions/omnipresent.c new file mode 100644 index 0000000..4309acc --- /dev/null +++ b/openbox/actions/omnipresent.c @@ -0,0 +1,23 @@ +#include "openbox/actions.h" +#include "openbox/client.h" +#include "openbox/screen.h" + +static gboolean run_func_toggle(ObActionsData *data, gpointer options); + +void action_omnipresent_startup(void) +{ + actions_register("ToggleOmnipresent", NULL, NULL, run_func_toggle); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_toggle(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_set_desktop(data->client, + data->client->desktop == DESKTOP_ALL ? + screen_desktop : DESKTOP_ALL, FALSE, TRUE); + actions_client_move(data, FALSE); + } + return FALSE; +} diff --git a/openbox/actions/raise.c b/openbox/actions/raise.c new file mode 100644 index 0000000..f6ac145 --- /dev/null +++ b/openbox/actions/raise.c @@ -0,0 +1,22 @@ +#include "openbox/actions.h" +#include "openbox/stacking.h" +#include "openbox/window.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_raise_startup(void) +{ + actions_register("Raise", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + stacking_raise(CLIENT_AS_WINDOW(data->client)); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/raiselower.c b/openbox/actions/raiselower.c new file mode 100644 index 0000000..dbe41d8 --- /dev/null +++ b/openbox/actions/raiselower.c @@ -0,0 +1,21 @@ +#include "openbox/actions.h" +#include "openbox/stacking.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_raiselower_startup(void) +{ + actions_register("RaiseLower", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + stacking_restack_request(data->client, NULL, Opposite); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/reconfigure.c b/openbox/actions/reconfigure.c new file mode 100644 index 0000000..813a122 --- /dev/null +++ b/openbox/actions/reconfigure.c @@ -0,0 +1,17 @@ +#include "openbox/actions.h" +#include "openbox/openbox.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_reconfigure_startup(void) +{ + actions_register("Reconfigure", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + ob_reconfigure(); + + return FALSE; +} diff --git a/openbox/actions/resize.c b/openbox/actions/resize.c new file mode 100644 index 0000000..f6858d2 --- /dev/null +++ b/openbox/actions/resize.c @@ -0,0 +1,198 @@ +#include "openbox/actions.h" +#include "openbox/moveresize.h" +#include "openbox/client.h" +#include "openbox/frame.h" +#include "obt/prop.h" + +typedef struct { + gboolean corner_specified; + guint32 corner; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer o); +static gboolean run_func(ObActionsData *data, gpointer options); + +static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch, + gboolean shaded); + +void action_resize_startup(void) +{ + actions_register("Resize", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "edge"))) { + gchar *s = obt_xml_node_string(n); + + o->corner_specified = TRUE; + if (!g_ascii_strcasecmp(s, "top")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP); + else if (!g_ascii_strcasecmp(s, "bottom")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM); + else if (!g_ascii_strcasecmp(s, "left")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT); + else if (!g_ascii_strcasecmp(s, "right")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT); + else if (!g_ascii_strcasecmp(s, "topleft")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT); + else if (!g_ascii_strcasecmp(s, "topright")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT); + else if (!g_ascii_strcasecmp(s, "bottomleft")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT); + else if (!g_ascii_strcasecmp(s, "bottomright")) + o->corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT); + else + o->corner_specified = FALSE; + + g_free(s); + } + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + ObClient *c = data->client; + guint32 corner; + + if (!data->button) + corner = OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD); + else if (o->corner_specified) + corner = o->corner; /* it was specified in the binding */ + else + corner = pick_corner(data->x, data->y, + c->frame->area.x, c->frame->area.y, + /* use the client size because the frame + can be differently sized (shaded + windows) and we want this based on the + clients size */ + c->area.width + c->frame->size.left + + c->frame->size.right, + c->area.height + c->frame->size.top + + c->frame->size.bottom, c->shaded); + + moveresize_start(c, data->x, data->y, data->button, corner); + } + + return FALSE; +} + +static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch, + gboolean shaded) +{ + /* let's make x and y client relative instead of screen relative */ + x = x - cx; + y = ch - (y - cy); /* y is inverted, 0 is at the bottom of the window */ + +#define X x*ch/cw +#define A -4*X + 7*ch/3 +#define B 4*X -15*ch/9 +#define C -X/4 + 2*ch/3 +#define D X/4 + 5*ch/12 +#define E X/4 + ch/3 +#define F -X/4 + 7*ch/12 +#define G 4*X - 4*ch/3 +#define H -4*X + 8*ch/3 +#define a (y > 5*ch/9) +#define b (x < 4*cw/9) +#define c (x > 5*cw/9) +#define d (y < 4*ch/9) + + /* + Each of these defines (except X which is just there for fun), represents + the equation of a line. The lines they represent are shown in the diagram + below. Checking y against these lines, we are able to choose a region + of the window as shown. + + +---------------------A-------|-------|-------B---------------------+ + | |A B| | + | |A | | B| | + | | A B | | + | | A | | B | | + | | A B | | + | | A | | B | | + | northwest | A north B | northeast | + | | A | | B | | + | | A B | | + C---------------------+----A--+-------+--B----+---------------------D + |CCCCCCC | A B | DDDDDDD| + | CCCCCCCC | A | | B | DDDDDDDD | + | CCCCCCC A B DDDDDDD | + - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - - + | | b c | | sh + | west | b move c | east | ad + | | b c | | ed + - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - - + | EEEEEEE G H FFFFFFF | + | EEEEEEEE | G | | H | FFFFFFFF | + |EEEEEEE | G H | FFFFFFF| + E---------------------+----G--+-------+--H----+---------------------F + | | G H | | + | | G | | H | | + | southwest | G south H | southeast | + | | G | | H | | + | | G H | | + | | G | | H | | + | | G H | | + | |G | | H| | + | |G H| | + +---------------------G-------|-------|-------H---------------------+ + */ + + if (shaded) { + /* for shaded windows, you can only resize west/east and move */ + if (b) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT); + if (c) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT); + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE); + } + + if (y < A && y >= C) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT); + else if (y >= A && y >= B && a) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP); + else if (y < B && y >= D) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT); + else if (y < C && y >= E && b) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT); + else if (y < D && y >= F && c) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT); + else if (y < E && y >= G) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT); + else if (y < G && y < H && d) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM); + else if (y >= H && y < F) + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT); + else + return OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE); + +#undef X +#undef A +#undef B +#undef C +#undef D +#undef E +#undef F +#undef G +#undef H +#undef a +#undef b +#undef c +#undef d +} diff --git a/openbox/actions/resizerelative.c b/openbox/actions/resizerelative.c new file mode 100644 index 0000000..e32aff3 --- /dev/null +++ b/openbox/actions/resizerelative.c @@ -0,0 +1,104 @@ +#include "openbox/actions.h" +#include "openbox/client.h" +#include "openbox/screen.h" +#include "openbox/frame.h" +#include "openbox/config.h" + +typedef struct { + gint left; + gint left_denom; + gint right; + gint right_denom; + gint top; + gint top_denom; + gint bottom; + gint bottom_denom; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_resizerelative_startup(void) +{ + actions_register("ResizeRelative", setup_func, free_func, run_func); +} + +static void xml_node_relative(xmlNodePtr n, gint *num, gint *denom) +{ + gchar *s; + + s = obt_xml_node_string(n); + config_parse_relative_number(s, num, denom); + g_free(s); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "left"))) + xml_node_relative(n, &o->left, &o->left_denom); + if ((n = obt_xml_find_node(node, "right"))) + xml_node_relative(n, &o->right, &o->right_denom); + if ((n = obt_xml_find_node(node, "top")) || + (n = obt_xml_find_node(node, "up"))) + xml_node_relative(n, &o->top, &o->top_denom); + if ((n = obt_xml_find_node(node, "bottom")) || + (n = obt_xml_find_node(node, "down"))) + xml_node_relative(n, &o->bottom, &o->bottom_denom); + + return o; +} + +static void free_func(gpointer o) +{ + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + if (data->client) { + ObClient *c = data->client; + gint x, y, ow, xoff, nw, oh, yoff, nh, lw, lh; + gint left = o->left, right = o->right, top = o->top, bottom = o->bottom; + + if (o->left_denom) + left = (left * c->area.width / c->size_inc.width) / o->left_denom; + if (o->right_denom) + right = (right * c->area.width / c->size_inc.width) / o->right_denom; + if (o->top_denom) + top = (top * c->area.height / c->size_inc.height) / o->top_denom; + if (o->bottom_denom) + bottom = (bottom * c->area.height / c->size_inc.height) / o->bottom_denom; + + x = c->area.x; + y = c->area.y; + ow = c->area.width; + xoff = -left * c->size_inc.width; + nw = ow + right * c->size_inc.width + + left * c->size_inc.width; + oh = c->area.height; + yoff = -top * c->size_inc.height; + nh = oh + bottom * c->size_inc.height + + top * c->size_inc.height; + + client_try_configure(c, &x, &y, &nw, &nh, &lw, &lh, TRUE); + xoff = xoff == 0 ? 0 : + (xoff < 0 ? MAX(xoff, ow-nw) : MIN(xoff, ow-nw)); + yoff = yoff == 0 ? 0 : + (yoff < 0 ? MAX(yoff, oh-nh) : MIN(yoff, oh-nh)); + + actions_client_move(data, TRUE); + client_move_resize(c, x + xoff, y + yoff, nw, nh); + actions_client_move(data, FALSE); + } + + return FALSE; +} diff --git a/openbox/actions/restart.c b/openbox/actions/restart.c new file mode 100644 index 0000000..dc9a218 --- /dev/null +++ b/openbox/actions/restart.c @@ -0,0 +1,50 @@ +#include "openbox/actions.h" +#include "openbox/openbox.h" +#include "obt/paths.h" + +typedef struct { + gchar *cmd; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_restart_startup(void) +{ + actions_register("Restart", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "command")) || + (n = obt_xml_find_node(node, "execute"))) + { + gchar *s = obt_xml_node_string(n); + o->cmd = obt_paths_expand_tilde(s); + g_free(s); + } + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + g_free(o->cmd); + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + ob_restart_other(o->cmd); + + return FALSE; +} diff --git a/openbox/actions/shade.c b/openbox/actions/shade.c new file mode 100644 index 0000000..502781d --- /dev/null +++ b/openbox/actions/shade.c @@ -0,0 +1,46 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func_on(ObActionsData *data, gpointer options); +static gboolean run_func_off(ObActionsData *data, gpointer options); +static gboolean run_func_toggle(ObActionsData *data, gpointer options); + +void action_shade_startup(void) +{ + actions_register("Shade", NULL, NULL, run_func_on); + actions_register("Unshade", NULL, NULL, run_func_off); + actions_register("ToggleShade", NULL, NULL, run_func_toggle); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_on(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_shade(data->client, TRUE); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_off(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_shade(data->client, FALSE); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_toggle(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + client_shade(data->client, !data->client->shaded); + actions_client_move(data, FALSE); + } + return FALSE; +} diff --git a/openbox/actions/shadelowerraise.c b/openbox/actions/shadelowerraise.c new file mode 100644 index 0000000..414e281 --- /dev/null +++ b/openbox/actions/shadelowerraise.c @@ -0,0 +1,40 @@ +#include "openbox/actions.h" +#include "openbox/client.h" + +static gboolean run_func_sl(ObActionsData *data, gpointer options); +static gboolean run_func_ur(ObActionsData *data, gpointer options); + +void action_shadelowerraise_startup() +{ + /* 3.4-compatibility */ + actions_register("ShadeLower", NULL, NULL, run_func_sl); + actions_register("UnshadeRaise", NULL, NULL, run_func_ur); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_sl(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + if (data->client->shaded) + stacking_lower(CLIENT_AS_WINDOW(data->client)); + else + client_shade(data->client, TRUE); + actions_client_move(data, FALSE); + } + return FALSE; +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func_ur(ObActionsData *data, gpointer options) +{ + if (data->client) { + actions_client_move(data, TRUE); + if (data->client->shaded) + client_shade(data->client, FALSE); + else + stacking_raise(CLIENT_AS_WINDOW(data->client)); + actions_client_move(data, FALSE); + } + return FALSE; +} diff --git a/openbox/actions/showdesktop.c b/openbox/actions/showdesktop.c new file mode 100644 index 0000000..6dc77d5 --- /dev/null +++ b/openbox/actions/showdesktop.c @@ -0,0 +1,17 @@ +#include "openbox/actions.h" +#include "openbox/screen.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_showdesktop_startup(void) +{ + actions_register("ToggleShowDesktop", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + screen_show_desktop(!screen_showing_desktop, NULL); + + return FALSE; +} diff --git a/openbox/actions/showmenu.c b/openbox/actions/showmenu.c new file mode 100644 index 0000000..485a31d --- /dev/null +++ b/openbox/actions/showmenu.c @@ -0,0 +1,47 @@ +#include "openbox/actions.h" +#include "openbox/menu.h" +#include <glib.h> + +typedef struct { + gchar *name; +} Options; + +static gpointer setup_func(xmlNodePtr node); +static void free_func(gpointer options); +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_showmenu_startup(void) +{ + actions_register("ShowMenu", setup_func, free_func, run_func); +} + +static gpointer setup_func(xmlNodePtr node) +{ + xmlNodePtr n; + Options *o; + + o = g_slice_new0(Options); + + if ((n = obt_xml_find_node(node, "menu"))) + o->name = obt_xml_node_string(n); + return o; +} + +static void free_func(gpointer options) +{ + Options *o = options; + g_free(o->name); + g_slice_free(Options, o); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + Options *o = options; + + /* you cannot call ShowMenu from inside a menu */ + if (data->uact != OB_USER_ACTION_MENU_SELECTION && o->name) + menu_show(o->name, data->x, data->y, data->button != 0, data->client); + + return FALSE; +} diff --git a/openbox/actions/unfocus.c b/openbox/actions/unfocus.c new file mode 100644 index 0000000..3db00ca --- /dev/null +++ b/openbox/actions/unfocus.c @@ -0,0 +1,17 @@ +#include "openbox/actions.h" +#include "openbox/focus.h" + +static gboolean run_func(ObActionsData *data, gpointer options); + +void action_unfocus_startup(void) +{ + actions_register("Unfocus", NULL, NULL, run_func); +} + +/* Always return FALSE because its not interactive */ +static gboolean run_func(ObActionsData *data, gpointer options) +{ + if (data->client && data->client == focus_client) + focus_fallback(FALSE, FALSE, TRUE, FALSE); + return FALSE; +} diff --git a/openbox/client.c b/openbox/client.c new file mode 100644 index 0000000..f3b4bda --- /dev/null +++ b/openbox/client.c @@ -0,0 +1,4603 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "client.h" +#include "debug.h" +#include "startupnotify.h" +#include "dock.h" +#include "screen.h" +#include "moveresize.h" +#include "ping.h" +#include "place.h" +#include "frame.h" +#include "session.h" +#include "event.h" +#include "grab.h" +#include "prompt.h" +#include "focus.h" +#include "focus_cycle.h" +#include "stacking.h" +#include "openbox.h" +#include "group.h" +#include "config.h" +#include "menuframe.h" +#include "keyboard.h" +#include "mouse.h" +#include "obrender/render.h" +#include "gettext.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/prop.h" + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifdef HAVE_SIGNAL_H +# include <signal.h> /* for kill() */ +#endif + +#include <glib.h> +#include <X11/Xutil.h> + +/*! The event mask to grab on client windows */ +#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \ + ColormapChangeMask) + +#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \ + ButtonMotionMask) + +typedef struct +{ + ObClientCallback func; + gpointer data; +} ClientCallback; + +GList *client_list = NULL; + +static GSList *client_destroy_notifies = NULL; +static RrImage *client_default_icon = NULL; + +static void client_get_all(ObClient *self, gboolean real); +static void client_get_startup_id(ObClient *self); +static void client_get_session_ids(ObClient *self); +static void client_save_app_rule_values(ObClient *self); +static void client_get_area(ObClient *self); +static void client_get_desktop(ObClient *self); +static void client_get_state(ObClient *self); +static void client_get_shaped(ObClient *self); +static void client_get_colormap(ObClient *self); +static void client_set_desktop_recursive(ObClient *self, + guint target, + gboolean donthide, + gboolean dontraise); +static void client_change_allowed_actions(ObClient *self); +static void client_change_state(ObClient *self); +static void client_change_wm_state(ObClient *self); +static void client_apply_startup_state(ObClient *self, + gint x, gint y, gint w, gint h); +static void client_restore_session_state(ObClient *self); +static gboolean client_restore_session_stacking(ObClient *self); +static ObAppSettings *client_get_settings_state(ObClient *self); +static void client_update_transient_tree(ObClient *self, + ObGroup *oldgroup, ObGroup *newgroup, + gboolean oldgtran, gboolean newgtran, + ObClient* oldparent, + ObClient *newparent); +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade); +static GSList *client_search_all_top_parents_internal(ObClient *self, + gboolean bylayer, + ObStackingLayer layer); +static void client_call_notifies(ObClient *self, GSList *list); +static void client_ping_event(ObClient *self, gboolean dead); +static void client_prompt_kill(ObClient *self); +static gboolean client_can_steal_focus(ObClient *self, + gboolean allow_other_desktop, + gboolean request_from_user, + Time steal_time, Time launch_time); +static void client_setup_default_decor_and_functions(ObClient *self); +static void client_setup_decor_undecorated(ObClient *self); + +void client_startup(gboolean reconfig) +{ + client_default_icon = RrImageNewFromData( + ob_rr_icons, ob_rr_theme->def_win_icon, + ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h); + + if (reconfig) return; + + client_set_list(); +} + +void client_shutdown(gboolean reconfig) +{ + RrImageUnref(client_default_icon); + client_default_icon = NULL; + + if (reconfig) return; +} + +static void client_call_notifies(ObClient *self, GSList *list) +{ + GSList *it; + + for (it = list; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + d->func(self, d->data); + } +} + +void client_add_destroy_notify(ObClientCallback func, gpointer data) +{ + ClientCallback *d = g_slice_new(ClientCallback); + d->func = func; + d->data = data; + client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d); +} + +void client_remove_destroy_notify(ObClientCallback func) +{ + GSList *it; + + for (it = client_destroy_notifies; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + if (d->func == func) { + g_slice_free(ClientCallback, d); + client_destroy_notifies = + g_slist_delete_link(client_destroy_notifies, it); + break; + } + } +} + +void client_set_list(void) +{ + Window *windows, *win_it; + GList *it; + guint size = g_list_length(client_list); + + /* create an array of the window ids */ + if (size > 0) { + windows = g_new(Window, size); + win_it = windows; + for (it = client_list; it; it = g_list_next(it), ++win_it) + *win_it = ((ObClient*)it->data)->window; + } else + windows = NULL; + + OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW, + (gulong*)windows, size); + + if (windows) + g_free(windows); + + stacking_set_list(); +} + +void client_manage(Window window, ObPrompt *prompt) +{ + ObClient *self; + XSetWindowAttributes attrib_set; + gboolean try_activate = FALSE; + gboolean do_activate; + ObAppSettings *settings; + gboolean transient = FALSE; + Rect place; + Time launch_time; + guint32 user_time; + gboolean obplaced; + + ob_debug("Managing window: 0x%lx", window); + + /* choose the events we want to receive on the CLIENT window + (ObPrompt windows can request events too) */ + attrib_set.event_mask = CLIENT_EVENTMASK | + (prompt ? prompt->event_mask : 0); + attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; + XChangeWindowAttributes(obt_display, window, + CWEventMask|CWDontPropagate, &attrib_set); + + /* create the ObClient struct, and populate it from the hints on the + window */ + self = g_slice_new0(ObClient); + self->obwin.type = OB_WINDOW_CLASS_CLIENT; + self->window = window; + self->prompt = prompt; + self->managed = TRUE; + + /* non-zero defaults */ + self->wmstate = WithdrawnState; /* make sure it gets updated first time */ + self->gravity = NorthWestGravity; + self->desktop = screen_num_desktops; /* always an invalid value */ + + /* get all the stuff off the window */ + client_get_all(self, TRUE); + + ob_debug("Window type: %d", self->type); + ob_debug("Window group: 0x%x", self->group?self->group->leader:0); + ob_debug("Window name: %s class: %s role: %s title: %s", + self->name, self->class, self->role, self->title); + + /* per-app settings override stuff from client_get_all, and return the + settings for other uses too. the returned settings is a shallow copy, + that needs to be freed with g_free(). */ + settings = client_get_settings_state(self); + + /* the session should get the last say though */ + client_restore_session_state(self); + + /* the per-app settings/session may have changed the decorations for + the window, so we setup decorations for that here. this is a special + case because we want to place the window according to these decoration + changes. + we do this before setting up the frame so that it will reflect the + decorations of the window as it will be placed on screen. + */ + client_setup_decor_undecorated(self); + + /* specify that if we exit, the window should not be destroyed and + should be reparented back to root automatically, unless we are managing + an internal ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(obt_display, window, SetModeInsert); + + /* create the decoration frame for the client window */ + self->frame = frame_new(self); + + frame_grab_client(self->frame); + + /* we've grabbed everything and set everything that we need to at mapping + time now */ + grab_server(FALSE); + + /* this needs to occur once we have a frame, since it sets a property on + the frame */ + client_update_opacity(self); + + /* don't put helper/modal windows on a different desktop if they are + related to the focused window. */ + if (!screen_compare_desktops(self->desktop, screen_desktop) && + focus_client && client_search_transient(focus_client, self) && + (client_helper(self) || self->modal)) + { + self->desktop = screen_desktop; + } + + /* tell startup notification that this app started */ + launch_time = sn_app_started(self->startup_id, self->class, self->name); + + if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time)) + user_time = event_time(); + + /* do this after we have a frame.. it uses the frame to help determine the + WM_STATE to apply. */ + client_change_state(self); + + /* add ourselves to the focus order */ + focus_order_add_new(self); + + /* do this to add ourselves to the stacking list in a non-intrusive way */ + client_calc_layer(self); + + /* focus the new window? */ + if (ob_state() != OB_STATE_STARTING && + (!self->session || self->session->focused) && + /* this means focus=true for window is same as config_focus_new=true */ + ((config_focus_new || settings->focus == 1) || + client_search_focus_tree_full(self)) && + /* NET_WM_USER_TIME 0 when mapping means don't focus */ + (user_time != 0) && + /* this checks for focus=false for the window */ + settings->focus != 0 && + focus_valid_target(self, self->desktop, + FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, + settings->focus == 1)) + { + try_activate = TRUE; + } + + /* remove the client's border */ + XSetWindowBorderWidth(obt_display, self->window, 0); + + /* adjust the frame to the client's size before showing or placing + the window */ + frame_adjust_area(self->frame, FALSE, TRUE, FALSE); + frame_adjust_client_area(self->frame); + + /* where the frame was placed is where the window was originally */ + place = self->area; + + ob_debug("Going to try activate new window? %s", + try_activate ? "yes" : "no"); + if (try_activate) + do_activate = client_can_steal_focus( + self, settings->focus == 1, + (!!launch_time || settings->focus == 1), + event_time(), launch_time); + else + do_activate = FALSE; + + /* figure out placement for the window if the window is new */ + if (ob_state() == OB_STATE_RUNNING) { + ob_debug("Positioned: %s @ %d %d", + (!self->positioned ? "no" : + (self->positioned == PPosition ? "program specified" : + (self->positioned == USPosition ? "user specified" : + (self->positioned == (PPosition | USPosition) ? + "program + user specified" : + "BADNESS !?")))), place.x, place.y); + + ob_debug("Sized: %s @ %d %d", + (!self->sized ? "no" : + (self->sized == PSize ? "program specified" : + (self->sized == USSize ? "user specified" : + (self->sized == (PSize | USSize) ? + "program + user specified" : + "BADNESS !?")))), place.width, place.height); + + obplaced = place_client(self, do_activate, &place.x, &place.y, + settings); + + /* watch for buggy apps that ask to be placed at (0,0) when there is + a strut there */ + if (!obplaced && place.x == 0 && place.y == 0 && + /* non-normal windows are allowed */ + client_normal(self) && + /* oldschool fullscreen windows are allowed */ + !client_is_oldfullscreen(self, &place)) + { + Rect *r; + + r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL); + if (r->x || r->y) { + place.x = r->x; + place.y = r->y; + ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y); + } + g_slice_free(Rect, r); + } + + /* make sure the window is visible. */ + client_find_onscreen(self, &place.x, &place.y, + place.width, place.height, + /* non-normal clients has less rules, and + windows that are being restored from a + session do also. we can assume you want + it back where you saved it. Clients saying + they placed themselves are subjected to + harder rules, ones that are placed by + place.c or by the user are allowed partially + off-screen and on xinerama divides (ie, + it is up to the placement routines to avoid + the xinerama divides) + + children and splash screens are forced on + screen, but i don't remember why i decided to + do that. + */ + ob_state() == OB_STATE_RUNNING && + (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_SPLASH || + (!((self->positioned & USPosition) || + settings->pos_given) && + client_normal(self) && + !self->session && + /* don't move oldschool fullscreen windows to + fit inside the struts (fixes Acroread, which + makes its fullscreen window fit the screen + but it is not USSize'd or USPosition'd) */ + !client_is_oldfullscreen(self, &place)))); + } + + /* if the window isn't user-sized, then make it fit inside + the visible screen area on its monitor. Use basically the same rules + for forcing the window on screen in the client_find_onscreen call. + + do this after place_client, it chooses the monitor! + + splash screens get "transient" set to TRUE by + the place_client call + */ + if (ob_state() == OB_STATE_RUNNING && + (transient || + (!(self->sized & USSize || self->positioned & USPosition) && + client_normal(self) && + !self->session && + /* don't shrink oldschool fullscreen windows to fit inside the + struts (fixes Acroread, which makes its fullscreen window + fit the screen but it is not USSize'd or USPosition'd) */ + !client_is_oldfullscreen(self, &place)))) + { + Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place); + + /* get the size of the frame */ + place.width += self->frame->size.left + self->frame->size.right; + place.height += self->frame->size.top + self->frame->size.bottom; + + /* fit the window inside the area */ + place.width = MIN(place.width, a->width); + place.height = MIN(place.height, a->height); + + ob_debug("setting window size to %dx%d", place.width, place.height); + + /* get the size of the client back */ + place.width -= self->frame->size.left + self->frame->size.right; + place.height -= self->frame->size.top + self->frame->size.bottom; + + g_slice_free(Rect, a); + } + + ob_debug("placing window 0x%x at %d, %d with size %d x %d. " + "some restrictions may apply", + self->window, place.x, place.y, place.width, place.height); + if (self->session) + ob_debug(" but session requested %d, %d %d x %d instead, " + "overriding", + self->session->x, self->session->y, + self->session->w, self->session->h); + + /* do this after the window is placed, so the premax/prefullscreen numbers + won't be all wacko!! + + this also places the window + */ + client_apply_startup_state(self, place.x, place.y, + place.width, place.height); + + /* set the initial value of the desktop hint, when one wasn't requested + on map. */ + OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop); + + /* grab mouse bindings before showing the window */ + mouse_grab_for_client(self, TRUE); + + /* this has to happen before we try focus the window, but we want it to + happen after the client's stacking has been determined or it looks bad + */ + { + gulong ignore_start; + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + + client_show(self); + + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + } + + /* activate/hilight/raise the window */ + if (try_activate) { + if (do_activate) { + gboolean stacked = client_restore_session_stacking(self); + client_present(self, FALSE, !stacked, TRUE); + } + else { + /* if the client isn't stealing focus, then hilite it so the user + knows it is there, but don't do this if we're restoring from a + session */ + if (!client_restore_session_stacking(self)) + client_hilite(self, TRUE); + } + } + else { + /* This may look rather odd. Well it's because new windows are added + to the stacking order non-intrusively. If we're not going to focus + the new window or hilite it, then we raise it to the top. This will + take affect for things that don't get focused like splash screens. + Also if you don't have focus_new enabled, then it's going to get + raised to the top. Legacy begets legacy I guess? + */ + if (!client_restore_session_stacking(self)) + stacking_raise(CLIENT_AS_WINDOW(self)); + } + + /* add to client list/map */ + client_list = g_list_append(client_list, self); + window_add(&self->window, CLIENT_AS_WINDOW(self)); + + /* this has to happen after we're in the client_list */ + if (STRUT_EXISTS(self->strut)) + screen_update_areas(); + + /* update the list hints */ + client_set_list(); + + /* free the ObAppSettings shallow copy */ + g_slice_free(ObAppSettings, settings); + + ob_debug("Managed window 0x%lx plate 0x%x (%s)", + window, self->frame->window, self->class); +} + +ObClient *client_fake_manage(Window window) +{ + ObClient *self; + ObAppSettings *settings; + + ob_debug("Pretend-managing window: %lx", window); + + /* do this minimal stuff to figure out the client's decorations */ + + self = g_slice_new0(ObClient); + self->window = window; + + client_get_all(self, FALSE); + /* per-app settings override stuff, and return the settings for other + uses too. this returns a shallow copy that needs to be freed */ + settings = client_get_settings_state(self); + + /* create the decoration frame for the client window and adjust its size */ + self->frame = frame_new(self); + + client_apply_startup_state(self, self->area.x, self->area.y, + self->area.width, self->area.height); + + ob_debug("gave extents left %d right %d top %d bottom %d", + self->frame->size.left, self->frame->size.right, + self->frame->size.top, self->frame->size.bottom); + + /* free the ObAppSettings shallow copy */ + g_slice_free(ObAppSettings, settings); + + return self; +} + +void client_unmanage_all(void) +{ + while (client_list) + client_unmanage(client_list->data); +} + +void client_unmanage(ObClient *self) +{ + GSList *it; + gulong ignore_start; + + ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)", + self->window, self->frame->window, + self->class, self->title ? self->title : ""); + + g_assert(self != NULL); + + /* we dont want events no more. do this before hiding the frame so we + don't generate more events */ + XSelectInput(obt_display, self->window, NoEventMask); + + /* ignore enter events from the unmap so it doesnt mess with the focus */ + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + + frame_hide(self->frame); + /* flush to send the hide to the server quickly */ + XFlush(obt_display); + + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + + mouse_grab_for_client(self, FALSE); + + self->managed = FALSE; + + /* remove the window from our save set, unless we are managing an internal + ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(obt_display, self->window, SetModeDelete); + + /* update the focus lists */ + focus_order_remove(self); + if (client_focused(self)) { + /* don't leave an invalid focus_client */ + focus_client = NULL; + } + + /* if we're prompting to kill the client, close that */ + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + + client_list = g_list_remove(client_list, self); + stacking_remove(self); + window_remove(self->window); + + /* once the client is out of the list, update the struts to remove its + influence */ + if (STRUT_EXISTS(self->strut)) + screen_update_areas(); + + client_call_notifies(self, client_destroy_notifies); + + /* tell our parent(s) that we're gone */ + for (it = self->parents; it; it = g_slist_next(it)) + ((ObClient*)it->data)->transients = + g_slist_remove(((ObClient*)it->data)->transients,self); + + /* tell our transients that we're gone */ + for (it = self->transients; it; it = g_slist_next(it)) { + ((ObClient*)it->data)->parents = + g_slist_remove(((ObClient*)it->data)->parents, self); + /* we could be keeping our children in a higher layer */ + client_calc_layer(it->data); + } + + /* remove from its group */ + if (self->group) { + group_remove(self->group, self); + self->group = NULL; + } + + /* restore the window's original geometry so it is not lost */ + { + Rect a; + + a = self->area; + + if (self->fullscreen) + a = self->pre_fullscreen_area; + else if (self->max_horz || self->max_vert) { + if (self->max_horz) { + a.x = self->pre_max_area.x; + a.width = self->pre_max_area.width; + } + if (self->max_vert) { + a.y = self->pre_max_area.y; + a.height = self->pre_max_area.height; + } + } + + self->fullscreen = self->max_horz = self->max_vert = FALSE; + /* let it be moved and resized no matter what */ + self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE; + self->decorations = 0; /* unmanaged windows have no decor */ + + /* give the client its border back */ + XSetWindowBorderWidth(obt_display, self->window, self->border_width); + + client_move_resize(self, a.x, a.y, a.width, a.height); + } + + /* reparent the window out of the frame, and free the frame */ + frame_release_client(self->frame); + frame_free(self->frame); + self->frame = NULL; + + if (ob_state() != OB_STATE_EXITING) { + /* these values should not be persisted across a window + unmapping/mapping */ + OBT_PROP_ERASE(self->window, NET_WM_DESKTOP); + OBT_PROP_ERASE(self->window, NET_WM_STATE); + OBT_PROP_ERASE(self->window, WM_STATE); + } else { + /* if we're left in an unmapped state, the client wont be mapped. + this is bad, since we will no longer be managing the window on + restart */ + XMapWindow(obt_display, self->window); + } + + /* these should not be left on the window ever. other window managers + don't necessarily use them and it will mess them up (like compiz) */ + OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_NAME); + OBT_PROP_ERASE(self->window, NET_WM_VISIBLE_ICON_NAME); + + /* update the list hints */ + client_set_list(); + + ob_debug("Unmanaged window 0x%lx", self->window); + + /* free all data allocated in the client struct */ + RrImageUnref(self->icon_set); + g_slist_free(self->transients); + g_free(self->startup_id); + g_free(self->wm_command); + g_free(self->title); + g_free(self->icon_title); + g_free(self->original_title); + g_free(self->name); + g_free(self->class); + g_free(self->role); + g_free(self->client_machine); + g_free(self->sm_client_id); + g_slice_free(ObClient, self); +} + +void client_fake_unmanage(ObClient *self) +{ + /* this is all that got allocated to get the decorations */ + + frame_free(self->frame); + g_slice_free(ObClient, self); +} + +static gboolean client_can_steal_focus(ObClient *self, + gboolean allow_other_desktop, + gboolean request_from_user, + Time steal_time, + Time launch_time) +{ + gboolean steal; + gboolean relative_focused; + + steal = TRUE; + + relative_focused = (focus_client != NULL && + (client_search_focus_tree_full(self) != NULL || + client_search_focus_group_full(self) != NULL)); + + /* This is focus stealing prevention */ + ob_debug("Want to focus window 0x%x at time %u " + "launched at %u (last user interaction time %u) " + "request from %s, allow other desktop: %s, " + "desktop switch time %u", + self->window, steal_time, launch_time, + event_last_user_time, + (request_from_user ? "user" : "other"), + (allow_other_desktop ? "yes" : "no"), + screen_desktop_user_time); + + /* + if no launch time is provided for an application, make one up. + + if the window is related to other existing windows + and one of those windows was the last used + then we will give it a launch time equal to the last user time, + which will end up giving the window focus probably. + else + the window is related to other windows, but you are not working in + them? + seems suspicious, so we will give it a launch time of + NOW - STEAL_INTERVAL, + so it will be given focus only if we didn't use something else + during the steal interval. + else + the window is all on its own, so we can't judge it. give it a launch + time equal to the last user time, so it will probably take focus. + + this way running things from a terminal will give them focus, but popups + without a launch time shouldn't steal focus so easily. + */ + + if (!launch_time) { + if (client_has_relative(self)) { + if (event_last_user_time && client_search_focus_group_full(self)) { + /* our relative is focused */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - window in active " + "group", launch_time); + } + else if (!request_from_user) { + /* has relatives which are not being used. suspicious */ + launch_time = event_time() - OB_EVENT_USER_TIME_DELAY; + ob_debug("Unknown launch time, using %u - window in inactive " + "group", launch_time); + } + else { + /* has relatives which are not being used, but the user seems + to want to go there! */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - user request", + launch_time); + } + } + else { + /* the window is on its own, probably the user knows it is going + to appear */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - independent window", + launch_time); + } + } + + /* if it's on another desktop + and if allow_other_desktop is true, we generally let it steal focus. + but if it didn't come from the user, don't let it steal unless it was + launched before the user switched desktops. + focus, unless it was launched after we changed desktops and the request + came from the user + */ + if (!screen_compare_desktops(screen_desktop, self->desktop)) { + /* must be allowed */ + if (!allow_other_desktop) { + steal = FALSE; + ob_debug("Not focusing the window because its on another desktop"); + } + /* if we don't know when the desktop changed, but request is from an + application, don't let it change desktop on you */ + else if (!request_from_user) { + steal = FALSE; + ob_debug("Not focusing the window because non-user request"); + } + } + /* If something is focused... */ + else if (focus_client) { + /* If the user is working in another window right now, then don't + steal focus */ + if (!relative_focused && + event_last_user_time && + /* last user time must be strictly > launch_time to block focus */ + (event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time) && + event_time_after(event_last_user_time, + steal_time - OB_EVENT_USER_TIME_DELAY)) + { + steal = FALSE; + ob_debug("Not focusing the window because the user is " + "working in another window that is not its relative"); + } + /* Don't move focus if it's not going to go to this window + anyway */ + else if (client_focus_target(self) != self) { + steal = FALSE; + ob_debug("Not focusing the window because another window " + "would get the focus anyway"); + } + /* For requests that don't come from the user */ + else if (!request_from_user) { + /* If the new window is a transient (and its relatives aren't + focused) */ + if (client_has_parent(self) && !relative_focused) { + steal = FALSE; + ob_debug("Not focusing the window because it is a " + "transient, and its relatives aren't focused"); + } + /* Don't steal focus from globally active clients. + I stole this idea from KWin. It seems nice. + */ + else if (!(focus_client->can_focus || focus_client->focus_notify)) + { + steal = FALSE; + ob_debug("Not focusing the window because a globally " + "active client has focus"); + } + /* Don't move focus if the window is not visible on the current + desktop and none of its relatives are focused */ + else if (!allow_other_desktop && + !screen_compare_desktops(self->desktop, screen_desktop) && + !relative_focused) + { + steal = FALSE; + ob_debug("Not focusing the window because it is on " + "another desktop and no relatives are focused "); + } + } + } + + if (!steal) + ob_debug("Focus stealing prevention activated for %s at " + "time %u (last user interaction time %u)", + self->title, steal_time, event_last_user_time); + else + ob_debug("Allowing focus stealing for %s at time %u (last user " + "interaction time %u)", + self->title, steal_time, event_last_user_time); + return steal; +} + +/*! Returns a new structure containing the per-app settings for this client. + The returned structure needs to be freed with g_free. */ +static ObAppSettings *client_get_settings_state(ObClient *self) +{ + ObAppSettings *settings; + GSList *it; + + settings = config_create_app_settings(); + + for (it = config_per_app_settings; it; it = g_slist_next(it)) { + ObAppSettings *app = it->data; + gboolean match = TRUE; + + g_assert(app->name != NULL || app->class != NULL || + app->role != NULL || app->title != NULL || + (signed)app->type >= 0); + + if (app->name && + !g_pattern_match(app->name, strlen(self->name), self->name, NULL)) + match = FALSE; + else if (app->class && + !g_pattern_match(app->class, + strlen(self->class), self->class, NULL)) + match = FALSE; + else if (app->role && + !g_pattern_match(app->role, + strlen(self->role), self->role, NULL)) + match = FALSE; + else if (app->title && + !g_pattern_match(app->title, + strlen(self->title), self->title, NULL)) + match = FALSE; + else if ((signed)app->type >= 0 && app->type != self->type) { + match = FALSE; + } + + if (match) { + ob_debug("Window matching: %s", app->name); + + /* copy the settings to our struct, overriding the existing + settings if they are not defaults */ + config_app_settings_copy_non_defaults(app, settings); + } + } + + if (settings->shade != -1) + self->shaded = !!settings->shade; + if (settings->decor != -1) + self->undecorated = !settings->decor; + if (settings->iconic != -1) + self->iconic = !!settings->iconic; + if (settings->skip_pager != -1) + self->skip_pager = !!settings->skip_pager; + if (settings->skip_taskbar != -1) + self->skip_taskbar = !!settings->skip_taskbar; + + if (settings->max_vert != -1) + self->max_vert = !!settings->max_vert; + if (settings->max_horz != -1) + self->max_horz = !!settings->max_horz; + + if (settings->fullscreen != -1) + self->fullscreen = !!settings->fullscreen; + + if (settings->desktop) { + if (settings->desktop == DESKTOP_ALL) + self->desktop = settings->desktop; + else if (settings->desktop > 0 && + settings->desktop <= screen_num_desktops) + self->desktop = settings->desktop - 1; + } + + if (settings->layer == -1) { + self->below = TRUE; + self->above = FALSE; + } + else if (settings->layer == 0) { + self->below = FALSE; + self->above = FALSE; + } + else if (settings->layer == 1) { + self->below = FALSE; + self->above = TRUE; + } + return settings; +} + +static void client_restore_session_state(ObClient *self) +{ + GList *it; + + ob_debug_type(OB_DEBUG_SM, + "Restore session for client %s", self->title); + + if (!(it = session_state_find(self))) { + ob_debug_type(OB_DEBUG_SM, + "Session data not found for client %s", self->title); + return; + } + + self->session = it->data; + + ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s", + self->title); + + RECT_SET_POINT(self->area, self->session->x, self->session->y); + self->positioned = USPosition; + self->sized = USSize; + if (self->session->w > 0) + self->area.width = self->session->w; + if (self->session->h > 0) + self->area.height = self->session->h; + XResizeWindow(obt_display, self->window, + self->area.width, self->area.height); + + self->desktop = (self->session->desktop == DESKTOP_ALL ? + self->session->desktop : + MIN(screen_num_desktops - 1, self->session->desktop)); + OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop); + + self->shaded = self->session->shaded; + self->iconic = self->session->iconic; + self->skip_pager = self->session->skip_pager; + self->skip_taskbar = self->session->skip_taskbar; + self->fullscreen = self->session->fullscreen; + self->above = self->session->above; + self->below = self->session->below; + self->max_horz = self->session->max_horz; + self->max_vert = self->session->max_vert; + self->undecorated = self->session->undecorated; +} + +static gboolean client_restore_session_stacking(ObClient *self) +{ + GList *it, *mypos; + + if (!self->session) return FALSE; + + mypos = g_list_find(session_saved_state, self->session); + if (!mypos) return FALSE; + + /* start above me and look for the first client */ + for (it = g_list_previous(mypos); it; it = g_list_previous(it)) { + GList *cit; + + for (cit = client_list; cit; cit = g_list_next(cit)) { + ObClient *c = cit->data; + /* found a client that was in the session, so go below it */ + if (c->session == it->data) { + stacking_below(CLIENT_AS_WINDOW(self), + CLIENT_AS_WINDOW(cit->data)); + return TRUE; + } + } + } + return FALSE; +} + +void client_move_onscreen(ObClient *self, gboolean rude) +{ + gint x = self->area.x; + gint y = self->area.y; + if (client_find_onscreen(self, &x, &y, + self->area.width, + self->area.height, rude)) { + client_move(self, x, y); + } +} + +gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, + gboolean rude) +{ + gint ox = *x, oy = *y; + gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude; + gint fw, fh; + Rect desired; + guint i; + gboolean found_mon; + + RECT_SET(desired, *x, *y, w, h); + frame_rect_to_frame(self->frame, &desired); + + /* get where the frame would be */ + frame_client_gravity(self->frame, x, y); + + /* get the requested size of the window with decorations */ + fw = self->frame->size.left + w + self->frame->size.right; + fh = self->frame->size.top + h + self->frame->size.bottom; + + /* If rudeness wasn't requested, then still be rude in a given direction + if the client is not moving, only resizing in that direction */ + if (!rude) { + Point oldtl, oldtr, oldbl, oldbr; + Point newtl, newtr, newbl, newbr; + gboolean stationary_l, stationary_r, stationary_t, stationary_b; + + POINT_SET(oldtl, self->frame->area.x, self->frame->area.y); + POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1, + self->frame->area.y + self->frame->area.height - 1); + POINT_SET(oldtr, oldbr.x, oldtl.y); + POINT_SET(oldbl, oldtl.x, oldbr.y); + + POINT_SET(newtl, *x, *y); + POINT_SET(newbr, *x + fw - 1, *y + fh - 1); + POINT_SET(newtr, newbr.x, newtl.y); + POINT_SET(newbl, newtl.x, newbr.y); + + /* is it moving or just resizing from some corner? */ + stationary_l = oldtl.x == newtl.x; + stationary_r = oldtr.x == newtr.x; + stationary_t = oldtl.y == newtl.y; + stationary_b = oldbl.y == newbl.y; + + /* if left edge is growing and didnt move right edge */ + if (stationary_r && newtl.x < oldtl.x) + rudel = TRUE; + /* if right edge is growing and didnt move left edge */ + if (stationary_l && newtr.x > oldtr.x) + ruder = TRUE; + /* if top edge is growing and didnt move bottom edge */ + if (stationary_b && newtl.y < oldtl.y) + rudet = TRUE; + /* if bottom edge is growing and didnt move top edge */ + if (stationary_t && newbl.y > oldbl.y) + rudeb = TRUE; + } + + /* we iterate through every monitor that the window is at least partially + on, to make sure it is obeying the rules on them all + + if the window does not appear on any monitors, then use the first one + */ + found_mon = FALSE; + for (i = 0; i < screen_num_monitors; ++i) { + Rect *a; + + if (!screen_physical_area_monitor_contains(i, &desired)) { + if (i < screen_num_monitors - 1 || found_mon) + continue; + + /* the window is not inside any monitor! so just use the first + one */ + a = screen_area(self->desktop, 0, NULL); + } else { + found_mon = TRUE; + a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); + } + + /* This makes sure windows aren't entirely outside of the screen so you + can't see them at all. + It makes sure 10% of the window is on the screen at least. And don't + let it move itself off the top of the screen, which would hide the + titlebar on you. (The user can still do this if they want too, it's + only limiting the application. + */ + if (client_normal(self)) { + if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1) + *x = a->x + a->width - fw/10; + if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1) + *y = a->y + a->height - fh/10; + if (!self->strut.left && *x + fw*9/10 - 1 < a->x) + *x = a->x - fw*9/10; + if (!self->strut.top && *y + fh*9/10 - 1 < a->y) + *y = a->y - fh*9/10; + } + + /* This here doesn't let windows even a pixel outside the + struts/screen. When called from client_manage, programs placing + themselves are forced completely onscreen, while things like + xterm -geometry resolution-width/2 will work fine. Trying to + place it completely offscreen will be handled in the above code. + Sorry for this confused comment, i am tired. */ + if (rudel && !self->strut.left && *x < a->x) *x = a->x; + if (ruder && !self->strut.right && *x + fw > a->x + a->width) + *x = a->x + MAX(0, a->width - fw); + + if (rudet && !self->strut.top && *y < a->y) *y = a->y; + if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height) + *y = a->y + MAX(0, a->height - fh); + + g_slice_free(Rect, a); + } + + /* get where the client should be */ + frame_frame_gravity(self->frame, x, y); + + return ox != *x || oy != *y; +} + +static void client_get_all(ObClient *self, gboolean real) +{ + /* this is needed for the frame to set itself up */ + client_get_area(self); + + /* these things can change the decor and functions of the window */ + + client_get_mwm_hints(self); + /* this can change the mwmhints for special cases */ + client_get_type_and_transientness(self); + client_update_normal_hints(self); + + /* set up the maximum possible decor/functions */ + client_setup_default_decor_and_functions(self); + + client_get_state(self); + + /* get the session related properties, these can change decorations + from per-app settings */ + client_get_session_ids(self); + + /* now we got everything that can affect the decorations */ + if (!real) + return; + + /* get this early so we have it for debugging */ + client_update_title(self); + + /* save the values of the variables used for app rule matching */ + client_save_app_rule_values(self); + + client_update_protocols(self); + + client_update_wmhints(self); + /* this may have already been called from client_update_wmhints */ + if (!self->parents && !self->transient_for_group) + client_update_transient_for(self); + + client_get_startup_id(self); + client_get_desktop(self);/* uses transient data/group/startup id if a + desktop is not specified */ + client_get_shaped(self); + + { + /* a couple type-based defaults for new windows */ + + /* this makes sure that these windows appear on all desktops */ + if (self->type == OB_CLIENT_TYPE_DESKTOP) + self->desktop = DESKTOP_ALL; + } + +#ifdef SYNC + client_update_sync_request_counter(self); +#endif + + client_get_colormap(self); + client_update_strut(self); + client_update_icons(self); + client_update_icon_geometry(self); +} + +static void client_get_startup_id(ObClient *self) +{ + if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id))) + if (self->group) + OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID, + &self->startup_id); +} + +static void client_get_area(ObClient *self) +{ + XWindowAttributes wattrib; + Status ret; + + ret = XGetWindowAttributes(obt_display, self->window, &wattrib); + g_assert(ret != BadWindow); + + RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height); + POINT_SET(self->root_pos, wattrib.x, wattrib.y); + self->border_width = wattrib.border_width; + + ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y, + wattrib.width, wattrib.height, wattrib.border_width); +} + +static void client_get_desktop(ObClient *self) +{ + guint32 d = screen_num_desktops; /* an always-invalid value */ + + if (OBT_PROP_GET32(self->window, NET_WM_DESKTOP, CARDINAL, &d)) { + if (d >= screen_num_desktops && d != DESKTOP_ALL) + self->desktop = screen_num_desktops - 1; + else + self->desktop = d; + ob_debug("client requested desktop 0x%x", self->desktop); + } else { + GSList *it; + gboolean first = TRUE; + guint all = screen_num_desktops; /* not a valid value */ + + /* if they are all on one desktop, then open it on the + same desktop */ + for (it = self->parents; it; it = g_slist_next(it)) { + ObClient *c = it->data; + + if (c->desktop == DESKTOP_ALL) continue; + + if (first) { + all = c->desktop; + first = FALSE; + } + else if (all != c->desktop) + all = screen_num_desktops; /* make it invalid */ + } + if (all != screen_num_desktops) { + self->desktop = all; + + ob_debug("client desktop set from parents: 0x%x", + self->desktop); + } + /* try get from the startup-notification protocol */ + else if (sn_get_desktop(self->startup_id, &self->desktop)) { + if (self->desktop >= screen_num_desktops && + self->desktop != DESKTOP_ALL) + self->desktop = screen_num_desktops - 1; + ob_debug("client desktop set from startup-notification: 0x%x", + self->desktop); + } + /* defaults to the current desktop */ + else { + self->desktop = screen_desktop; + ob_debug("client desktop set to the current desktop: %d", + self->desktop); + } + } +} + +static void client_get_state(ObClient *self) +{ + guint32 *state; + guint num; + + if (OBT_PROP_GETA32(self->window, NET_WM_STATE, ATOM, &state, &num)) { + gulong i; + for (i = 0; i < num; ++i) { + if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) + self->modal = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) + self->shaded = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) + self->iconic = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) + self->skip_taskbar = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) + self->skip_pager = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) + self->fullscreen = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) + self->max_vert = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) + self->max_horz = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) + self->above = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) + self->below = TRUE; + else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)) + self->demands_attention = TRUE; + else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) + self->undecorated = TRUE; + } + + g_free(state); + } +} + +static void client_get_shaped(ObClient *self) +{ + self->shaped = FALSE; +#ifdef SHAPE + if (obt_display_extension_shape) { + gint foo; + guint ufoo; + gint s; + + XShapeSelectInput(obt_display, self->window, ShapeNotifyMask); + + XShapeQueryExtents(obt_display, self->window, &s, &foo, + &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, + &ufoo); + self->shaped = !!s; + } +#endif +} + +void client_update_transient_for(ObClient *self) +{ + Window t = None; + ObClient *target = NULL; + gboolean trangroup = FALSE; + + if (XGetTransientForHint(obt_display, self->window, &t)) { + if (t != self->window) { /* can't be transient to itself! */ + ObWindow *tw = window_find(t); + /* if this happens then we need to check for it */ + g_assert(tw != CLIENT_AS_WINDOW(self)); + if (tw && WINDOW_IS_CLIENT(tw)) { + /* watch out for windows with a parent that is something + different, like a dockapp for example */ + target = WINDOW_AS_CLIENT(tw); + } + } + + /* Setting the transient_for to Root is actually illegal, however + applications from time have done this to specify transient for + their group */ + if (!target && self->group && t == obt_root(ob_screen)) + trangroup = TRUE; + } else if (self->group && self->transient) + trangroup = TRUE; + + client_update_transient_tree(self, self->group, self->group, + self->transient_for_group, trangroup, + client_direct_parent(self), target); + self->transient_for_group = trangroup; + +} + +static void client_update_transient_tree(ObClient *self, + ObGroup *oldgroup, ObGroup *newgroup, + gboolean oldgtran, gboolean newgtran, + ObClient* oldparent, + ObClient *newparent) +{ + GSList *it, *next; + ObClient *c; + + g_assert(!oldgtran || oldgroup); + g_assert(!newgtran || newgroup); + g_assert((!oldgtran && !oldparent) || + (oldgtran && !oldparent) || + (!oldgtran && oldparent)); + g_assert((!newgtran && !newparent) || + (newgtran && !newparent) || + (!newgtran && newparent)); + + /* * * + Group transient windows are not allowed to have other group + transient windows as their children. + * * */ + + /* No change has occured */ + if (oldgroup == newgroup && + oldgtran == newgtran && + oldparent == newparent) return; + + /** Remove the client from the transient tree **/ + + for (it = self->transients; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->transients = g_slist_delete_link(self->transients, it); + c->parents = g_slist_remove(c->parents, self); + } + for (it = self->parents; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->parents = g_slist_delete_link(self->parents, it); + c->transients = g_slist_remove(c->transients, self); + } + + /** Re-add the client to the transient tree **/ + + /* If we're transient for a group then we need to add ourselves to all our + parents */ + if (newgtran) { + for (it = newgroup->members; it; it = g_slist_next(it)) { + c = it->data; + if (c != self && + !client_search_top_direct_parent(c)->transient_for_group && + client_normal(c)) + { + c->transients = g_slist_prepend(c->transients, self); + self->parents = g_slist_prepend(self->parents, c); + } + } + } + + /* If we are now transient for a single window we need to add ourselves to + its children + + WARNING: Cyclical transient-ness is possible if two windows are + transient for eachother. + */ + else if (newparent && + /* don't make ourself its child if it is already our child */ + !client_is_direct_child(self, newparent) && + client_normal(newparent)) + { + newparent->transients = g_slist_prepend(newparent->transients, self); + self->parents = g_slist_prepend(self->parents, newparent); + } + + /* Add any group transient windows to our children. But if we're transient + for the group, then other group transients are not our children. + + WARNING: Cyclical transient-ness is possible. For e.g. if: + A is transient for the group + B is transient for A + C is transient for B + A can't be transient for C or we have a cycle + */ + if (!newgtran && newgroup && + (!newparent || + !client_search_top_direct_parent(newparent)->transient_for_group) && + client_normal(self)) + { + for (it = newgroup->members; it; it = g_slist_next(it)) { + c = it->data; + if (c != self && c->transient_for_group && + /* Don't make it our child if it is already our parent */ + !client_is_direct_child(c, self)) + { + self->transients = g_slist_prepend(self->transients, c); + c->parents = g_slist_prepend(c->parents, self); + } + } + } + + /** If we change our group transient-ness, our children change their + effective group transient-ness, which affects how they relate to other + group windows **/ + + for (it = self->transients; it; it = g_slist_next(it)) { + c = it->data; + if (!c->transient_for_group) + client_update_transient_tree(c, c->group, c->group, + c->transient_for_group, + c->transient_for_group, + client_direct_parent(c), + client_direct_parent(c)); + } +} + +void client_get_mwm_hints(ObClient *self) +{ + guint num; + guint32 *hints; + + self->mwmhints.flags = 0; /* default to none */ + + if (OBT_PROP_GETA32(self->window, MOTIF_WM_HINTS, MOTIF_WM_HINTS, + &hints, &num)) { + if (num >= OB_MWM_ELEMENTS) { + self->mwmhints.flags = hints[0]; + self->mwmhints.functions = hints[1]; + self->mwmhints.decorations = hints[2]; + } + g_free(hints); + } +} + +void client_get_type_and_transientness(ObClient *self) +{ + guint num, i; + guint32 *val; + Window t; + + self->type = -1; + self->transient = FALSE; + + if (OBT_PROP_GETA32(self->window, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) { + /* use the first value that we know about in the array */ + for (i = 0; i < num; ++i) { + if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP)) + self->type = OB_CLIENT_TYPE_DESKTOP; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK)) + self->type = OB_CLIENT_TYPE_DOCK; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR)) + self->type = OB_CLIENT_TYPE_TOOLBAR; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU)) + self->type = OB_CLIENT_TYPE_MENU; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY)) + self->type = OB_CLIENT_TYPE_UTILITY; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH)) + self->type = OB_CLIENT_TYPE_SPLASH; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG)) + self->type = OB_CLIENT_TYPE_DIALOG; + else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL)) + self->type = OB_CLIENT_TYPE_NORMAL; + else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE)) + { + /* prevent this window from getting any decor or + functionality */ + self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS | + OB_MWM_FLAG_DECORATIONS); + self->mwmhints.decorations = 0; + self->mwmhints.functions = 0; + } + if (self->type != (ObClientType) -1) + break; /* grab the first legit type */ + } + g_free(val); + } + + if (XGetTransientForHint(obt_display, self->window, &t)) + self->transient = TRUE; + + if (self->type == (ObClientType) -1) { + /*the window type hint was not set, which means we either classify + ourself as a normal window or a dialog, depending on if we are a + transient. */ + if (self->transient) + self->type = OB_CLIENT_TYPE_DIALOG; + else + self->type = OB_CLIENT_TYPE_NORMAL; + } + + /* then, based on our type, we can update our transientness.. */ + if (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_UTILITY) + { + self->transient = TRUE; + } +} + +void client_update_protocols(ObClient *self) +{ + guint32 *proto; + guint num_ret, i; + + self->focus_notify = FALSE; + self->delete_window = FALSE; + + if (OBT_PROP_GETA32(self->window, WM_PROTOCOLS, ATOM, &proto, &num_ret)) { + for (i = 0; i < num_ret; ++i) { + if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW)) + /* this means we can request the window to close */ + self->delete_window = TRUE; + else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS)) + /* if this protocol is requested, then the window will be + notified whenever we want it to receive focus */ + self->focus_notify = TRUE; + else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING)) + /* if this protocol is requested, then the window will allow + pings to determine if it is still alive */ + self->ping = TRUE; +#ifdef SYNC + else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST)) + /* if this protocol is requested, then resizing the + window will be synchronized between the frame and the + client */ + self->sync_request = TRUE; +#endif + } + g_free(proto); + } +} + +#ifdef SYNC +void client_update_sync_request_counter(ObClient *self) +{ + guint32 i; + + if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i)) + { + XSyncValue val; + + self->sync_counter = i; + + /* this must be set when managing a new window according to EWMH */ + XSyncIntToValue(&val, 0); + XSyncSetCounter(obt_display, self->sync_counter, val); + } else + self->sync_counter = None; +} +#endif + +static void client_get_colormap(ObClient *self) +{ + XWindowAttributes wa; + + if (XGetWindowAttributes(obt_display, self->window, &wa)) + client_update_colormap(self, wa.colormap); +} + +void client_update_colormap(ObClient *self, Colormap colormap) +{ + if (colormap == self->colormap) return; + + ob_debug("Setting client %s colormap: 0x%x", self->title, colormap); + + if (client_focused(self)) { + screen_install_colormap(self, FALSE); /* uninstall old one */ + self->colormap = colormap; + screen_install_colormap(self, TRUE); /* install new one */ + } else + self->colormap = colormap; +} + +void client_update_opacity(ObClient *self) +{ + guint32 o; + + if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o)) + OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o); + else + OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY); +} + +void client_update_normal_hints(ObClient *self) +{ + XSizeHints size; + glong ret; + + /* defaults */ + self->min_ratio = 0.0f; + self->max_ratio = 0.0f; + SIZE_SET(self->size_inc, 1, 1); + SIZE_SET(self->base_size, -1, -1); + SIZE_SET(self->min_size, 0, 0); + SIZE_SET(self->max_size, G_MAXINT, G_MAXINT); + + /* get the hints from the window */ + if (XGetWMNormalHints(obt_display, self->window, &size, &ret)) { + /* normal windows can't request placement! har har + if (!client_normal(self)) + */ + self->positioned = (size.flags & (PPosition|USPosition)); + self->sized = (size.flags & (PSize|USSize)); + + if (size.flags & PWinGravity) + self->gravity = size.win_gravity; + + if (size.flags & PAspect) { + if (size.min_aspect.y) + self->min_ratio = + (gfloat) size.min_aspect.x / size.min_aspect.y; + if (size.max_aspect.y) + self->max_ratio = + (gfloat) size.max_aspect.x / size.max_aspect.y; + } + + if (size.flags & PMinSize) + SIZE_SET(self->min_size, size.min_width, size.min_height); + + if (size.flags & PMaxSize) + SIZE_SET(self->max_size, size.max_width, size.max_height); + + if (size.flags & PBaseSize) + SIZE_SET(self->base_size, size.base_width, size.base_height); + + if (size.flags & PResizeInc && size.width_inc && size.height_inc) + SIZE_SET(self->size_inc, size.width_inc, size.height_inc); + + ob_debug("Normal hints: min size (%d %d) max size (%d %d)", + self->min_size.width, self->min_size.height, + self->max_size.width, self->max_size.height); + ob_debug("size inc (%d %d) base size (%d %d)", + self->size_inc.width, self->size_inc.height, + self->base_size.width, self->base_size.height); + } + else + ob_debug("Normal hints: not set"); +} + +static void client_setup_default_decor_and_functions(ObClient *self) +{ + /* start with everything (cept fullscreen) */ + self->decorations = + (OB_FRAME_DECOR_TITLEBAR | + OB_FRAME_DECOR_HANDLE | + OB_FRAME_DECOR_GRIPS | + OB_FRAME_DECOR_BORDER | + OB_FRAME_DECOR_ICON | + OB_FRAME_DECOR_ALLDESKTOPS | + OB_FRAME_DECOR_ICONIFY | + OB_FRAME_DECOR_MAXIMIZE | + OB_FRAME_DECOR_SHADE | + OB_FRAME_DECOR_CLOSE); + self->functions = + (OB_CLIENT_FUNC_RESIZE | + OB_CLIENT_FUNC_MOVE | + OB_CLIENT_FUNC_ICONIFY | + OB_CLIENT_FUNC_MAXIMIZE | + OB_CLIENT_FUNC_SHADE | + OB_CLIENT_FUNC_CLOSE | + OB_CLIENT_FUNC_BELOW | + OB_CLIENT_FUNC_ABOVE | + OB_CLIENT_FUNC_UNDECORATE); + + if (!(self->min_size.width < self->max_size.width || + self->min_size.height < self->max_size.height)) + self->functions &= ~OB_CLIENT_FUNC_RESIZE; + + switch (self->type) { + case OB_CLIENT_TYPE_NORMAL: + /* normal windows retain all of the possible decorations and + functionality, and can be fullscreen */ + self->functions |= OB_CLIENT_FUNC_FULLSCREEN; + break; + + case OB_CLIENT_TYPE_DIALOG: + /* sometimes apps make dialog windows fullscreen for some reason (for + e.g. kpdf does this..) */ + self->functions |= OB_CLIENT_FUNC_FULLSCREEN; + break; + + case OB_CLIENT_TYPE_UTILITY: + /* these windows don't have anything added or removed by default */ + break; + + case OB_CLIENT_TYPE_MENU: + case OB_CLIENT_TYPE_TOOLBAR: + /* these windows can't iconify or maximize */ + self->decorations &= ~(OB_FRAME_DECOR_ICONIFY | + OB_FRAME_DECOR_MAXIMIZE); + self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | + OB_CLIENT_FUNC_MAXIMIZE); + break; + + case OB_CLIENT_TYPE_SPLASH: + /* these don't get get any decorations, and the only thing you can + do with them is move them */ + self->decorations = 0; + self->functions = OB_CLIENT_FUNC_MOVE; + break; + + case OB_CLIENT_TYPE_DESKTOP: + /* these windows are not manipulated by the window manager */ + self->decorations = 0; + self->functions = 0; + break; + + case OB_CLIENT_TYPE_DOCK: + /* these windows are not manipulated by the window manager, but they + can set below layer which has a special meaning */ + self->decorations = 0; + self->functions = OB_CLIENT_FUNC_BELOW; + break; + } + + /* If the client has no decor from its type (which never changes) then + don't allow the user to "undecorate" the window. Otherwise, allow them + to, even if there are motif hints removing the decor, because those + may change these days (e.g. chromium) */ + if (self->decorations == 0) + self->functions &= ~OB_CLIENT_FUNC_UNDECORATE; + + /* Mwm Hints are applied subtractively to what has already been chosen for + decor and functionality */ + if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) { + if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) { + if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) || + (self->mwmhints.decorations & OB_MWM_DECOR_TITLE))) + { + /* if the mwm hints request no handle or title, then all + decorations are disabled, but keep the border if that's + specified */ + if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER) + self->decorations = OB_FRAME_DECOR_BORDER; + else + self->decorations = 0; + } + } + } + + if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) { + if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) { + if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE)) + self->functions &= ~OB_CLIENT_FUNC_RESIZE; + if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE)) + self->functions &= ~OB_CLIENT_FUNC_MOVE; + /* dont let mwm hints kill any buttons + if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY)) + self->functions &= ~OB_CLIENT_FUNC_ICONIFY; + if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE)) + self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE; + */ + /* dont let mwm hints kill the close button + if (! (self->mwmhints.functions & MwmFunc_Close)) + self->functions &= ~OB_CLIENT_FUNC_CLOSE; */ + } + } + + if (!(self->functions & OB_CLIENT_FUNC_SHADE)) + self->decorations &= ~OB_FRAME_DECOR_SHADE; + if (!(self->functions & OB_CLIENT_FUNC_ICONIFY)) + self->decorations &= ~OB_FRAME_DECOR_ICONIFY; + if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) + self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE); + + /* can't maximize without moving/resizing */ + if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) && + (self->functions & OB_CLIENT_FUNC_MOVE) && + (self->functions & OB_CLIENT_FUNC_RESIZE))) { + self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE; + self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE; + } +} + +/*! Set up decor for a client based on its undecorated state. */ +static void client_setup_decor_undecorated(ObClient *self) +{ + /* If the user requested no decorations, then remove all the decorations, + except the border. But don't add a border if there wasn't one. */ + if (self->undecorated) + self->decorations &= (config_theme_keepborder ? + OB_FRAME_DECOR_BORDER : 0); +} + +void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) +{ + client_setup_default_decor_and_functions(self); + + client_setup_decor_undecorated(self); + + if (self->max_horz && self->max_vert) { + /* once upon a time you couldn't resize maximized windows, that is not + the case any more though ! + + but do kill the handle on fully maxed windows */ + self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); + } + + /* if we don't have a titlebar, then we cannot shade! */ + if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR)) + self->functions &= ~OB_CLIENT_FUNC_SHADE; + + /* now we need to check against rules for the client's current state */ + if (self->fullscreen) { + self->functions &= (OB_CLIENT_FUNC_CLOSE | + OB_CLIENT_FUNC_FULLSCREEN | + OB_CLIENT_FUNC_ICONIFY); + self->decorations = 0; + } + + client_change_allowed_actions(self); + + if (reconfig) + /* reconfigure to make sure decorations are updated */ + client_reconfigure(self, FALSE); +} + +static void client_change_allowed_actions(ObClient *self) +{ + gulong actions[12]; + gint num = 0; + + /* desktop windows are kept on all desktops */ + if (self->type != OB_CLIENT_TYPE_DESKTOP) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP); + + if (self->functions & OB_CLIENT_FUNC_SHADE) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE); + if (self->functions & OB_CLIENT_FUNC_CLOSE) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE); + if (self->functions & OB_CLIENT_FUNC_MOVE) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE); + if (self->functions & OB_CLIENT_FUNC_ICONIFY) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE); + if (self->functions & OB_CLIENT_FUNC_RESIZE) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE); + if (self->functions & OB_CLIENT_FUNC_FULLSCREEN) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN); + if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) { + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ); + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT); + } + if (self->functions & OB_CLIENT_FUNC_ABOVE) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE); + if (self->functions & OB_CLIENT_FUNC_BELOW) + actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW); + if (self->functions & OB_CLIENT_FUNC_UNDECORATE) + actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE); + + OBT_PROP_SETA32(self->window, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num); + + /* make sure the window isn't breaking any rules now + + don't check ICONIFY here. just cuz a window can't iconify doesnt mean + it can't be iconified with its parent + */ + + if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) { + if (self->frame) client_shade(self, FALSE); + else self->shaded = FALSE; + } + if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) { + if (self->frame) client_fullscreen(self, FALSE); + else self->fullscreen = FALSE; + } + if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz || + self->max_vert)) { + if (self->frame) client_maximize(self, FALSE, 0); + else self->max_vert = self->max_horz = FALSE; + } +} + +void client_update_wmhints(ObClient *self) +{ + XWMHints *hints; + + /* assume a window takes input if it doesn't specify */ + self->can_focus = TRUE; + + if ((hints = XGetWMHints(obt_display, self->window)) != NULL) { + gboolean ur; + + if (hints->flags & InputHint) + self->can_focus = hints->input; + + /* only do this when first managing the window *AND* when we aren't + starting up! */ + if (ob_state() != OB_STATE_STARTING && self->frame == NULL) + if (hints->flags & StateHint) + self->iconic = hints->initial_state == IconicState; + + ur = self->urgent; + self->urgent = (hints->flags & XUrgencyHint); + if (self->urgent && !ur) + client_hilite(self, TRUE); + else if (!self->urgent && ur && self->demands_attention) + client_hilite(self, FALSE); + + if (!(hints->flags & WindowGroupHint)) + hints->window_group = None; + + /* did the group state change? */ + if (hints->window_group != + (self->group ? self->group->leader : None)) + { + ObGroup *oldgroup = self->group; + + /* remove from the old group if there was one */ + if (self->group) { + group_remove(self->group, self); + self->group = NULL; + } + + /* add ourself to the group if we have one */ + if (hints->window_group != None) { + self->group = group_add(hints->window_group, self); + } + + /* Put ourselves into the new group's transient tree, and remove + ourselves from the old group's */ + client_update_transient_tree(self, oldgroup, self->group, + self->transient_for_group, + self->transient_for_group, + client_direct_parent(self), + client_direct_parent(self)); + + /* Lastly, being in a group, or not, can change if the window is + transient for anything. + + The logic for this is: + self->transient = TRUE always if the window wants to be + transient for something, even if transient_for was NULL because + it wasn't in a group before. + + If parents was NULL and oldgroup was NULL we can assume + that when we add the new group, it will become transient for + something. + + If transient_for_group is TRUE, then it must have already + had a group. If it is getting a new group, the above call to + client_update_transient_tree has already taken care of + everything ! If it is losing all group status then it will + no longer be transient for anything and that needs to be + updated. + */ + if (self->transient && + ((self->parents == NULL && oldgroup == NULL) || + (self->transient_for_group && !self->group))) + client_update_transient_for(self); + } + + /* the WM_HINTS can contain an icon */ + if (hints->flags & IconPixmapHint) + client_update_icons(self); + + XFree(hints); + } + + focus_cycle_addremove(self, TRUE); +} + +void client_update_title(ObClient *self) +{ + gchar *data = NULL; + gchar *visible = NULL; + + g_free(self->title); + g_free(self->original_title); + + /* try netwm */ + if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) { + /* try old x stuff */ + if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) { + if (self->transient) { + /* + GNOME alert windows are not given titles: + http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html + */ + data = g_strdup(""); + } else + data = g_strdup(_("Unnamed Window")); + } + } + self->original_title = g_strdup(data); + + if (self->client_machine) { + visible = g_strdup_printf("%s (%s)", data, self->client_machine); + g_free(data); + } else + visible = data; + + if (self->not_responding) { + data = visible; + if (self->kill_level > 0) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + + OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible); + self->title = visible; + + if (self->frame) + frame_adjust_title(self->frame); + + /* update the icon title */ + data = NULL; + g_free(self->icon_title); + + /* try netwm */ + if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data)) + /* try old x stuff */ + if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data)) + data = g_strdup(self->title); + + if (self->client_machine) { + visible = g_strdup_printf("%s (%s)", data, self->client_machine); + g_free(data); + } else + visible = data; + + if (self->not_responding) { + data = visible; + if (self->kill_level > 0) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + + OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible); + self->icon_title = visible; +} + +void client_update_strut(ObClient *self) +{ + guint num; + guint32 *data; + gboolean got = FALSE; + StrutPartial strut; + + if (OBT_PROP_GETA32(self->window, NET_WM_STRUT_PARTIAL, CARDINAL, + &data, &num)) + { + if (num == 12) { + got = TRUE; + STRUT_PARTIAL_SET(strut, + data[0], data[2], data[1], data[3], + data[4], data[5], data[8], data[9], + data[6], data[7], data[10], data[11]); + } + g_free(data); + } + + if (!got && + OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) { + if (num == 4) { + const Rect *a; + + got = TRUE; + + /* use the screen's width/height */ + a = screen_physical_area_all_monitors(); + + STRUT_PARTIAL_SET(strut, + data[0], data[2], data[1], data[3], + a->y, a->y + a->height - 1, + a->x, a->x + a->width - 1, + a->y, a->y + a->height - 1, + a->x, a->x + a->width - 1); + } + g_free(data); + } + + if (!got) + STRUT_PARTIAL_SET(strut, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + + if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) { + self->strut = strut; + + /* updating here is pointless while we're being mapped cuz we're not in + the client list yet */ + if (self->frame) + screen_update_areas(); + } +} + +void client_update_icons(ObClient *self) +{ + guint num; + guint32 *data; + guint w, h, i, j; + RrImage *img; + + img = NULL; + + /* grab the server, because we might be setting the window's icon and + we don't want them to set it in between and we overwrite their own + icon */ + grab_server(TRUE); + + if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) { + /* figure out how many valid icons are in here */ + i = 0; + while (i + 2 < num) { /* +2 is to make sure there is a w and h */ + w = data[i++]; + h = data[i++]; + /* watch for the data being too small for the specified size, + or for zero sized icons. */ + if (i + w*h > num || w == 0 || h == 0) { + i += w*h; + continue; + } + + /* convert it to the right bit order for ObRender */ + for (j = 0; j < w*h; ++j) + data[i+j] = + (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) + + (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) + + (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) + + (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset); + + /* add it to the image cache as an original */ + if (!img) + img = RrImageNewFromData(ob_rr_icons, &data[i], w, h); + else + RrImageAddFromData(img, &data[i], w, h); + + i += w*h; + } + + g_free(data); + } + + /* if we didn't find an image from the NET_WM_ICON stuff, then try the + legacy X hints */ + if (!img) { + XWMHints *hints; + + if ((hints = XGetWMHints(obt_display, self->window))) { + if (hints->flags & IconPixmapHint) { + gboolean xicon; + obt_display_ignore_errors(TRUE); + xicon = RrPixmapToRGBA(ob_rr_inst, + hints->icon_pixmap, + (hints->flags & IconMaskHint ? + hints->icon_mask : None), + (gint*)&w, (gint*)&h, &data); + obt_display_ignore_errors(FALSE); + + if (xicon) { + if (w > 0 && h > 0) { + if (!img) + img = RrImageNewFromData(ob_rr_icons, data, w, h); + else + RrImageAddFromData(img, data, w, h); + } + + g_free(data); + } + } + XFree(hints); + } + } + + /* set the client's icons to be whatever we found */ + RrImageUnref(self->icon_set); + self->icon_set = img; + + /* if the client has no icon at all, then we set a default icon onto it. + but, if it has parents, then one of them will have an icon already + */ + if (!self->icon_set && !self->parents) { + RrPixel32 *icon = ob_rr_theme->def_win_icon; + gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */ + + w = ob_rr_theme->def_win_icon_w; + h = ob_rr_theme->def_win_icon_h; + ldata = g_new(gulong, w*h+2); + ldata[0] = w; + ldata[1] = h; + for (i = 0; i < w*h; ++i) + ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) + + (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) + + (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) + + (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0); + OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, ldata, w*h+2); + g_free(ldata); + } else if (self->frame) + /* don't draw the icon empty if we're just setting one now anyways, + we'll get the property change any second */ + frame_adjust_icon(self->frame); + + grab_server(FALSE); +} + +void client_update_icon_geometry(ObClient *self) +{ + guint num; + guint32 *data; + + RECT_SET(self->icon_geometry, 0, 0, 0, 0); + + if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL, + &data, &num)) + { + if (num == 4) + /* don't let them set it with an area < 0 */ + RECT_SET(self->icon_geometry, data[0], data[1], + MAX(data[2],0), MAX(data[3],0)); + g_free(data); + } +} + +static void client_get_session_ids(ObClient *self) +{ + guint32 leader; + gboolean got; + gchar *s; + gchar **ss; + + if (!OBT_PROP_GET32(self->window, WM_CLIENT_LEADER, WINDOW, &leader)) + leader = None; + + /* get the SM_CLIENT_ID */ + if (leader && leader != self->window) + OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id); + else + OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id); + + /* get the WM_CLASS (name and class). make them "" if they are not + provided */ + got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss); + + if (got) { + if (ss[0]) { + self->name = g_strdup(ss[0]); + if (ss[1]) + self->class = g_strdup(ss[1]); + } + g_strfreev(ss); + } + + if (self->name == NULL) self->name = g_strdup(""); + if (self->class == NULL) self->class = g_strdup(""); + + /* get the WM_WINDOW_ROLE. make it "" if it is not provided */ + got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s); + + if (got) + self->role = s; + else + self->role = g_strdup(""); + + /* get the WM_COMMAND */ + got = FALSE; + + if (leader) + got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss); + if (!got) + got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss); + + if (got) { + /* merge/mash them all together */ + gchar *merge = NULL; + gint i; + + for (i = 0; ss[i]; ++i) { + gchar *tmp = merge; + if (merge) + merge = g_strconcat(merge, ss[i], NULL); + else + merge = g_strconcat(ss[i], NULL); + g_free(tmp); + } + g_strfreev(ss); + + self->wm_command = merge; + } + + /* get the WM_CLIENT_MACHINE */ + got = FALSE; + if (leader) + got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s); + if (!got) + got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s); + + if (got) { + gchar localhost[128]; + guint32 pid; + + gethostname(localhost, 127); + localhost[127] = '\0'; + if (strcmp(localhost, s) != 0) + self->client_machine = s; + else + g_free(s); + + /* see if it has the PID set too (the PID requires that the + WM_CLIENT_MACHINE be set) */ + if (OBT_PROP_GET32(self->window, NET_WM_PID, CARDINAL, &pid)) + self->pid = pid; + } +} + +/*! Save the properties used for app matching rules, as seen by Openbox when + the window mapped, so that users can still access them later if the app + changes them */ +static void client_save_app_rule_values(ObClient *self) +{ + const gchar *type; + + OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role); + OBT_PROP_SETS(self->window, OB_APP_NAME, self->name); + OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class); + OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title); + + switch (self->type) { + case OB_CLIENT_TYPE_NORMAL: + type = "normal"; break; + case OB_CLIENT_TYPE_DIALOG: + type = "dialog"; break; + case OB_CLIENT_TYPE_UTILITY: + type = "utility"; break; + case OB_CLIENT_TYPE_MENU: + type = "menu"; break; + case OB_CLIENT_TYPE_TOOLBAR: + type = "toolbar"; break; + case OB_CLIENT_TYPE_SPLASH: + type = "splash"; break; + case OB_CLIENT_TYPE_DESKTOP: + type = "desktop"; break; + case OB_CLIENT_TYPE_DOCK: + type = "dock"; break; + } + OBT_PROP_SETS(self->window, OB_APP_TYPE, type); +} + +static void client_change_wm_state(ObClient *self) +{ + gulong state[2]; + glong old; + + old = self->wmstate; + + if (self->shaded || self->iconic || + (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop)) + { + self->wmstate = IconicState; + } else + self->wmstate = NormalState; + + if (old != self->wmstate) { + OBT_PROP_MSG(ob_screen, self->window, KDE_WM_CHANGE_STATE, + self->wmstate, 1, 0, 0, 0); + + state[0] = self->wmstate; + state[1] = None; + OBT_PROP_SETA32(self->window, WM_STATE, WM_STATE, state, 2); + } +} + +static void client_change_state(ObClient *self) +{ + gulong netstate[12]; + guint num; + + num = 0; + if (self->modal) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL); + if (self->shaded) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED); + if (self->iconic) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN); + if (self->skip_taskbar) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR); + if (self->skip_pager) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER); + if (self->fullscreen) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN); + if (self->max_vert) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT); + if (self->max_horz) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ); + if (self->above) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE); + if (self->below) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW); + if (self->demands_attention) + netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION); + if (self->undecorated) + netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED); + OBT_PROP_SETA32(self->window, NET_WM_STATE, ATOM, netstate, num); + + if (self->frame) + frame_adjust_state(self->frame); +} + +ObClient *client_search_focus_tree(ObClient *self) +{ + GSList *it; + ObClient *ret; + + for (it = self->transients; it; it = g_slist_next(it)) { + if (client_focused(it->data)) return it->data; + if ((ret = client_search_focus_tree(it->data))) return ret; + } + return NULL; +} + +ObClient *client_search_focus_tree_full(ObClient *self) +{ + if (self->parents) { + GSList *it; + + for (it = self->parents; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if ((c = client_search_focus_tree_full(c))) return c; + } + + return NULL; + } + else { + /* this function checks the whole tree, the client_search_focus_tree + does not, so we need to check this window */ + if (client_focused(self)) + return self; + return client_search_focus_tree(self); + } +} + +ObClient *client_search_focus_group_full(ObClient *self) +{ + GSList *it; + + if (self->group) { + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + + if (client_focused(c)) return c; + if ((c = client_search_focus_tree(it->data))) return c; + } + } else + if (client_focused(self)) return self; + return NULL; +} + +gboolean client_has_parent(ObClient *self) +{ + return self->parents != NULL; +} + +gboolean client_has_children(ObClient *self) +{ + return self->transients != NULL; +} + +gboolean client_is_oldfullscreen(const ObClient *self, + const Rect *area) +{ + const Rect *monitor, *allmonitors; + + /* No decorations and fills the monitor = oldskool fullscreen. + But not for maximized windows. + */ + + if (self->decorations || self->max_horz || self->max_vert) return FALSE; + + monitor = screen_physical_area_monitor(screen_find_monitor(area)); + allmonitors = screen_physical_area_all_monitors(); + + return (RECT_EQUAL(*area, *monitor) || + RECT_EQUAL(*area, *allmonitors)); +} + +static ObStackingLayer calc_layer(ObClient *self) +{ + ObStackingLayer l; + + if (self->type == OB_CLIENT_TYPE_DESKTOP) + l = OB_STACKING_LAYER_DESKTOP; + else if (self->type == OB_CLIENT_TYPE_DOCK) { + if (self->below) l = OB_STACKING_LAYER_NORMAL; + else l = OB_STACKING_LAYER_ABOVE; + } + else if ((self->fullscreen || + client_is_oldfullscreen(self, &self->area)) && + /* you are fullscreen while you or your children are focused.. */ + (client_focused(self) || client_search_focus_tree(self) || + /* you can be fullscreen if you're on another desktop */ + (self->desktop != screen_desktop && + self->desktop != DESKTOP_ALL) || + /* and you can also be fullscreen if the focused client is on + another monitor, or nothing else is focused */ + (!focus_client || + client_monitor(focus_client) != client_monitor(self)))) + l = OB_STACKING_LAYER_FULLSCREEN; + else if (self->above) l = OB_STACKING_LAYER_ABOVE; + else if (self->below) l = OB_STACKING_LAYER_BELOW; + else l = OB_STACKING_LAYER_NORMAL; + + return l; +} + +static void client_calc_layer_recursive(ObClient *self, ObClient *orig, + ObStackingLayer min) +{ + ObStackingLayer old, own; + GSList *it; + + old = self->layer; + own = calc_layer(self); + self->layer = MAX(own, min); + + if (self->layer != old) { + stacking_remove(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + } + + /* we've been restacked */ + self->visited = TRUE; + + for (it = self->transients; it; it = g_slist_next(it)) + client_calc_layer_recursive(it->data, orig, + self->layer); +} + +static void client_calc_layer_internal(ObClient *self) +{ + GSList *sit; + + /* transients take on the layer of their parents */ + sit = client_search_all_top_parents(self); + + for (; sit; sit = g_slist_next(sit)) + client_calc_layer_recursive(sit->data, self, 0); +} + +void client_calc_layer(ObClient *self) +{ + GList *it; + + /* skip over stuff above fullscreen layer */ + for (it = stacking_list; it; it = g_list_next(it)) + if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break; + + /* find the windows in the fullscreen layer, and mark them not-visited */ + for (; it; it = g_list_next(it)) { + if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break; + else if (WINDOW_IS_CLIENT(it->data)) + WINDOW_AS_CLIENT(it->data)->visited = FALSE; + } + + client_calc_layer_internal(self); + + /* skip over stuff above fullscreen layer */ + for (it = stacking_list; it; it = g_list_next(it)) + if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break; + + /* now recalc any windows in the fullscreen layer which have not + had their layer recalced already */ + for (; it; it = g_list_next(it)) { + if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break; + else if (WINDOW_IS_CLIENT(it->data) && + !WINDOW_AS_CLIENT(it->data)->visited) + client_calc_layer_internal(it->data); + } +} + +gboolean client_should_show(ObClient *self) +{ + if (self->iconic) + return FALSE; + if (client_normal(self) && screen_showing_desktop) + return FALSE; + if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) + return TRUE; + + return FALSE; +} + +gboolean client_show(ObClient *self) +{ + gboolean show = FALSE; + + if (client_should_show(self)) { + /* replay pending pointer event before showing the window, in case it + should be going to something under the window */ + mouse_replay_pointer(); + + frame_show(self->frame); + show = TRUE; + + /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, + it needs to be in IconicState. This includes when it is on another + desktop! + */ + client_change_wm_state(self); + } + return show; +} + +gboolean client_hide(ObClient *self) +{ + gboolean hide = FALSE; + + if (!client_should_show(self)) { + /* We don't need to ignore enter events here. + The window can hide/iconify in 3 different ways: + 1 - through an x message. in this case we ignore all enter events + caused by responding to the x message (unless underMouse) + 2 - by a keyboard action. in this case we ignore all enter events + caused by the action + 3 - by a mouse action. in this case they are doing stuff with the + mouse and focus _should_ move. + + Also in action_end, we simulate an enter event that can't be ignored + so trying to ignore them is futile in case 3 anyways + */ + + /* replay pending pointer event before hiding the window, in case it + should be going to the window */ + mouse_replay_pointer(); + + frame_hide(self->frame); + hide = TRUE; + + /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, + it needs to be in IconicState. This includes when it is on another + desktop! + */ + client_change_wm_state(self); + } + return hide; +} + +void client_showhide(ObClient *self) +{ + if (!client_show(self)) + client_hide(self); +} + +gboolean client_normal(ObClient *self) { + return ! (self->type == OB_CLIENT_TYPE_DESKTOP || + self->type == OB_CLIENT_TYPE_DOCK || + self->type == OB_CLIENT_TYPE_SPLASH); +} + +gboolean client_helper(ObClient *self) +{ + return (self->type == OB_CLIENT_TYPE_UTILITY || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_TOOLBAR); +} + +gboolean client_mouse_focusable(ObClient *self) +{ + return !(self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_SPLASH || + self->type == OB_CLIENT_TYPE_DOCK); +} + +gboolean client_enter_focusable(ObClient *self) +{ + /* you can focus desktops but it shouldn't on enter */ + return (client_mouse_focusable(self) && + self->type != OB_CLIENT_TYPE_DESKTOP); +} + +static void client_apply_startup_state(ObClient *self, + gint x, gint y, gint w, gint h) +{ + /* save the states that we are going to apply */ + gboolean iconic = self->iconic; + gboolean fullscreen = self->fullscreen; + gboolean undecorated = self->undecorated; + gboolean shaded = self->shaded; + gboolean demands_attention = self->demands_attention; + gboolean max_horz = self->max_horz; + gboolean max_vert = self->max_vert; + Rect oldarea; + gint l; + + /* turn them all off in the client, so they won't affect the window + being placed */ + self->iconic = self->fullscreen = self->undecorated = self->shaded = + self->demands_attention = self->max_horz = self->max_vert = FALSE; + + /* move the client to its placed position, or it it's already there, + generate a ConfigureNotify telling the client where it is. + + do this after adjusting the frame. otherwise it gets all weird and + clients don't work right + + do this before applying the states so they have the correct + pre-max/pre-fullscreen values + */ + client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE); + ob_debug("placed window 0x%x at %d, %d with size %d x %d", + self->window, x, y, w, h); + /* save the area, and make it where it should be for the premax stuff */ + oldarea = self->area; + RECT_SET(self->area, x, y, w, h); + + /* apply the states. these are in a carefully crafted order.. */ + + if (iconic) + client_iconify(self, TRUE, FALSE, TRUE); + if (undecorated) + client_set_undecorated(self, TRUE); + if (shaded) + client_shade(self, TRUE); + if (demands_attention) + client_hilite(self, TRUE); + + if (max_vert && max_horz) + client_maximize(self, TRUE, 0); + else if (max_vert) + client_maximize(self, TRUE, 2); + else if (max_horz) + client_maximize(self, TRUE, 1); + + /* fullscreen removes the ability to apply other states */ + if (fullscreen) + client_fullscreen(self, TRUE); + + /* make sure client_setup_decor_and_functions() is called at least once */ + client_setup_decor_and_functions(self, FALSE); + + /* if the window hasn't been configured yet, then do so now, in fact the + x,y,w,h may _not_ be the same as the area rect, which can end up + meaning that the client isn't properly moved/resized by the fullscreen + function + pho can cause this because it maps at size of the screen but not 0,0 + so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't). + then fullscreen'ing makes it go to 0,0 which it thinks it already is at + cuz thats where the pre-fullscreen will be. however the actual area is + not, so this needs to be called even if we have fullscreened/maxed + */ + self->area = oldarea; + client_configure(self, x, y, w, h, FALSE, TRUE, FALSE); + + /* nothing to do for the other states: + skip_taskbar + skip_pager + modal + above + below + */ +} + +void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww) +{ + /* these should be the current values. this is for when you're not moving, + just resizing */ + g_assert(*x == self->area.x); + g_assert(oldw == self->area.width); + + /* horizontal */ + switch (self->gravity) { + default: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + case StaticGravity: + case ForgetGravity: + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + *x -= (neww - oldw) / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + *x -= neww - oldw; + break; + } +} + +void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh) +{ + /* these should be the current values. this is for when you're not moving, + just resizing */ + g_assert(*y == self->area.y); + g_assert(oldh == self->area.height); + + /* vertical */ + switch (self->gravity) { + default: + case NorthWestGravity: + case NorthGravity: + case NorthEastGravity: + case StaticGravity: + case ForgetGravity: + break; + case WestGravity: + case CenterGravity: + case EastGravity: + *y -= (newh - oldh) / 2; + break; + case SouthWestGravity: + case SouthGravity: + case SouthEastGravity: + *y -= newh - oldh; + break; + } +} + +void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, + gint *logicalw, gint *logicalh, + gboolean user) +{ + Rect desired = {*x, *y, *w, *h}; + frame_rect_to_frame(self->frame, &desired); + + /* make the frame recalculate its dimensions n shit without changing + anything visible for real, this way the constraints below can work with + the updated frame dimensions. */ + frame_adjust_area(self->frame, FALSE, TRUE, TRUE); + + /* cap any X windows at the size of an unsigned short */ + *w = MIN(*w, + (gint)G_MAXUSHORT + - self->frame->size.left - self->frame->size.right); + *h = MIN(*h, + (gint)G_MAXUSHORT + - self->frame->size.top - self->frame->size.bottom); + + /* gets the frame's position */ + frame_client_gravity(self->frame, x, y); + + /* these positions are frame positions, not client positions */ + + /* set the size and position if fullscreen */ + if (self->fullscreen) { + const Rect *a; + guint i; + + i = screen_find_monitor(&desired); + a = screen_physical_area_monitor(i); + + *x = a->x; + *y = a->y; + *w = a->width; + *h = a->height; + + user = FALSE; /* ignore if the client can't be moved/resized when it + is fullscreening */ + } else if (self->max_horz || self->max_vert) { + Rect *a; + guint i; + + /* use all possible struts when maximizing to the full screen */ + i = screen_find_monitor(&desired); + a = screen_area(self->desktop, i, + (self->max_horz && self->max_vert ? NULL : &desired)); + + /* set the size and position if maximized */ + if (self->max_horz) { + *x = a->x; + *w = a->width - self->frame->size.left - self->frame->size.right; + } + if (self->max_vert) { + *y = a->y; + *h = a->height - self->frame->size.top - self->frame->size.bottom; + } + + user = FALSE; /* ignore if the client can't be moved/resized when it + is maximizing */ + + g_slice_free(Rect, a); + } + + /* gets the client's position */ + frame_frame_gravity(self->frame, x, y); + + /* work within the preferred sizes given by the window, these may have + changed rather than it's requested width and height, so always run + through this code */ + { + gint basew, baseh, minw, minh; + gint incw, inch, maxw, maxh; + gfloat minratio, maxratio; + + incw = self->size_inc.width; + inch = self->size_inc.height; + minratio = self->fullscreen || (self->max_horz && self->max_vert) ? + 0 : self->min_ratio; + maxratio = self->fullscreen || (self->max_horz && self->max_vert) ? + 0 : self->max_ratio; + + /* base size is substituted with min size if not specified */ + if (self->base_size.width >= 0 || self->base_size.height >= 0) { + basew = self->base_size.width; + baseh = self->base_size.height; + } else { + basew = self->min_size.width; + baseh = self->min_size.height; + } + /* min size is substituted with base size if not specified */ + if (self->min_size.width || self->min_size.height) { + minw = self->min_size.width; + minh = self->min_size.height; + } else { + minw = self->base_size.width; + minh = self->base_size.height; + } + + /* This comment is no longer true */ + /* if this is a user-requested resize, then check against min/max + sizes */ + + /* smaller than min size or bigger than max size? */ + if (*w > self->max_size.width) *w = self->max_size.width; + if (*w < minw) *w = minw; + if (*h > self->max_size.height) *h = self->max_size.height; + if (*h < minh) *h = minh; + + *w -= basew; + *h -= baseh; + + /* the sizes to used for maximized */ + maxw = *w; + maxh = *h; + + /* keep to the increments */ + *w /= incw; + *h /= inch; + + /* you cannot resize to nothing */ + if (basew + *w < 1) *w = 1 - basew; + if (baseh + *h < 1) *h = 1 - baseh; + + /* save the logical size */ + *logicalw = incw > 1 ? *w : *w + basew; + *logicalh = inch > 1 ? *h : *h + baseh; + + *w *= incw; + *h *= inch; + + /* if maximized/fs then don't use the size increments */ + if (self->fullscreen || self->max_horz) *w = maxw; + if (self->fullscreen || self->max_vert) *h = maxh; + + *w += basew; + *h += baseh; + + /* adjust the height to match the width for the aspect ratios. + for this, min size is not substituted for base size ever. */ + *w -= self->base_size.width; + *h -= self->base_size.height; + + if (minratio) + if (*h * minratio > *w) { + *h = (gint)(*w / minratio); + + /* you cannot resize to nothing */ + if (*h < 1) { + *h = 1; + *w = (gint)(*h * minratio); + } + } + if (maxratio) + if (*h * maxratio < *w) { + *h = (gint)(*w / maxratio); + + /* you cannot resize to nothing */ + if (*h < 1) { + *h = 1; + *w = (gint)(*h * minratio); + } + } + + *w += self->base_size.width; + *h += self->base_size.height; + } + + /* these override the above states! if you cant move you can't move! */ + if (user) { + if (!(self->functions & OB_CLIENT_FUNC_MOVE)) { + *x = self->area.x; + *y = self->area.y; + } + if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) { + *w = self->area.width; + *h = self->area.height; + } + } + + g_assert(*w > 0); + g_assert(*h > 0); +} + +void client_configure(ObClient *self, gint x, gint y, gint w, gint h, + gboolean user, gboolean final, gboolean force_reply) +{ + Rect oldframe, oldclient; + gboolean send_resize_client; + gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE; + gboolean fmoved, fresized; + guint fdecor = self->frame->decorations; + gboolean fhorz = self->frame->max_horz; + gboolean fvert = self->frame->max_vert; + gint logicalw, logicalh; + + /* find the new x, y, width, and height (and logical size) */ + client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user); + + /* set the logical size if things changed */ + if (!(w == self->area.width && h == self->area.height)) + SIZE_SET(self->logical_size, logicalw, logicalh); + + /* figure out if we moved or resized or what */ + moved = (x != self->area.x || y != self->area.y); + resized = (w != self->area.width || h != self->area.height); + + oldframe = self->frame->area; + oldclient = self->area; + RECT_SET(self->area, x, y, w, h); + + /* for app-requested resizes, always resize if 'resized' is true. + for user-requested ones, only resize if final is true, or when + resizing in redraw mode */ + send_resize_client = ((!user && resized) || + (user && (final || + (resized && config_resize_redraw)))); + + /* if the client is enlarging, then resize the client before the frame */ + if (send_resize_client && (w > oldclient.width || h > oldclient.height)) { + XMoveResizeWindow(obt_display, self->window, + self->frame->size.left, self->frame->size.top, + MAX(w, oldclient.width), MAX(h, oldclient.height)); + frame_adjust_client_area(self->frame); + } + + /* find the frame's dimensions and move/resize it */ + fmoved = moved; + fresized = resized; + + /* if decorations changed, then readjust everything for the frame */ + if (self->decorations != fdecor || + self->max_horz != fhorz || self->max_vert != fvert) + { + fmoved = fresized = TRUE; + } + + /* adjust the frame */ + if (fmoved || fresized) { + gulong ignore_start; + if (!user) + ignore_start = event_start_ignore_all_enters(); + + /* replay pending pointer event before move the window, in case it + would change what window gets the event */ + mouse_replay_pointer(); + + frame_adjust_area(self->frame, fmoved, fresized, FALSE); + + if (!user) + event_end_ignore_all_enters(ignore_start); + } + + if (!user || final) { + gint oldrx = self->root_pos.x; + gint oldry = self->root_pos.y; + /* we have reset the client to 0 border width, so don't include + it in these coords */ + POINT_SET(self->root_pos, + self->frame->area.x + self->frame->size.left - + self->border_width, + self->frame->area.y + self->frame->size.top - + self->border_width); + if (self->root_pos.x != oldrx || self->root_pos.y != oldry) + rootmoved = TRUE; + } + + /* This is kinda tricky and should not be changed.. let me explain! + + When user = FALSE, then the request is coming from the application + itself, and we are more strict about when to send a synthetic + ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5 + in this case (or send one if force_reply is true) + + When user = TRUE, then the request is coming from "us", like when we + maximize a window or something. In this case we are more lenient. We + used to follow the same rules as above, but _Java_ Swing can't handle + this. So just to appease Swing, when user = TRUE, we always send + a synthetic ConfigureNotify to give the window its root coordinates. + Lastly, if force_reply is TRUE, we always send a + ConfigureNotify, which is needed during a resize with XSYNCronization. + */ + if ((!user && !resized && (rootmoved || force_reply)) || + (user && ((!resized && force_reply) || (final && rootmoved)))) + { + XEvent event; + + event.type = ConfigureNotify; + event.xconfigure.display = obt_display; + event.xconfigure.event = self->window; + event.xconfigure.window = self->window; + + ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d", + self->title, self->root_pos.x, self->root_pos.y, w, h); + + /* root window real coords */ + event.xconfigure.x = self->root_pos.x; + event.xconfigure.y = self->root_pos.y; + event.xconfigure.width = w; + event.xconfigure.height = h; + event.xconfigure.border_width = self->border_width; + event.xconfigure.above = None; + event.xconfigure.override_redirect = FALSE; + XSendEvent(event.xconfigure.display, event.xconfigure.window, + FALSE, StructureNotifyMask, &event); + } + + /* if the client is shrinking, then resize the frame before the client. + + both of these resize sections may run, because the top one only resizes + in the direction that is growing + */ + if (send_resize_client && (w <= oldclient.width || h <= oldclient.height)) + { + frame_adjust_client_area(self->frame); + XMoveResizeWindow(obt_display, self->window, + self->frame->size.left, self->frame->size.top, w, h); + } + + XFlush(obt_display); + + /* if it moved between monitors, then this can affect the stacking + layer of this window or others - for fullscreen windows. + also if it changed to/from oldschool fullscreen then its layer may + change + + watch out tho, don't try change stacking stuff if the window is no + longer being managed ! + */ + if (self->managed && + (screen_find_monitor(&self->frame->area) != + screen_find_monitor(&oldframe) || + (final && (client_is_oldfullscreen(self, &oldclient) != + client_is_oldfullscreen(self, &self->area))))) + { + client_calc_layer(self); + } +} + +void client_fullscreen(ObClient *self, gboolean fs) +{ + gint x, y, w, h; + + if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */ + self->fullscreen == fs) return; /* already done */ + + self->fullscreen = fs; + client_change_state(self); /* change the state hints on the client */ + + if (fs) { + self->pre_fullscreen_area = self->area; + self->pre_fullscreen_max_horz = self->max_horz; + self->pre_fullscreen_max_vert = self->max_vert; + + /* if the window is maximized, its area isn't all that meaningful. + save its premax area instead. */ + if (self->max_horz) { + self->pre_fullscreen_area.x = self->pre_max_area.x; + self->pre_fullscreen_area.width = self->pre_max_area.width; + } + if (self->max_vert) { + self->pre_fullscreen_area.y = self->pre_max_area.y; + self->pre_fullscreen_area.height = self->pre_max_area.height; + } + + /* these will help configure_full figure out where to fullscreen + the window */ + x = self->area.x; + y = self->area.y; + w = self->area.width; + h = self->area.height; + } else { + g_assert(self->pre_fullscreen_area.width > 0 && + self->pre_fullscreen_area.height > 0); + + self->max_horz = self->pre_fullscreen_max_horz; + self->max_vert = self->pre_fullscreen_max_vert; + if (self->max_horz) { + self->pre_max_area.x = self->pre_fullscreen_area.x; + self->pre_max_area.width = self->pre_fullscreen_area.width; + } + if (self->max_vert) { + self->pre_max_area.y = self->pre_fullscreen_area.y; + self->pre_max_area.height = self->pre_fullscreen_area.height; + } + + x = self->pre_fullscreen_area.x; + y = self->pre_fullscreen_area.y; + w = self->pre_fullscreen_area.width; + h = self->pre_fullscreen_area.height; + RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); + } + + ob_debug("Window %s going fullscreen (%d)", + self->title, self->fullscreen); + + if (fs) { + /* make sure the window is on some monitor */ + client_find_onscreen(self, &x, &y, w, h, FALSE); + } + + client_setup_decor_and_functions(self, FALSE); + client_move_resize(self, x, y, w, h); + + /* and adjust our layer/stacking. do this after resizing the window, + and applying decorations, because windows which fill the screen are + considered "fullscreen" and it affects their layer */ + client_calc_layer(self); + + if (fs) { + /* try focus us when we go into fullscreen mode */ + client_focus(self); + } +} + +static void client_iconify_recursive(ObClient *self, + gboolean iconic, gboolean curdesk, + gboolean hide_animation) +{ + GSList *it; + gboolean changed = FALSE; + + if (self->iconic != iconic) { + ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"), + self->window); + + if (iconic) { + /* don't let non-normal windows iconify along with their parents + or whatever */ + if (client_normal(self)) { + self->iconic = iconic; + + /* update the focus lists.. iconic windows go to the bottom of + the list. this will also call focus_cycle_addremove(). */ + focus_order_to_bottom(self); + + changed = TRUE; + } + } else { + self->iconic = iconic; + + if (curdesk && self->desktop != screen_desktop && + self->desktop != DESKTOP_ALL) + client_set_desktop(self, screen_desktop, FALSE, FALSE); + + /* this puts it after the current focused window, this will + also cause focus_cycle_addremove() to be called for the + client */ + focus_order_like_new(self); + + changed = TRUE; + } + } + + if (changed) { + client_change_state(self); + if (config_animate_iconify && !hide_animation) + frame_begin_iconify_animation(self->frame, iconic); + /* do this after starting the animation so it doesn't flash */ + client_showhide(self); + } + + /* iconify all direct transients, and deiconify all transients + (non-direct too) */ + for (it = self->transients; it; it = g_slist_next(it)) + if (it->data != self) + if (client_is_direct_child(self, it->data) || !iconic) + client_iconify_recursive(it->data, iconic, curdesk, + hide_animation); +} + +void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, + gboolean hide_animation) +{ + if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) { + /* move up the transient chain as far as possible first */ + self = client_search_top_direct_parent(self); + client_iconify_recursive(self, iconic, curdesk, hide_animation); + } +} + +void client_maximize(ObClient *self, gboolean max, gint dir) +{ + gint x, y, w, h; + + g_assert(dir == 0 || dir == 1 || dir == 2); + if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */ + + /* check if already done */ + if (max) { + if (dir == 0 && self->max_horz && self->max_vert) return; + if (dir == 1 && self->max_horz) return; + if (dir == 2 && self->max_vert) return; + } else { + if (dir == 0 && !self->max_horz && !self->max_vert) return; + if (dir == 1 && !self->max_horz) return; + if (dir == 2 && !self->max_vert) return; + } + + /* these will help configure_full figure out which screen to fill with + the window */ + x = self->area.x; + y = self->area.y; + w = self->area.width; + h = self->area.height; + + if (max) { + if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */ + RECT_SET(self->pre_max_area, + self->area.x, self->pre_max_area.y, + self->area.width, self->pre_max_area.height); + } + if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */ + RECT_SET(self->pre_max_area, + self->pre_max_area.x, self->area.y, + self->pre_max_area.width, self->area.height); + } + } else { + if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */ + g_assert(self->pre_max_area.width > 0); + + x = self->pre_max_area.x; + w = self->pre_max_area.width; + + RECT_SET(self->pre_max_area, 0, self->pre_max_area.y, + 0, self->pre_max_area.height); + } + if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */ + g_assert(self->pre_max_area.height > 0); + + y = self->pre_max_area.y; + h = self->pre_max_area.height; + + RECT_SET(self->pre_max_area, self->pre_max_area.x, 0, + self->pre_max_area.width, 0); + } + } + + if (dir == 0 || dir == 1) /* horz */ + self->max_horz = max; + if (dir == 0 || dir == 2) /* vert */ + self->max_vert = max; + + if (max) { + /* make sure the window is on some monitor */ + client_find_onscreen(self, &x, &y, w, h, FALSE); + } + + client_change_state(self); /* change the state hints on the client */ + + client_setup_decor_and_functions(self, FALSE); + client_move_resize(self, x, y, w, h); +} + +void client_shade(ObClient *self, gboolean shade) +{ + if ((!(self->functions & OB_CLIENT_FUNC_SHADE) && + shade) || /* can't shade */ + self->shaded == shade) return; /* already done */ + + self->shaded = shade; + client_change_state(self); + client_change_wm_state(self); /* the window is being hidden/shown */ + /* resize the frame to just the titlebar */ + frame_adjust_area(self->frame, FALSE, TRUE, FALSE); +} + +static void client_ping_event(ObClient *self, gboolean dead) +{ + if (self->not_responding != dead) { + self->not_responding = dead; + client_update_title(self); + + if (dead) + /* the client isn't responding, so ask to kill it */ + client_prompt_kill(self); + else { + /* it came back to life ! */ + + if (self->kill_prompt) { + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + } + + self->kill_level = 0; + } + } +} + +void client_close(ObClient *self) +{ + if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; + + /* if closing an internal obprompt, that is just cancelling it */ + if (self->prompt) { + prompt_cancel(self->prompt); + return; + } + + /* in the case that the client provides no means to requesting that it + close, we just kill it */ + if (!self->delete_window) + /* don't use client_kill(), we should only kill based on PID in + response to a lack of PING replies */ + XKillClient(obt_display, self->window); + else { + /* request the client to close with WM_DELETE_WINDOW */ + OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS, + OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(), + 0, 0, 0, NoEventMask); + + /* we're trying to close the window, so see if it is responding. if it + is not, then we will let them kill the window */ + if (self->ping) + ping_start(self, client_ping_event); + + /* if we already know the window isn't responding (maybe they clicked + no in the kill dialog but it hasn't come back to life), then show + the kill dialog */ + if (self->not_responding) + client_prompt_kill(self); + } +} + +#define OB_KILL_RESULT_NO 0 +#define OB_KILL_RESULT_YES 1 + +static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data) +{ + ObClient *self = data; + + if (result == OB_KILL_RESULT_YES) + client_kill(self); + return TRUE; /* call the cleanup func */ +} + +static void client_kill_cleanup(ObPrompt *p, gpointer data) +{ + ObClient *self = data; + + g_assert(p == self->kill_prompt); + + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; +} + +static void client_prompt_kill(ObClient *self) +{ + /* check if we're already prompting */ + if (!self->kill_prompt) { + ObPromptAnswer answers[] = { + { 0, OB_KILL_RESULT_NO }, + { 0, OB_KILL_RESULT_YES } + }; + gchar *m; + const gchar *y, *title; + + title = self->original_title; + if (title[0] == '\0') { + /* empty string, so use its parent */ + ObClient *p = client_search_top_direct_parent(self); + if (p) title = p->original_title; + } + + if (client_on_localhost(self)) { + const gchar *sig; + + if (self->kill_level == 0) + sig = "terminate"; + else + sig = "kill"; + + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), + title, sig); + y = _("End Process"); + } + else { + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"), + title); + y = _("Disconnect"); + } + /* set the dialog buttons' text */ + answers[0].text = _("Cancel"); /* "no" */ + answers[1].text = y; /* "yes" */ + + self->kill_prompt = prompt_new(m, NULL, answers, + sizeof(answers)/sizeof(answers[0]), + OB_KILL_RESULT_NO, /* default = no */ + OB_KILL_RESULT_NO, /* cancel = no */ + client_kill_requested, + client_kill_cleanup, + self); + g_free(m); + } + + prompt_show(self->kill_prompt, self, TRUE); +} + +void client_kill(ObClient *self) +{ + /* don't kill our own windows */ + if (self->prompt) return; + + if (client_on_localhost(self) && self->pid) { + /* running on the local host */ + if (self->kill_level == 0) { + ob_debug("killing window 0x%x with pid %lu, with SIGTERM", + self->window, self->pid); + kill(self->pid, SIGTERM); + ++self->kill_level; + + /* show that we're trying to kill it */ + client_update_title(self); + } + else { + ob_debug("killing window 0x%x with pid %lu, with SIGKILL", + self->window, self->pid); + kill(self->pid, SIGKILL); /* kill -9 */ + } + } + else { + /* running on a remote host */ + XKillClient(obt_display, self->window); + } +} + +void client_hilite(ObClient *self, gboolean hilite) +{ + if (self->demands_attention == hilite) + return; /* no change */ + + /* don't allow focused windows to hilite */ + self->demands_attention = hilite && !client_focused(self); + if (self->frame != NULL) { /* if we're mapping, just set the state */ + if (self->demands_attention) { + frame_flash_start(self->frame); + + /* if the window is on another desktop then raise it and make it + the most recently used window */ + if (self->desktop != screen_desktop && + self->desktop != DESKTOP_ALL) + { + stacking_raise(CLIENT_AS_WINDOW(self)); + focus_order_to_top(self); + } + } + else + frame_flash_stop(self->frame); + client_change_state(self); + } +} + +static void client_set_desktop_recursive(ObClient *self, + guint target, + gboolean donthide, + gboolean dontraise) +{ + guint old; + GSList *it; + + if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) { + + ob_debug("Setting desktop %u", target+1); + + g_assert(target < screen_num_desktops || target == DESKTOP_ALL); + + old = self->desktop; + self->desktop = target; + OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, target); + /* the frame can display the current desktop state */ + frame_adjust_state(self->frame); + /* 'move' the window to the new desktop */ + if (!donthide) + client_hide(self); + client_show(self); + /* raise if it was not already on the desktop */ + if (old != DESKTOP_ALL && !dontraise) + stacking_raise(CLIENT_AS_WINDOW(self)); + if (STRUT_EXISTS(self->strut)) + screen_update_areas(); + else + /* the new desktop's geometry may be different, so we may need to + resize, for example if we are maximized */ + client_reconfigure(self, FALSE); + + focus_cycle_addremove(self, FALSE); + } + + /* move all transients */ + for (it = self->transients; it; it = g_slist_next(it)) + if (it->data != self) + if (client_is_direct_child(self, it->data)) + client_set_desktop_recursive(it->data, target, + donthide, dontraise); +} + +void client_set_desktop(ObClient *self, guint target, + gboolean donthide, gboolean dontraise) +{ + self = client_search_top_direct_parent(self); + client_set_desktop_recursive(self, target, donthide, dontraise); + + focus_cycle_addremove(NULL, TRUE); +} + +gboolean client_is_direct_child(ObClient *parent, ObClient *child) +{ + while (child != parent && (child = client_direct_parent(child))); + return child == parent; +} + +ObClient *client_search_modal_child(ObClient *self) +{ + GSList *it; + ObClient *ret; + + for (it = self->transients; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if ((ret = client_search_modal_child(c))) return ret; + if (c->modal) return c; + } + return NULL; +} + +struct ObClientFindDestroyUnmap { + Window window; + gint ignore_unmaps; +}; + +static gboolean find_destroy_unmap(XEvent *e, gpointer data) +{ + struct ObClientFindDestroyUnmap *find = data; + if (e->type == DestroyNotify) + return e->xdestroywindow.window == find->window; + if (e->type == UnmapNotify && e->xunmap.window == find->window) + /* ignore the first $find->ignore_unmaps$ many unmap events */ + return --find->ignore_unmaps < 0; + return FALSE; +} + +gboolean client_validate(ObClient *self) +{ + struct ObClientFindDestroyUnmap find; + + XSync(obt_display, FALSE); /* get all events on the server */ + + find.window = self->window; + find.ignore_unmaps = self->ignore_unmaps; + if (xqueue_exists_local(find_destroy_unmap, &find)) + return FALSE; + + return TRUE; +} + +void client_set_wm_state(ObClient *self, glong state) +{ + if (state == self->wmstate) return; /* no change */ + + switch (state) { + case IconicState: + client_iconify(self, TRUE, TRUE, FALSE); + break; + case NormalState: + client_iconify(self, FALSE, TRUE, FALSE); + break; + } +} + +void client_set_state(ObClient *self, Atom action, glong data1, glong data2) +{ + gboolean shaded = self->shaded; + gboolean fullscreen = self->fullscreen; + gboolean undecorated = self->undecorated; + gboolean max_horz = self->max_horz; + gboolean max_vert = self->max_vert; + gboolean modal = self->modal; + gboolean iconic = self->iconic; + gboolean demands_attention = self->demands_attention; + gboolean above = self->above; + gboolean below = self->below; + gint i; + gboolean value; + + if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) || + action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) || + action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE))) + /* an invalid action was passed to the client message, ignore it */ + return; + + for (i = 0; i < 2; ++i) { + Atom state = i == 0 ? data1 : data2; + + if (!state) continue; + + /* if toggling, then pick whether we're adding or removing */ + if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) { + if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) + value = modal; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) + value = self->max_vert; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) + value = self->max_horz; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) + value = shaded; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) + value = self->skip_taskbar; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) + value = self->skip_pager; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) + value = self->iconic; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) + value = fullscreen; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) + value = self->above; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) + value = self->below; + else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)) + value = self->demands_attention; + else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) + value = undecorated; + else + g_assert_not_reached(); + action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : + OBT_PROP_ATOM(NET_WM_STATE_ADD); + } + + value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD); + if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) { + modal = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) { + max_vert = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) { + max_horz = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) { + shaded = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) { + self->skip_taskbar = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) { + self->skip_pager = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) { + iconic = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) { + fullscreen = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) { + above = value; + /* only unset below when setting above, otherwise you can't get to + the normal layer */ + if (value) + below = FALSE; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) { + /* and vice versa */ + if (value) + above = FALSE; + below = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){ + demands_attention = value; + } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) { + undecorated = value; + } + } + + if (max_horz != self->max_horz || max_vert != self->max_vert) { + if (max_horz != self->max_horz && max_vert != self->max_vert) { + /* toggling both */ + if (max_horz == max_vert) { /* both going the same way */ + client_maximize(self, max_horz, 0); + } else { + client_maximize(self, max_horz, 1); + client_maximize(self, max_vert, 2); + } + } else { + /* toggling one */ + if (max_horz != self->max_horz) + client_maximize(self, max_horz, 1); + else + client_maximize(self, max_vert, 2); + } + } + /* change fullscreen state before shading, as it will affect if the window + can shade or not */ + if (fullscreen != self->fullscreen) + client_fullscreen(self, fullscreen); + if (shaded != self->shaded) + client_shade(self, shaded); + if (undecorated != self->undecorated) + client_set_undecorated(self, undecorated); + if (above != self->above || below != self->below) { + self->above = above; + self->below = below; + client_calc_layer(self); + } + + if (modal != self->modal) { + self->modal = modal; + /* when a window changes modality, then its stacking order with its + transients needs to change */ + stacking_raise(CLIENT_AS_WINDOW(self)); + + /* it also may get focused. if something is focused that shouldn't + be focused anymore, then move the focus */ + if (focus_client && client_focus_target(focus_client) != focus_client) + client_focus(focus_client); + } + + if (iconic != self->iconic) + client_iconify(self, iconic, FALSE, FALSE); + + if (demands_attention != self->demands_attention) + client_hilite(self, demands_attention); + + client_change_state(self); /* change the hint to reflect these changes */ + + focus_cycle_addremove(self, TRUE); +} + +ObClient *client_focus_target(ObClient *self) +{ + ObClient *child = NULL; + + child = client_search_modal_child(self); + if (child) return child; + return self; +} + +gboolean client_can_focus(ObClient *self) +{ + /* choose the correct target */ + self = client_focus_target(self); + + if (!self->frame->visible) + return FALSE; + + if (!(self->can_focus || self->focus_notify)) + return FALSE; + + return TRUE; +} + +gboolean client_focus(ObClient *self) +{ + if (!client_validate(self)) return FALSE; + + /* we might not focus this window, so if we have modal children which would + be focused instead, bring them to this desktop */ + client_bring_modal_windows(self); + + /* choose the correct target */ + self = client_focus_target(self); + + if (!client_can_focus(self)) { + ob_debug_type(OB_DEBUG_FOCUS, + "Client %s can't be focused", self->title); + return FALSE; + } + + /* if we have helper windows they should be there with the window */ + client_bring_helper_windows(self); + + ob_debug_type(OB_DEBUG_FOCUS, + "Focusing client \"%s\" (0x%x) at time %u", + self->title, self->window, event_time()); + + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + + obt_display_ignore_errors(TRUE); + + if (self->can_focus) { + /* This can cause a BadMatch error with CurrentTime, or if an app + passed in a bad time for _NET_WM_ACTIVE_WINDOW. */ + XSetInputFocus(obt_display, self->window, RevertToPointerRoot, + event_time()); + } + + if (self->focus_notify) { + XEvent ce; + ce.xclient.type = ClientMessage; + ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS); + ce.xclient.display = obt_display; + ce.xclient.window = self->window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS); + ce.xclient.data.l[1] = event_time(); + ce.xclient.data.l[2] = 0l; + ce.xclient.data.l[3] = 0l; + ce.xclient.data.l[4] = 0l; + XSendEvent(obt_display, self->window, FALSE, NoEventMask, &ce); + } + + obt_display_ignore_errors(FALSE); + + ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d", + obt_display_error_occured); + return !obt_display_error_occured; +} + +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade) +{ + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE, self); + if (self->iconic) + client_iconify(self, FALSE, here, FALSE); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) + { + if (here) + client_set_desktop(self, screen_desktop, FALSE, TRUE); + else + screen_set_desktop(self->desktop, FALSE); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded && unshade) + client_shade(self, FALSE); + if (raise) + stacking_raise(CLIENT_AS_WINDOW(self)); + + client_focus(self); +} + +/* this function exists to map to the net_active_window message in the ewmh */ +void client_activate(ObClient *self, gboolean desktop, + gboolean here, gboolean raise, + gboolean unshade, gboolean user) +{ + self = client_focus_target(self); + + if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime)) + client_present(self, here, raise, unshade); + else + client_hilite(self, TRUE); +} + +static void client_bring_windows_recursive(ObClient *self, + guint desktop, + gboolean helpers, + gboolean modals, + gboolean iconic) +{ + GSList *it; + + for (it = self->transients; it; it = g_slist_next(it)) + client_bring_windows_recursive(it->data, desktop, + helpers, modals, iconic); + + if (((helpers && client_helper(self)) || + (modals && self->modal)) && + (!screen_compare_desktops(self->desktop, desktop) || + (iconic && self->iconic))) + { + if (iconic && self->iconic) + client_iconify(self, FALSE, TRUE, FALSE); + else + client_set_desktop(self, desktop, FALSE, FALSE); + } +} + +void client_bring_helper_windows(ObClient *self) +{ + client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE); +} + +void client_bring_modal_windows(ObClient *self) +{ + client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE); +} + +gboolean client_focused(ObClient *self) +{ + return self == focus_client; +} + +RrImage* client_icon(ObClient *self) +{ + RrImage *ret = NULL; + + if (self->icon_set) + ret = self->icon_set; + else if (self->parents) { + GSList *it; + for (it = self->parents; it && !ret; it = g_slist_next(it)) + ret = client_icon(it->data); + } + if (!ret) + ret = client_default_icon; + return ret; +} + +void client_set_layer(ObClient *self, gint layer) +{ + if (layer < 0) { + self->below = TRUE; + self->above = FALSE; + } else if (layer == 0) { + self->below = self->above = FALSE; + } else { + self->below = FALSE; + self->above = TRUE; + } + client_calc_layer(self); + client_change_state(self); /* reflect this in the state hints */ +} + +void client_set_undecorated(ObClient *self, gboolean undecorated) +{ + if (self->undecorated != undecorated && + /* don't let it undecorate if the function is missing, but let + it redecorate */ + (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated)) + { + self->undecorated = undecorated; + client_setup_decor_and_functions(self, TRUE); + client_change_state(self); /* reflect this in the state hints */ + } +} + +guint client_monitor(ObClient *self) +{ + return screen_find_monitor(&self->frame->area); +} + +ObClient *client_direct_parent(ObClient *self) +{ + if (!self->parents) return NULL; + if (self->transient_for_group) return NULL; + return self->parents->data; +} + +ObClient *client_search_top_direct_parent(ObClient *self) +{ + ObClient *p; + while ((p = client_direct_parent(self))) self = p; + return self; +} + +static GSList *client_search_all_top_parents_internal(ObClient *self, + gboolean bylayer, + ObStackingLayer layer) +{ + GSList *ret; + ObClient *p; + + /* move up the direct transient chain as far as possible */ + while ((p = client_direct_parent(self)) && + (!bylayer || p->layer == layer)) + self = p; + + if (!self->parents) + ret = g_slist_prepend(NULL, self); + else + ret = g_slist_copy(self->parents); + + return ret; +} + +GSList *client_search_all_top_parents(ObClient *self) +{ + return client_search_all_top_parents_internal(self, FALSE, 0); +} + +GSList *client_search_all_top_parents_layer(ObClient *self) +{ + return client_search_all_top_parents_internal(self, TRUE, self->layer); +} + +ObClient *client_search_focus_parent(ObClient *self) +{ + GSList *it; + + for (it = self->parents; it; it = g_slist_next(it)) + if (client_focused(it->data)) return it->data; + + return NULL; +} + +ObClient *client_search_focus_parent_full(ObClient *self) +{ + GSList *it; + ObClient *ret = NULL; + + for (it = self->parents; it; it = g_slist_next(it)) { + if (client_focused(it->data)) + ret = it->data; + else + ret = client_search_focus_parent_full(it->data); + if (ret) break; + } + return ret; +} + +ObClient *client_search_parent(ObClient *self, ObClient *search) +{ + GSList *it; + + for (it = self->parents; it; it = g_slist_next(it)) + if (it->data == search) return search; + + return NULL; +} + +ObClient *client_search_transient(ObClient *self, ObClient *search) +{ + GSList *sit; + + for (sit = self->transients; sit; sit = g_slist_next(sit)) { + if (sit->data == search) + return search; + if (client_search_transient(sit->data, search)) + return search; + } + return NULL; +} + +static void detect_edge(Rect area, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) +{ + gint edge_start, edge_size, head, tail; + gboolean skip_head = FALSE, skip_tail = FALSE; + + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + edge_start = area.x; + edge_size = area.width; + break; + case OB_DIRECTION_EAST: + case OB_DIRECTION_WEST: + edge_start = area.y; + edge_size = area.height; + break; + default: + g_assert_not_reached(); + } + + /* do we collide with this window? */ + if (!RANGES_INTERSECT(my_edge_start, my_edge_size, + edge_start, edge_size)) + return; + + switch (dir) { + case OB_DIRECTION_NORTH: + head = RECT_BOTTOM(area); + tail = RECT_TOP(area); + break; + case OB_DIRECTION_SOUTH: + head = RECT_TOP(area); + tail = RECT_BOTTOM(area); + break; + case OB_DIRECTION_WEST: + head = RECT_RIGHT(area); + tail = RECT_LEFT(area); + break; + case OB_DIRECTION_EAST: + head = RECT_LEFT(area); + tail = RECT_RIGHT(area); + break; + default: + g_assert_not_reached(); + } + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_WEST: + /* check if our window is past the head of this window */ + if (my_head <= head + 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head + my_size - 1 <= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head + (*near_edge ? 0 : my_size) <= *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail - (!*near_edge ? 0 : my_size) <= *dest) + skip_tail = TRUE; + break; + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_EAST: + /* check if our window is past the head of this window */ + if (my_head >= head - 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head - my_size + 1 >= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head - (*near_edge ? 0 : my_size) >= *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail + (!*near_edge ? 0 : my_size) >= *dest) + skip_tail = TRUE; + break; + default: + g_assert_not_reached(); + } + + ob_debug("my head %d size %d", my_head, my_size); + ob_debug("head %d tail %d dest %d", head, tail, *dest); + if (!skip_head) { + ob_debug("using near edge %d", head); + *dest = head; + *near_edge = TRUE; + } + else if (!skip_tail) { + ob_debug("using far edge %d", tail); + *dest = tail; + *near_edge = FALSE; + } +} + +void client_find_edge_directional(ObClient *self, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) +{ + GList *it; + Rect *a; + Rect dock_area; + gint edge; + guint i; + + a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, + &self->frame->area); + + switch (dir) { + case OB_DIRECTION_NORTH: + edge = RECT_TOP(*a) - 1; + break; + case OB_DIRECTION_SOUTH: + edge = RECT_BOTTOM(*a) + 1; + break; + case OB_DIRECTION_EAST: + edge = RECT_RIGHT(*a) + 1; + break; + case OB_DIRECTION_WEST: + edge = RECT_LEFT(*a) - 1; + break; + default: + g_assert_not_reached(); + } + /* default to the far edge, then narrow it down */ + *dest = edge; + *near_edge = TRUE; + + /* search for edges of monitors */ + for (i = 0; i < screen_num_monitors; ++i) { + Rect *area = screen_area(self->desktop, i, NULL); + detect_edge(*area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + g_slice_free(Rect, area); + } + + /* search for edges of clients */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *cur = it->data; + + /* skip windows to not bump into */ + if (cur == self) + continue; + if (cur->iconic) + continue; + if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && + cur->desktop != screen_desktop) + continue; + + ob_debug("trying window %s", cur->title); + + detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + } + dock_get_area(&dock_area); + detect_edge(dock_area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + + g_slice_free(Rect, a); +} + +void client_find_move_directional(ObClient *self, ObDirection dir, + gint *x, gint *y) +{ + gint head, size; + gint e, e_start, e_size; + gboolean near; + + switch (dir) { + case OB_DIRECTION_EAST: + head = RECT_RIGHT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + default: + g_assert_not_reached(); + } + + client_find_edge_directional(self, dir, head, size, + e_start, e_size, &e, &near); + *x = self->frame->area.x; + *y = self->frame->area.y; + switch (dir) { + case OB_DIRECTION_EAST: + if (near) e -= self->frame->area.width; + else e++; + *x = e; + break; + case OB_DIRECTION_WEST: + if (near) e++; + else e -= self->frame->area.width; + *x = e; + break; + case OB_DIRECTION_NORTH: + if (near) e++; + else e -= self->frame->area.height; + *y = e; + break; + case OB_DIRECTION_SOUTH: + if (near) e -= self->frame->area.height; + else e++; + *y = e; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); +} + +void client_find_resize_directional(ObClient *self, ObDirection side, + gboolean grow, + gint *x, gint *y, gint *w, gint *h) +{ + gint head; + gint e, e_start, e_size, delta; + gboolean near; + ObDirection dir; + + switch (side) { + case OB_DIRECTION_EAST: + head = RECT_RIGHT(self->frame->area) + + (self->size_inc.width - 1) * (grow ? 1 : 0); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area) - + (self->size_inc.width - 1) * (grow ? 1 : 0); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area) - + (self->size_inc.height - 1) * (grow ? 1 : 0); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area) + + (self->size_inc.height - 1) * (grow ? 1 : 0); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH; + break; + default: + g_assert_not_reached(); + } + + ob_debug("head %d dir %d", head, dir); + client_find_edge_directional(self, dir, head, 1, + e_start, e_size, &e, &near); + ob_debug("edge %d", e); + *x = self->frame->area.x; + *y = self->frame->area.y; + *w = self->frame->area.width; + *h = self->frame->area.height; + switch (side) { + case OB_DIRECTION_EAST: + if (grow == near) --e; + delta = e - RECT_RIGHT(self->frame->area); + *w += delta; + break; + case OB_DIRECTION_WEST: + if (grow == near) ++e; + delta = RECT_LEFT(self->frame->area) - e; + *x -= delta; + *w += delta; + break; + case OB_DIRECTION_NORTH: + if (grow == near) ++e; + delta = RECT_TOP(self->frame->area) - e; + *y -= delta; + *h += delta; + break; + case OB_DIRECTION_SOUTH: + if (grow == near) --e; + delta = e - RECT_BOTTOM(self->frame->area); + *h += delta; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); + *w -= self->frame->size.left + self->frame->size.right; + *h -= self->frame->size.top + self->frame->size.bottom; +} + +ObClient* client_under_pointer(void) +{ + gint x, y; + GList *it; + ObClient *ret = NULL; + + if (screen_pointer_pos(&x, &y)) { + for (it = stacking_list; it; it = g_list_next(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = WINDOW_AS_CLIENT(it->data); + if (c->frame->visible && + /* check the desktop, this is done during desktop + switching and windows are shown/hidden status is not + reliable */ + (c->desktop == screen_desktop || + c->desktop == DESKTOP_ALL) && + /* ignore all animating windows */ + !frame_iconify_animating(c->frame) && + RECT_CONTAINS(c->frame->area, x, y)) + { + ret = c; + break; + } + } + } + } + return ret; +} + +gboolean client_has_group_siblings(ObClient *self) +{ + return self->group && self->group->members->next; +} + +gboolean client_has_relative(ObClient *self) +{ + return client_has_parent(self) || + client_has_group_siblings(self) || + client_has_children(self); +} + +/*! Returns TRUE if the client is running on the same machine as Openbox */ +gboolean client_on_localhost(ObClient *self) +{ + return self->client_machine == NULL; +} diff --git a/openbox/client.h b/openbox/client.h new file mode 100644 index 0000000..d5b344f --- /dev/null +++ b/openbox/client.h @@ -0,0 +1,758 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __client_h +#define __client_h + +#include "misc.h" +#include "mwm.h" +#include "geom.h" +#include "stacking.h" +#include "window.h" +#include "obrender/color.h" + +#include <glib.h> +#include <X11/Xlib.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> /* for pid_t */ +#endif + +struct _ObFrame; +struct _ObGroup; +struct _ObSessionState; +struct _ObPrompt; + +typedef struct _ObClient ObClient; + +/*! Possible window types */ +typedef enum +{ + OB_CLIENT_TYPE_DESKTOP, /*!< A desktop (bottom-most window) */ + OB_CLIENT_TYPE_DOCK, /*!< A dock bar/panel window */ + OB_CLIENT_TYPE_TOOLBAR, /*!< A toolbar window, pulled off an app */ + OB_CLIENT_TYPE_MENU, /*!< An unpinned menu from an app */ + OB_CLIENT_TYPE_UTILITY, /*!< A small utility window such as a palette */ + OB_CLIENT_TYPE_SPLASH, /*!< A splash screen window */ + OB_CLIENT_TYPE_DIALOG, /*!< A dialog window */ + OB_CLIENT_TYPE_NORMAL /*!< A normal application window */ +} ObClientType; + +/*! The things the user can do to the client window */ +typedef enum +{ + OB_CLIENT_FUNC_RESIZE = 1 << 0, /*!< Allow user resizing */ + OB_CLIENT_FUNC_MOVE = 1 << 1, /*!< Allow user moving */ + OB_CLIENT_FUNC_ICONIFY = 1 << 2, /*!< Allow to be iconified */ + OB_CLIENT_FUNC_MAXIMIZE = 1 << 3, /*!< Allow to be maximized */ + OB_CLIENT_FUNC_SHADE = 1 << 4, /*!< Allow to be shaded */ + OB_CLIENT_FUNC_FULLSCREEN = 1 << 5, /*!< Allow to be made fullscreen */ + OB_CLIENT_FUNC_CLOSE = 1 << 6, /*!< Allow to be closed */ + OB_CLIENT_FUNC_ABOVE = 1 << 7, /*!< Allow to be put in lower layer */ + OB_CLIENT_FUNC_BELOW = 1 << 8, /*!< Allow to be put in higher layer */ + OB_CLIENT_FUNC_UNDECORATE = 1 << 9 /*!< Allow to be undecorated */ +} ObFunctions; + +struct _ObClient +{ + ObWindow obwin; + Window window; + gboolean managed; + + /*! If this client is managing an ObPrompt window, then this is set to the + prompt */ + struct _ObPrompt *prompt; + + /*! The window's decorations. NULL while the window is being managed! */ + struct _ObFrame *frame; + + /*! The number of unmap events to ignore on the window */ + gint ignore_unmaps; + + /*! The id of the group the window belongs to */ + struct _ObGroup *group; + + /*! Saved session data to apply to this client */ + struct _ObSessionState *session; + + /*! Whether or not the client is a transient window. It may or may not + have parents when this is true. */ + gboolean transient; + /*! Whether or not the client is transient for its group */ + gboolean transient_for_group; + /*! The client which are parents of this client */ + GSList *parents; + /*! The clients which are transients (children) of this client */ + GSList *transients; + /*! The desktop on which the window resides (0xffffffff for all + desktops) */ + guint desktop; + + /*! The startup id for the startup-notification protocol. This will be + NULL if a startup id is not set. */ + gchar *startup_id; + + /*! Normal window title */ + gchar *title; + /*! Window title when iconified */ + gchar *icon_title; + /*! The title as requested by the client, without any of our own changes */ + gchar *original_title; + /*! Hostname of machine running the client */ + gchar *client_machine; + /*! The command used to run the program. Pre-XSMP window identification. */ + gchar *wm_command; + /*! The PID of the process which owns the window */ + pid_t pid; + + /*! The application that created the window */ + gchar *name; + /*! The class of the window, can used for grouping */ + gchar *class; + /*! The specified role of the window, used for identification */ + gchar *role; + /*! The session client id for the window. *This can be NULL!* */ + gchar *sm_client_id; + + /*! The type of window (what its function is) */ + ObClientType type; + + /*! Position and size of the window + This will not always be the actual position of the window on screen, it + is, rather, the position requested by the client, to which the window's + gravity is applied. + */ + Rect area; + + /*! Position of the client window relative to the root window */ + Point root_pos; + + /*! Position and size of the window prior to being maximized */ + Rect pre_max_area; + /*! Position and size of the window prior to being fullscreened */ + Rect pre_fullscreen_area; + /*! Remember if the window was maximized before going fullscreen */ + gboolean pre_fullscreen_max_horz, + pre_fullscreen_max_vert; + + /*! The window's strut + The strut defines areas of the screen that are marked off-bounds for + window placement. In theory, where this window exists. + */ + StrutPartial strut; + + /*! The logical size of the window + The "logical" size of the window is refers to the user's perception of + the size of the window, and is the value that should be displayed to the + user. For example, with xterms, this value it the number of characters + being displayed in the terminal, instead of the number of pixels. + */ + Size logical_size; + + /*! Width of the border on the window. + The window manager will set this to 0 while the window is being managed, + but needs to restore it afterwards, so it is saved here. + */ + gint border_width; + + /*! The minimum aspect ratio the client window can be sized to. + A value of 0 means this is ignored. + */ + gfloat min_ratio; + /*! The maximum aspect ratio the client window can be sized to. + A value of 0 means this is ignored. + */ + gfloat max_ratio; + + /*! The minimum size of the client window + If the min is > the max, then the window is not resizable + */ + Size min_size; + /*! The maximum size of the client window + If the min is > the max, then the window is not resizable + */ + Size max_size; + /*! The size of increments to resize the client window by */ + Size size_inc; + /*! The base size of the client window + This value should be subtracted from the window's actual size when + displaying its size to the user, or working with its min/max size + */ + Size base_size; + + /*! Window decoration and functionality hints */ + ObMwmHints mwmhints; + + /*! The client's specified colormap */ + Colormap colormap; + + /*! Where to place the decorated window in relation to the undecorated + window */ + gint gravity; + + /*! The state of the window, one of WithdrawnState, IconicState, or + NormalState */ + glong wmstate; + + /*! True if the client supports the delete_window protocol */ + gboolean delete_window; + + /*! Was the window's position requested by the application or the user? + if by the application, we force it completely onscreen, if by the user + we only force it if it tries to go completely offscreen, if neither, we + should place the window ourselves when it first appears */ + guint positioned; + + /*! Was the window's size requested by the application or the user? + If by the application we don't let it go outside the available area */ + guint sized; + + /*! Can the window receive input focus? */ + gboolean can_focus; + /*! Notify the window when it receives focus? */ + gboolean focus_notify; + + /*! Will the client respond to pings? */ + gboolean ping; + /*! Indicates if the client is trying to close but has stopped responding + to pings */ + gboolean not_responding; + /*! A prompt shown when you are trying to close a client that is not + responding. It asks if you want to kill the client */ + struct _ObPrompt *kill_prompt; + /*! We tried to close the window with a SIGTERM */ + gint kill_level; + +#ifdef SYNC + /*! The client wants to sync during resizes */ + gboolean sync_request; + /*! The XSync counter used for synchronizing during resizes */ + guint32 sync_counter; + /*! The value we're waiting for the counter to reach */ + gulong sync_counter_value; +#endif + + /*! The window uses shape extension to be non-rectangular? */ + gboolean shaped; + /*! The window uses shape extension to have non-rectangular input? */ + gboolean shaped_input; + + /*! The window is modal, so it must be processed before any windows it is + related to can be focused */ + gboolean modal; + /*! Only the window's titlebar is displayed */ + gboolean shaded; + /*! The window is iconified */ + gboolean iconic; + /*! The window is maximized to fill the screen vertically */ + gboolean max_vert; + /*! The window is maximized to fill the screen horizontally */ + gboolean max_horz; + /*! The window should not be displayed by pagers */ + gboolean skip_pager; + /*! The window should not be displayed by taskbars */ + gboolean skip_taskbar; + /*! The window is a 'fullscreen' window, and should be on top of all + others */ + gboolean fullscreen; + /*! The window should be on top of other windows of the same type. + above takes priority over below. */ + gboolean above; + /*! The window should be underneath other windows of the same type. + above takes priority over below. */ + gboolean below; + /*! Demands attention flag */ + gboolean demands_attention; + + /*! The urgent flag */ + gboolean urgent; + + /*! The layer in which the window will be stacked, windows in lower layers + are always below windows in higher layers. */ + ObStackingLayer layer; + + /*! A bitmask of values in the ObFrameDecorations enum + The values in the variable are the decorations that the client wants to + be displayed around it. + */ + guint decorations; + + /*! A user option. When this is set to TRUE the client will not ever + be decorated. + */ + gboolean undecorated; + + /*! A bitmask of values in the ObFunctions enum + The values in the variable specify the ways in which the user is allowed + to modify this window. + */ + guint functions; + + /* The window's icon, in a variety of shapes and sizes */ + RrImage *icon_set; + + /*! Where the window should iconify to/from */ + Rect icon_geometry; + + /*! A boolean used for algorithms which need to mark clients as visited */ + gboolean visited; +}; + +extern GList *client_list; + +void client_startup(gboolean reconfig); +void client_shutdown(gboolean reconfig); + +typedef void (*ObClientCallback)(ObClient *client, gpointer data); + +/* Callback functions */ + +/*! Get notified when the client is unmanaged */ +void client_add_destroy_notify(ObClientCallback func, gpointer data); +void client_remove_destroy_notify(ObClientCallback func); + +/*! Manages a given window + @param prompt This specifies an ObPrompt which is being managed. It is + possible to manage Openbox-owned windows through this. +*/ +void client_manage(Window win, struct _ObPrompt *prompt); +/*! Unmanages all managed windows */ +void client_unmanage_all(void); +/*! Unmanages a given client */ +void client_unmanage(ObClient *client); + +/*! This manages a window only so far as is needed to get it's decorations. + This is used when you want to determine a window's decorations before it + is mapped. Call client_fake_unmanage() with the returned client when you + are done with it. */ +ObClient *client_fake_manage(Window win); +/*! Free the stuff created by client_fake_manage() */ +void client_fake_unmanage(ObClient *self); + +/*! Sets the client list on the root window from the client_list */ +void client_set_list(void); + +/*! Determines if the client should be shown or hidden currently. + @return TRUE if it should be visible; otherwise, FALSE. +*/ +gboolean client_should_show(ObClient *self); + +/*! Returns if the window should be treated as a normal window. + Some windows (desktops, docks, splash screens) have special rules applied + to them in a number of places regarding focus or user interaction. */ +gboolean client_normal(ObClient *self); + +/*! Returns if the window is one of an application's helper windows + (utilty, menu, etc) */ +gboolean client_helper(ObClient *self); + +/*! Return if the client is a type which should be given focus from mouse + presses on the *client* window. This doesn't affect clicking on the + decorations. This doesn't count for focus cycling, different rules apply to + that. */ +gboolean client_mouse_focusable(ObClient *self); + +/*! Return if the client is a type which should be given focus from the + mouse entering the window. This doesn't count for focus cycling, different + rules apply to that. */ +gboolean client_enter_focusable(ObClient *self); + +/* Returns if the window is focused */ +gboolean client_focused(ObClient *self); + +/*! When the client is resized but not moved, figure out the new position + for it based on its gravity: + http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512541 +*/ +void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww); + +/*! When the client is resized but not moved, figure out the new position + for it based on its gravity: + http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512541 +*/ +void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh); + +/*! Convert a position/size from a given gravity to the client's true gravity, + when the client is only resizing (the reference point doesn't move) + */ +void client_convert_gravity_resize(ObClient *self, gint gravity, + gint *x, gint *y, + gint w, gint h); + +#define client_move(self, x, y) \ + client_configure(self, x, y, self->area.width, self->area.height, TRUE, TRUE,\ + FALSE) +#define client_resize(self, w, h) \ + client_configure(self, self->area.x, self->area.y, w, h, TRUE, TRUE, FALSE) +#define client_move_resize(self, x, y, w, h) \ + client_configure(self, x, y, w, h, TRUE, TRUE, FALSE) +#define client_reconfigure(self, force) \ + client_configure(self, ((ObClient*)self)->area.x, ((ObClient*)self)->area.y, \ + ((ObClient*)self)->area.width, \ + ((ObClient*)self)->area.height, FALSE, TRUE, force) + +/*! Figure out where a window will end up and what size it will be if you + told it to move/resize to these coordinates. + + These values are what client_configure will give the window. + + @param x The x coordiante of the new position for the client. + @param y The y coordiante of the new position for the client. + @param w The width component of the new size for the client. + @param h The height component of the new size for the client. + @param logicalw Returns the width component of the new logical width. + This value is only returned when the new w or h calculated + differ from the ones passed in. + @param logicalh Returns the height component of the new logical height. + This value is only returned when the new w or h calculated + differ from the ones passed in. + @param user Specifies whether this is a user-requested change or a + program requested change. For program requested changes, the + constraints are not checked. +*/ +void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, + gint *logicalw, gint *logicalh, + gboolean user); + +/*! Move and/or resize the window. + This also maintains things like the client's minsize, and size increments. + @param x The x coordiante of the new position for the client. + @param y The y coordiante of the new position for the client. + @param w The width component of the new size for the client. + @param h The height component of the new size for the client. + @param user Specifies whether this is a user-requested change or a + program requested change. For program requested changes, the + constraints are not checked. + @param final If user is true, then this should specify if this is a final + configuration. e.g. Final should be FALSE if doing an + interactive move/resize, and then be TRUE for the last call + only. + @param force_reply Send a ConfigureNotify to the client regardless of if + the position/size changed. +*/ +void client_configure(ObClient *self, gint x, gint y, gint w, gint h, + gboolean user, gboolean final, gboolean force_reply); + +/*! Finds coordinates to keep a client on the screen. + @param self The client + @param x The x coord of the client, may be changed. + @param y The y coord of the client, may be changed. + @param w The width of the client. + @param w The height of the client. + @param rude Be rude about it. If false, it is only moved if it is entirely + not visible. If true, then make sure the window is inside the + struts if possible. + @return true if the client was moved to be on-screen; false if not. +*/ +gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, + gboolean rude); + +/*! Moves a client so that it is on screen if it is entirely out of the + viewable screen. + @param self The client to move + @param rude Be rude about it. If false, it is only moved if it is entirely + not visible. If true, then make sure the window is inside the + struts if possible. +*/ +void client_move_onscreen(ObClient *self, gboolean rude); + +/*! dir is either North, South, East or West. It can't be, for example, + Northwest */ +void client_find_edge_directional(ObClient *self, ObDirection dir, + gint my_head, gint my_tail, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge); +void client_find_move_directional(ObClient *self, ObDirection dir, + gint *x, gint *y); +void client_find_resize_directional(ObClient *self, ObDirection side, + gboolean grow, + gint *x, gint *y, gint *w, gint *h); + +/*! Fullscreen's or unfullscreen's the client window + @param fs true if the window should be made fullscreen; false if it should + be returned to normal state. +*/ +void client_fullscreen(ObClient *self, gboolean fs); + +/*! Determine if the window, using the given client-area, would be considered + as an "oldschool fullscreen" window, that is, if it is filling a whole + monitor. */ +gboolean client_is_oldfullscreen(const ObClient const *self, const Rect *area); + +/*! Iconifies or uniconifies the client window + @param iconic true if the window should be iconified; false if it should be + restored. + @param curdesk If iconic is FALSE, then this determines if the window will + be uniconified to the current viewable desktop (true) or to + its previous desktop (false) +*/ +void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, + gboolean hide_animation); + +/*! Maximize or unmaximize the client window + @param max true if the window should be maximized; false if it should be + returned to normal size. + @param dir 0 to set both horz and vert, 1 to set horz, 2 to set vert. +*/ +void client_maximize(ObClient *self, gboolean max, gint dir); + +/*! Shades or unshades the client window + @param shade true if the window should be shaded; false if it should be + unshaded. +*/ +void client_shade(ObClient *self, gboolean shade); + +/*! Set a client window to have decorations or not */ +void client_set_undecorated(ObClient *self, gboolean undecorated); + +/*! Hilite the window to make the user notice it */ +void client_hilite(ObClient *self, gboolean hilite); + +/*! Request the client to close its window */ +void client_close(ObClient *self); + +/*! Kill the client off violently */ +void client_kill(ObClient *self); + +/*! Sends the window to the specified desktop + @param donthide If TRUE, the window will not be shown/hidden after its + desktop has been changed. Generally this should be FALSE. + @param dontraise If TRUE, the window will not be raised. Generally this should + be FALSE. +*/ +void client_set_desktop(ObClient *self, guint target, gboolean donthide, + gboolean dontraise); + +/*! Show the client if it should be shown. Returns if the window is shown. */ +gboolean client_show(ObClient *self); + +/*! Show the client if it should be shown. Returns if the window is hidden. */ +gboolean client_hide(ObClient *self); + +/*! Show the client if it should be shown, and hide it if it should be + hidden. This is for example, when switching desktops. +*/ +void client_showhide(ObClient *self); + +/*! Validate client, by making sure no Destroy or Unmap events exist in + the event queue for the window. + @return true if the client is valid; false if the client has already + been unmapped/destroyed, and so is invalid. +*/ +gboolean client_validate(ObClient *self); + +/*! Sets the wm_state to the specified value */ +void client_set_wm_state(ObClient *self, glong state); + +/*! Adjusts the window's net_state + This should not be called as part of the window mapping process! It is for + use when updating the state post-mapping.<br> + client_apply_startup_state is used to do the same things during the mapping + process. +*/ +void client_set_state(ObClient *self, Atom action, glong data1, glong data2); + +/* Given a ObClient, find the client that focus would actually be sent to if + you wanted to give focus to the specified ObClient. Will return the same + ObClient passed to it or another ObClient if appropriate. */ +ObClient *client_focus_target(ObClient *self); + +/*! Returns what client_focus would return if passed the same client, but + without focusing it or modifying the focus order lists. */ +gboolean client_can_focus(ObClient *self); + +/*! Attempt to focus the client window */ +gboolean client_focus(ObClient *self); + +/*! Activates the client for use, focusing, uniconifying it, etc. To be used + when the user deliberately selects a window for use. + @param desktop If true, and the window is on another desktop, it will still + be activated. + @param here If true, and the window is on another desktop, it will be moved + to the current desktop, otherwise the desktop will switch to + where the window is. + @param raise If true, the client is brought to the front. + @param unshade If true, the client is unshaded (if it is shaded) + @param user If true, then a user action is what requested the activation; + otherwise, it means an application requested it on its own +*/ +void client_activate(ObClient *self, gboolean desktop, gboolean here, + gboolean raise, gboolean unshade, gboolean user); + +/*! Bring all of its helper windows to its desktop. These are the utility and + stuff windows. */ +void client_bring_helper_windows(ObClient *self); + +/*! Bring all of its modal windows to its desktop. */ +void client_bring_modal_windows(ObClient *self); + +/*! Calculates the stacking layer for the client window */ +void client_calc_layer(ObClient *self); + +/*! Updates the window's transient status, and any parents of it */ +void client_update_transient_for(ObClient *self); +/*! Update the protocols that the window supports and adjusts things if they + change */ +void client_update_protocols(ObClient *self); +#ifdef SYNC +/*! Updates the window's sync request counter for resizes */ +void client_update_sync_request_counter(ObClient *self); +#endif +/*! Updates the window's colormap */ +void client_update_colormap(ObClient *self, Colormap colormap); +/*! Updates the requested opacity for the window from the client. */ +void client_update_opacity(ObClient *self); +/*! Updates the WMNormalHints and adjusts things if they change */ +void client_update_normal_hints(ObClient *self); + +/*! Updates the WMHints and adjusts things if they change + @param initstate Whether to read the initial_state property from the + WMHints. This should only be used during the mapping + process. +*/ +void client_update_wmhints(ObClient *self); +/*! Updates the window's title and icon title */ +void client_update_title(ObClient *self); +/*! Updates the strut for the client */ +void client_update_strut(ObClient *self); +/*! Updates the window's icons */ +void client_update_icons(ObClient *self); +/*! Updates the window's icon geometry (where to iconify to/from) */ +void client_update_icon_geometry(ObClient *self); + +/*! Set up what decor should be shown on the window and what functions should + be allowed (ObClient::decorations and ObClient::functions). + This also updates the NET_WM_ALLOWED_ACTIONS hint. + @param reconfig When TRUE, the window will be reconfigured to show the + changes +*/ +void client_setup_decor_and_functions(ObClient *self, gboolean reconfig); + +/*! Sets the window's type and transient flag */ +void client_get_type_and_transientness(ObClient *self); +/*! Gets the motif wm hints */ +void client_get_mwm_hints(ObClient *self); + +/*! Returns a client's icon set, or its parents (recursively) if it doesn't + have one +*/ +RrImage* client_icon(ObClient *self); + +/*! Return TRUE if the client is transient for some other window. Return + FALSE if it's not transient or there is no window for it to be + transient for */ +gboolean client_has_parent(ObClient *self); + +/*! Return TRUE if the client has some transient children, and FALSE otherwise. +*/ +gboolean client_has_children(ObClient *self); + +/*! Searches a client's immediate parents for a focused window. The function + does not check for the passed client, only for *ONE LEVEL* of its parents. + If no focused parent is found, NULL is returned. +*/ +ObClient *client_search_focus_parent(ObClient *self); + +/*! Searches a client's parents for a focused window. The function + does not check for the passed client, but searches through all of its + parents. If no focused parent is found, NULL is returned. +*/ +ObClient *client_search_focus_parent_full(ObClient *self); + +/*! Searches a client's transients for a focused window. The function does not + check for the passed client, only for its transients. + If no focused transient is found, NULL is returned. +*/ +ObClient *client_search_focus_tree(ObClient *self); + +/*! Searches a client's transient tree for a focused window. The function + searches up the tree and down other branches as well as the passed client's. + If no focused client is found, NULL is returned. +*/ +ObClient *client_search_focus_tree_full(ObClient *self); + +/*! Searches a client's group and each member's transients for a focused + window. This doesn't go up the window's transient tree at all. If no + focused client is found, NULL is returned. */ +ObClient *client_search_focus_group_full(ObClient *self); + +/*! Return a modal child of the client window that can be focused. + @return A modal child of the client window that can be focused, or 0 if + none was found. +*/ +ObClient *client_search_modal_child(ObClient *self); + +/*! Returns a list of top-level windows which this is a transient for. + It will only contain more than 1 element if the client is transient for its + group. +*/ +GSList *client_search_all_top_parents(ObClient *self); + +/*! Returns a list of top-level windows which this is a transient for, and + which are in the same layer as this client. + It will only contain more than 1 element if the client is transient for its + group. +*/ +GSList *client_search_all_top_parents_layer(ObClient *self); + +/*! Returns the client's parent when it is transient for a direct window + rather than a group. If it has no parents, or is transient for the + group, this returns null */ +ObClient *client_direct_parent(ObClient *self); + +/*! Returns a window's top level parent. This only counts direct parents, + not groups if it is transient for its group. +*/ +ObClient *client_search_top_direct_parent(ObClient *self); + +/*! Is one client a direct child of another (i.e. not through the group.) + This checks more than one level, so there may be another direct child in + between */ +gboolean client_is_direct_child(ObClient *parent, ObClient *child); + +/*! Search for a parent of a client. This only searches up *ONE LEVEL*, and + returns the searched for parent if it is a parent, or NULL if not. */ +ObClient *client_search_parent(ObClient *self, ObClient *search); + +/*! Search for a transient of a client. The transient is returned if it is one, + NULL is returned if the given search is not a transient of the client. */ +ObClient *client_search_transient(ObClient *self, ObClient *search); + +/*! Set a client window to be above/below other clients. + @layer < 0 indicates the client should be placed below other clients.<br /> + = 0 indicates the client should be placed with other clients.<br /> + > 0 indicates the client should be placed above other clients. +*/ +void client_set_layer(ObClient *self, gint layer); + +guint client_monitor(ObClient *self); + +ObClient* client_under_pointer(void); + +gboolean client_has_group_siblings(ObClient *self); + +/*! Returns TRUE if the client has a transient child, a parent, or a + group member. Returns FALSE otherwise. +*/ +gboolean client_has_relative(ObClient *self); + +/*! Returns TRUE if the client is running on the same machine as Openbox */ +gboolean client_on_localhost(ObClient *self); + +#endif diff --git a/openbox/client_list_combined_menu.c b/openbox/client_list_combined_menu.c new file mode 100644 index 0000000..c26b6fa --- /dev/null +++ b/openbox/client_list_combined_menu.c @@ -0,0 +1,167 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_list_menu.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "openbox.h" +#include "menu.h" +#include "menuframe.h" +#include "screen.h" +#include "client.h" +#include "client_list_combined_menu.h" +#include "focus.h" +#include "config.h" +#include "gettext.h" + +#include <glib.h> + +#define MENU_NAME "client-list-combined-menu" + +static ObMenu *combined_menu; + +#define SEPARATOR -1 +#define ADD_DESKTOP -2 +#define REMOVE_DESKTOP -3 + +static void self_cleanup(ObMenu *menu, gpointer data) +{ + menu_clear_entries(menu); +} + +static gboolean self_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + ObMenuEntry *e; + GList *it; + guint desktop; + + menu_clear_entries(menu); + + for (desktop = 0; desktop < screen_num_desktops; desktop++) { + gboolean empty = TRUE; + gboolean onlyiconic = TRUE; + + menu_add_separator(menu, SEPARATOR, screen_desktop_names[desktop]); + for (it = focus_order; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (focus_valid_target(c, desktop, + TRUE, TRUE, + FALSE, TRUE, FALSE, FALSE, FALSE)) + { + empty = FALSE; + + if (c->iconic) { + gchar *title = g_strdup_printf("(%s)", c->icon_title); + e = menu_add_normal(menu, desktop, title, NULL, FALSE); + g_free(title); + } else { + onlyiconic = FALSE; + e = menu_add_normal(menu, desktop, c->title, NULL, FALSE); + } + + if (config_menu_show_icons) { + e->data.normal.icon = client_icon(c); + RrImageRef(e->data.normal.icon); + e->data.normal.icon_alpha = + c->iconic ? OB_ICONIC_ALPHA : 0xff; + } + + e->data.normal.data = c; + } + } + + if (empty || onlyiconic) { + /* no entries or only iconified windows, so add a + * way to go to this desktop without uniconifying a window */ + if (!empty) + menu_add_separator(menu, SEPARATOR, NULL); + + e = menu_add_normal(menu, desktop, _("Go there..."), NULL, TRUE); + if (desktop == screen_desktop) + e->data.normal.enabled = FALSE; + } + } + + if (config_menu_manage_desktops) { + menu_add_separator(menu, SEPARATOR, _("Manage desktops")); + menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE); + menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"), + NULL, TRUE); + } + + return TRUE; /* always show the menu */ +} + +static void menu_execute(ObMenuEntry *self, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + if (self->id == ADD_DESKTOP) { + screen_add_desktop(FALSE); + menu_frame_hide_all(); + } + else if (self->id == REMOVE_DESKTOP) { + screen_remove_desktop(FALSE); + menu_frame_hide_all(); + } + else { + ObClient *t = self->data.normal.data; + if (t) { /* it's set to NULL if its destroyed */ + gboolean here = state & ShiftMask; + + client_activate(t, TRUE, here, TRUE, TRUE, TRUE); + /* if the window is omnipresent then we need to go to its + desktop */ + if (!here && t->desktop == DESKTOP_ALL) + screen_set_desktop(self->id, FALSE); + } + else + screen_set_desktop(self->id, TRUE); + } +} + +static void client_dest(ObClient *client, gpointer data) +{ + /* This concise function removes all references to a closed + * client in the client_list_menu, so we don't have to check + * in client.c */ + GList *eit; + for (eit = combined_menu->entries; eit; eit = g_list_next(eit)) { + ObMenuEntry *meit = eit->data; + if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL && + meit->data.normal.data == client) + { + meit->data.normal.data = NULL; + } + } +} + +void client_list_combined_menu_startup(gboolean reconfig) +{ + if (!reconfig) + client_add_destroy_notify(client_dest, NULL); + + combined_menu = menu_new(MENU_NAME, _("Windows"), TRUE, NULL); + menu_set_update_func(combined_menu, self_update); + menu_set_cleanup_func(combined_menu, self_cleanup); + menu_set_execute_func(combined_menu, menu_execute); +} + +void client_list_combined_menu_shutdown(gboolean reconfig) +{ + if (!reconfig) + client_remove_destroy_notify(client_dest); +} diff --git a/openbox/client_list_combined_menu.h b/openbox/client_list_combined_menu.h new file mode 100644 index 0000000..420e898 --- /dev/null +++ b/openbox/client_list_combined_menu.h @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_list_menu.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__client_list_combined_menu_h +#define ob__client_list_combined_menu_h + +void client_list_combined_menu_startup(gboolean reconfig); +void client_list_combined_menu_shutdown(gboolean reconfig); + +#endif diff --git a/openbox/client_list_menu.c b/openbox/client_list_menu.c new file mode 100644 index 0000000..f3df2a5 --- /dev/null +++ b/openbox/client_list_menu.c @@ -0,0 +1,224 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_list_menu.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "openbox.h" +#include "menu.h" +#include "menuframe.h" +#include "screen.h" +#include "client.h" +#include "client_list_menu.h" +#include "focus.h" +#include "config.h" +#include "gettext.h" + +#include <glib.h> + +#define MENU_NAME "client-list-menu" + +static GSList *desktop_menus; + +typedef struct +{ + guint desktop; +} DesktopData; + +#define SEPARATOR -1 +#define ADD_DESKTOP -2 +#define REMOVE_DESKTOP -3 + +static gboolean desk_menu_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + DesktopData *d = data; + GList *it; + gboolean empty = TRUE; + gboolean onlyiconic = TRUE; + + menu_clear_entries(menu); + + for (it = focus_order; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (focus_valid_target(c, d->desktop, + TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE)) { + ObMenuEntry *e; + + empty = FALSE; + + if (c->iconic) { + gchar *title = g_strdup_printf("(%s)", c->icon_title); + e = menu_add_normal(menu, d->desktop, title, NULL, FALSE); + g_free(title); + } else { + onlyiconic = FALSE; + e = menu_add_normal(menu, d->desktop, c->title, NULL, FALSE); + } + + if (config_menu_show_icons) { + e->data.normal.icon = client_icon(c); + RrImageRef(e->data.normal.icon); + e->data.normal.icon_alpha = c->iconic ? OB_ICONIC_ALPHA : 0xff; + } + + e->data.normal.data = c; + } + } + + if (empty || onlyiconic) { + ObMenuEntry *e; + + /* no entries or only iconified windows, so add a + * way to go to this desktop without uniconifying a window */ + if (!empty) + menu_add_separator(menu, SEPARATOR, NULL); + + e = menu_add_normal(menu, d->desktop, _("Go there..."), NULL, TRUE); + if (d->desktop == screen_desktop) + e->data.normal.enabled = FALSE; + } + + return TRUE; /* always show */ +} + +static void desk_menu_execute(ObMenuEntry *self, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + ObClient *t = self->data.normal.data; + if (t) { /* it's set to NULL if its destroyed */ + gboolean here = state & ShiftMask; + + client_activate(t, TRUE, here, TRUE, TRUE, TRUE); + /* if the window is omnipresent then we need to go to its + desktop */ + if (!here && t->desktop == DESKTOP_ALL) + screen_set_desktop(self->id, FALSE); + } + else + screen_set_desktop(self->id, TRUE); +} + +static void desk_menu_destroy(ObMenu *menu, gpointer data) +{ + DesktopData *d = data; + + g_slice_free(DesktopData, d); + + desktop_menus = g_slist_remove(desktop_menus, menu); +} + +static void self_cleanup(ObMenu *menu, gpointer data) +{ + menu_clear_entries(menu); + + while (desktop_menus) { + menu_free(desktop_menus->data); + desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus); + } +} + +static gboolean self_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + guint i; + + menu_clear_entries(menu); + + while (desktop_menus) { + menu_free(desktop_menus->data); + desktop_menus = g_slist_delete_link(desktop_menus, desktop_menus); + } + + for (i = 0; i < screen_num_desktops; ++i) { + ObMenu *submenu; + gchar *name = g_strdup_printf("%s-%u", MENU_NAME, i); + DesktopData *ddata = g_slice_new(DesktopData); + + ddata->desktop = i; + submenu = menu_new(name, screen_desktop_names[i], FALSE, ddata); + menu_set_update_func(submenu, desk_menu_update); + menu_set_execute_func(submenu, desk_menu_execute); + menu_set_destroy_func(submenu, desk_menu_destroy); + + menu_add_submenu(menu, i, name); + + g_free(name); + + desktop_menus = g_slist_append(desktop_menus, submenu); + } + + if (config_menu_manage_desktops) { + menu_add_separator(menu, SEPARATOR, NULL); + menu_add_normal(menu, ADD_DESKTOP, _("_Add new desktop"), NULL, TRUE); + menu_add_normal(menu, REMOVE_DESKTOP, _("_Remove last desktop"), + NULL, TRUE); + } + + return TRUE; /* always show */ +} + +static void self_execute(ObMenuEntry *self, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + if (self->id == ADD_DESKTOP) { + screen_add_desktop(FALSE); + menu_frame_hide_all(); + } + else if (self->id == REMOVE_DESKTOP) { + screen_remove_desktop(FALSE); + menu_frame_hide_all(); + } +} + +static void client_dest(ObClient *client, gpointer data) +{ + /* This concise function removes all references to a closed + * client in the client_list_menu, so we don't have to check + * in client.c */ + GSList *it; + for (it = desktop_menus; it; it = g_slist_next(it)) { + ObMenu *mit = it->data; + GList *eit; + for (eit = mit->entries; eit; eit = g_list_next(eit)) { + ObMenuEntry *meit = eit->data; + if (meit->type == OB_MENU_ENTRY_TYPE_NORMAL && + meit->data.normal.data == client) + { + meit->data.normal.data = NULL; + } + } + } +} + +void client_list_menu_startup(gboolean reconfig) +{ + ObMenu *menu; + + if (!reconfig) + client_add_destroy_notify(client_dest, NULL); + + menu = menu_new(MENU_NAME, _("Desktops"), TRUE, NULL); + menu_set_update_func(menu, self_update); + menu_set_cleanup_func(menu, self_cleanup); + menu_set_execute_func(menu, self_execute); +} + +void client_list_menu_shutdown(gboolean reconfig) +{ + if (!reconfig) + client_remove_destroy_notify(client_dest); +} diff --git a/openbox/client_list_menu.h b/openbox/client_list_menu.h new file mode 100644 index 0000000..860cd50 --- /dev/null +++ b/openbox/client_list_menu.h @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_list_menu.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__client_list_menu_h +#define ob__client_list_menu_h + +void client_list_menu_startup(gboolean reconfig); +void client_list_menu_shutdown(gboolean reconfig); + +#endif diff --git a/openbox/client_menu.c b/openbox/client_menu.c new file mode 100644 index 0000000..c6cdd63 --- /dev/null +++ b/openbox/client_menu.c @@ -0,0 +1,421 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_menu.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "menu.h" +#include "menuframe.h" +#include "config.h" +#include "screen.h" +#include "client.h" +#include "client_menu.h" +#include "openbox.h" +#include "frame.h" +#include "moveresize.h" +#include "event.h" +#include "gettext.h" +#include "obt/prop.h" + +#include <glib.h> + +#define CLIENT_MENU_NAME "client-menu" +#define SEND_TO_MENU_NAME "client-send-to-menu" +#define LAYER_MENU_NAME "client-layer-menu" + +enum { + LAYER_TOP = 1, + LAYER_NORMAL = 0, + LAYER_BOTTOM = -1 +}; + +enum { + CLIENT_SEND_TO, + CLIENT_LAYER, + CLIENT_ICONIFY, + CLIENT_RESTORE, + CLIENT_MAXIMIZE, + CLIENT_SHADE, + CLIENT_DECORATE, + CLIENT_MOVE, + CLIENT_RESIZE, + CLIENT_CLOSE +}; + +static void set_icon_color(ObMenuEntry *e) +{ + e->data.normal.mask_normal_color = ob_rr_theme->menu_color; + e->data.normal.mask_selected_color = ob_rr_theme->menu_selected_color; + e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color; + e->data.normal.mask_disabled_selected_color = + ob_rr_theme->menu_disabled_selected_color; +} + +static gboolean client_menu_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + GList *it; + + if (frame->client == NULL || !client_normal(frame->client)) + return FALSE; /* don't show the menu */ + + for (it = menu->entries; it; it = g_list_next(it)) { + ObMenuEntry *e = it->data; + gboolean *en = &e->data.normal.enabled; /* save some typing */ + ObClient *c = frame->client; + + if (e->type == OB_MENU_ENTRY_TYPE_NORMAL) { + switch (e->id) { + case CLIENT_ICONIFY: + *en = c->functions & OB_CLIENT_FUNC_ICONIFY; + break; + case CLIENT_RESTORE: + *en = c->max_horz || c->max_vert; + break; + case CLIENT_MAXIMIZE: + *en = ((c->functions & OB_CLIENT_FUNC_MAXIMIZE) && + (!c->max_horz || !c->max_vert)); + break; + case CLIENT_SHADE: + *en = c->functions & OB_CLIENT_FUNC_SHADE; + break; + case CLIENT_MOVE: + *en = c->functions & OB_CLIENT_FUNC_MOVE; + break; + case CLIENT_RESIZE: + *en = c->functions & OB_CLIENT_FUNC_RESIZE; + break; + case CLIENT_CLOSE: + *en = c->functions & OB_CLIENT_FUNC_CLOSE; + break; + case CLIENT_DECORATE: + *en = c->functions & OB_CLIENT_FUNC_UNDECORATE; + break; + default: + *en = TRUE; + } + } + } + return TRUE; /* show the menu */ +} + +static void client_menu_execute(ObMenuEntry *e, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + gint x, y; + gulong ignore_start; + + if (!c) + return; + + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + + switch (e->id) { + case CLIENT_ICONIFY: + /* the client won't be on screen anymore so hide the menu */ + menu_frame_hide_all(); + f = NULL; /* and don't update */ + + client_iconify(c, TRUE, FALSE, FALSE); + break; + case CLIENT_RESTORE: + client_maximize(c, FALSE, 0); + break; + case CLIENT_MAXIMIZE: + client_maximize(c, TRUE, 0); + break; + case CLIENT_SHADE: + client_shade(c, !c->shaded); + break; + case CLIENT_DECORATE: + client_set_undecorated(c, !c->undecorated); + break; + case CLIENT_MOVE: + /* this needs to grab the keyboard so hide the menu */ + menu_frame_hide_all(); + f = NULL; /* and don't update */ + + screen_pointer_pos(&x, &y); + moveresize_start(c, x, y, 0, + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD)); + break; + case CLIENT_RESIZE: + /* this needs to grab the keyboard so hide the menu */ + menu_frame_hide_all(); + f = NULL; /* and don't update */ + + screen_pointer_pos(&x, &y); + moveresize_start(c, x, y, 0, + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)); + break; + case CLIENT_CLOSE: + client_close(c); + break; + default: + g_assert_not_reached(); + } + + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + + /* update the menu cuz stuff can have changed */ + if (f) { + client_menu_update(f, NULL); + menu_frame_render(f); + } +} + +static gboolean layer_menu_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + GList *it; + + if (frame->client == NULL || !client_normal(frame->client)) + return FALSE; /* don't show the menu */ + + for (it = menu->entries; it; it = g_list_next(it)) { + ObMenuEntry *e = it->data; + gboolean *en = &e->data.normal.enabled; /* save some typing */ + ObClient *c = frame->client; + + if (e->type == OB_MENU_ENTRY_TYPE_NORMAL) { + switch (e->id) { + case LAYER_TOP: + *en = !c->above && (c->functions & OB_CLIENT_FUNC_ABOVE); + break; + case LAYER_NORMAL: + *en = c->above || c->below; + break; + case LAYER_BOTTOM: + *en = !c->below && (c->functions & OB_CLIENT_FUNC_BELOW); + break; + default: + *en = TRUE; + } + } + } + return TRUE; /* show the menu */ +} + +static void layer_menu_execute(ObMenuEntry *e, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + gulong ignore_start; + + g_assert(c); + + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + + client_set_layer(c, e->id); + + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + + /* update the menu cuz stuff can have changed */ + if (f) { + layer_menu_update(f, NULL); + menu_frame_render(f); + } +} + +static gboolean send_to_menu_update(ObMenuFrame *frame, gpointer data) +{ + ObMenu *menu = frame->menu; + ObClient *c = frame->client; + guint i; + ObMenuEntry *e; + GList *it; + + if (c == NULL || !client_normal(c)) + return FALSE; /* don't show the menu */ + + if (!data) + menu_clear_entries(menu); + + if (!menu->entries) { + for (i = 0; i <= screen_num_desktops; ++i) { + const gchar *name; + guint desk; + + if (i == screen_num_desktops) { + menu_add_separator(menu, -1, NULL); + + desk = DESKTOP_ALL; + name = _("All desktops"); + } else { + desk = i; + name = screen_desktop_names[i]; + } + + e = menu_add_normal(menu, desk, name, NULL, FALSE); + e->id = desk; + } + } + + for (it = menu->entries; it; it = g_list_next(it)) { + ObMenuEntry *e = it->data; + guint desk = e->id; + + e->data.normal.enabled = c->desktop != desk; + + if ((desk == DESKTOP_ALL && c->desktop != DESKTOP_ALL) || + (c->desktop == DESKTOP_ALL && desk == screen_desktop)) + { + e->data.normal.mask = ob_rr_theme->btn_desk->mask; + set_icon_color(e); + } else + e->data.normal.mask = NULL; + } + + return TRUE; /* show the menu */ +} + +static void send_to_menu_execute(ObMenuEntry *e, ObMenuFrame *f, + ObClient *c, guint state, gpointer data) +{ + g_assert(c); + + client_set_desktop(c, e->id, FALSE, FALSE); + if (f && c->desktop != screen_desktop && c->desktop != DESKTOP_ALL) + /* the client won't even be on the screen anymore, so hide the menu */ + menu_frame_hide_all(); + else if (f) { + send_to_menu_update(f, (gpointer)1); + menu_frame_render(f); + } +} + +static void client_menu_place(ObMenuFrame *frame, gint *x, gint *y, + gboolean mouse, gpointer data) +{ + gint dx, dy; + + if (!mouse && frame->client) { + *x = frame->client->frame->area.x; + + /* try below the titlebar */ + *y = frame->client->frame->area.y + frame->client->frame->size.top - + frame->client->frame->bwidth; + menu_frame_move_on_screen(frame, *x, *y, &dx, &dy); + if (dy != 0) { + /* try above the titlebar */ + *y = frame->client->frame->area.y + frame->client->frame->bwidth - + frame->area.height; + menu_frame_move_on_screen(frame, *x, *y, &dx, &dy); + } + if (dy != 0) { + /* didnt fit either way, use move on screen's values */ + *y = frame->client->frame->area.y + frame->client->frame->size.top; + menu_frame_move_on_screen(frame, *x, *y, &dx, &dy); + } + + *x += dx; + *y += dy; + } else { + gint myx, myy; + + myx = *x; + myy = *y; + + /* try to the bottom right of the cursor */ + menu_frame_move_on_screen(frame, myx, myy, &dx, &dy); + if (dx != 0 || dy != 0) { + /* try to the bottom left of the cursor */ + myx = *x - frame->area.width; + myy = *y; + menu_frame_move_on_screen(frame, myx, myy, &dx, &dy); + } + if (dx != 0 || dy != 0) { + /* try to the top right of the cursor */ + myx = *x; + myy = *y - frame->area.height; + menu_frame_move_on_screen(frame, myx, myy, &dx, &dy); + } + if (dx != 0 || dy != 0) { + /* try to the top left of the cursor */ + myx = *x - frame->area.width; + myy = *y - frame->area.height; + menu_frame_move_on_screen(frame, myx, myy, &dx, &dy); + } + if (dx != 0 || dy != 0) { + /* if didnt fit on either side so just use what it says */ + myx = *x; + myy = *y; + menu_frame_move_on_screen(frame, myx, myy, &dx, &dy); + } + *x = myx + dx; + *y = myy + dy; + } +} + +void client_menu_startup(void) +{ + ObMenu *menu; + ObMenuEntry *e; + + menu = menu_new(LAYER_MENU_NAME, _("_Layer"), TRUE, NULL); + menu_show_all_shortcuts(menu, TRUE); + menu_set_update_func(menu, layer_menu_update); + menu_set_execute_func(menu, layer_menu_execute); + + menu_add_normal(menu, LAYER_TOP, _("Always on _top"), NULL, TRUE); + menu_add_normal(menu, LAYER_NORMAL, _("_Normal"), NULL, TRUE); + menu_add_normal(menu, LAYER_BOTTOM, _("Always on _bottom"),NULL, TRUE); + + menu = menu_new(SEND_TO_MENU_NAME, _("_Send to desktop"), TRUE, NULL); + menu_set_update_func(menu, send_to_menu_update); + menu_set_execute_func(menu, send_to_menu_execute); + + menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), TRUE, NULL); + menu_show_all_shortcuts(menu, TRUE); + menu_set_update_func(menu, client_menu_update); + menu_set_place_func(menu, client_menu_place); + menu_set_execute_func(menu, client_menu_execute); + + menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME); + + menu_add_submenu(menu, CLIENT_LAYER, LAYER_MENU_NAME); + + e = menu_add_normal(menu, CLIENT_RESTORE, _("R_estore"), NULL, TRUE); + e->data.normal.mask = ob_rr_theme->btn_max->toggled_mask; + set_icon_color(e); + + menu_add_normal(menu, CLIENT_MOVE, _("_Move"), NULL, TRUE); + + menu_add_normal(menu, CLIENT_RESIZE, _("Resi_ze"), NULL, TRUE); + + e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico_nify"), NULL, TRUE); + e->data.normal.mask = ob_rr_theme->btn_iconify->mask; + set_icon_color(e); + + e = menu_add_normal(menu, CLIENT_MAXIMIZE, _("Ma_ximize"), NULL, TRUE); + e->data.normal.mask = ob_rr_theme->btn_max->mask; + set_icon_color(e); + + e = menu_add_normal(menu, CLIENT_SHADE, _("_Roll up/down"), NULL, TRUE); + e->data.normal.mask = ob_rr_theme->btn_shade->mask; + set_icon_color(e); + + menu_add_normal(menu, CLIENT_DECORATE, _("Un/_Decorate"), NULL, TRUE); + + menu_add_separator(menu, -1, NULL); + + e = menu_add_normal(menu, CLIENT_CLOSE, _("_Close"), NULL, TRUE); + e->data.normal.mask = ob_rr_theme->btn_close->mask; + set_icon_color(e); +} diff --git a/openbox/client_menu.h b/openbox/client_menu.h new file mode 100644 index 0000000..5c55516 --- /dev/null +++ b/openbox/client_menu.h @@ -0,0 +1,24 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client_menu.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__client_menu_h +#define ob__client_menu_h + +void client_menu_startup(void); + +#endif diff --git a/openbox/config.c b/openbox/config.c new file mode 100644 index 0000000..0d9eb68 --- /dev/null +++ b/openbox/config.c @@ -0,0 +1,1142 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + config.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "config.h" +#include "keyboard.h" +#include "mouse.h" +#include "actions.h" +#include "translate.h" +#include "client.h" +#include "screen.h" +#include "openbox.h" +#include "gettext.h" +#include "obt/paths.h" + +gboolean config_focus_new; +gboolean config_focus_follow; +guint config_focus_delay; +gboolean config_focus_raise; +gboolean config_focus_last; +gboolean config_focus_under_mouse; +gboolean config_unfocus_leave; + +ObPlacePolicy config_place_policy; +gboolean config_place_center; +ObPlaceMonitor config_place_monitor; + +guint config_primary_monitor_index; +ObPlaceMonitor config_primary_monitor; + +StrutPartial config_margins; + +gchar *config_theme; +gboolean config_theme_keepborder; +guint config_theme_window_list_icon_size; + +gchar *config_title_layout; + +gboolean config_animate_iconify; + +RrFont *config_font_activewindow; +RrFont *config_font_inactivewindow; +RrFont *config_font_menuitem; +RrFont *config_font_menutitle; +RrFont *config_font_activeosd; +RrFont *config_font_inactiveosd; + +guint config_desktops_num; +GSList *config_desktops_names; +guint config_screen_firstdesk; +guint config_desktop_popup_time; + +gboolean config_resize_redraw; +gint config_resize_popup_show; +ObResizePopupPos config_resize_popup_pos; +GravityPoint config_resize_popup_fixed; + +ObStackingLayer config_dock_layer; +gboolean config_dock_floating; +gboolean config_dock_nostrut; +ObDirection config_dock_pos; +gint config_dock_x; +gint config_dock_y; +ObOrientation config_dock_orient; +gboolean config_dock_hide; +guint config_dock_hide_delay; +guint config_dock_show_delay; +guint config_dock_app_move_button; +guint config_dock_app_move_modifiers; + +guint config_keyboard_reset_keycode; +guint config_keyboard_reset_state; + +gint config_mouse_threshold; +gint config_mouse_dclicktime; +gint config_mouse_screenedgetime; +gboolean config_mouse_screenedgewarp; + +guint config_menu_hide_delay; +gboolean config_menu_middle; +guint config_submenu_show_delay; +guint config_submenu_hide_delay; +gboolean config_menu_manage_desktops; +gboolean config_menu_show_icons; + +GSList *config_menu_files; + +gint config_resist_win; +gint config_resist_edge; + +GSList *config_per_app_settings; + +ObAppSettings* config_create_app_settings(void) +{ + ObAppSettings *settings = g_slice_new0(ObAppSettings); + settings->type = -1; + settings->decor = -1; + settings->shade = -1; + settings->monitor_type = OB_PLACE_MONITOR_ANY; + settings->monitor = -1; + settings->focus = -1; + settings->desktop = 0; + settings->layer = -2; + settings->iconic = -1; + settings->skip_pager = -1; + settings->skip_taskbar = -1; + settings->fullscreen = -1; + settings->max_horz = -1; + settings->max_vert = -1; + return settings; +} + +#define copy_if(setting, default) \ + if (src->setting != default) dst->setting = src->setting +void config_app_settings_copy_non_defaults(const ObAppSettings *src, + ObAppSettings *dst) +{ + g_assert(src != NULL); + g_assert(dst != NULL); + + copy_if(type, (ObClientType)-1); + copy_if(decor, -1); + copy_if(shade, -1); + copy_if(monitor_type, OB_PLACE_MONITOR_ANY); + copy_if(monitor, -1); + copy_if(focus, -1); + copy_if(desktop, 0); + copy_if(layer, -2); + copy_if(iconic, -1); + copy_if(skip_pager, -1); + copy_if(skip_taskbar, -1); + copy_if(fullscreen, -1); + copy_if(max_horz, -1); + copy_if(max_vert, -1); + + if (src->pos_given) { + dst->pos_given = TRUE; + dst->pos_force = src->pos_force; + dst->position = src->position; + /* monitor is copied above */ + } +} + +void config_parse_relative_number(gchar *s, gint *num, gint *denom) +{ + *num = strtol(s, &s, 10); + + if (*s == '%') { + *denom = 100; + } else if (*s == '/') { + *denom = atoi(s+1); + } +} + +void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c) +{ + gchar *s = obt_xml_node_string(node); + if (!g_ascii_strcasecmp(s, "center")) + c->center = TRUE; + else { + gchar *ps = s; + if (s[0] == '-') + c->opposite = TRUE; + if (s[0] == '-' || s[0] == '+') + ps++; + config_parse_relative_number(ps, &c->pos, &c->denom); + } + g_free(s); +} + +/* + <applications> + <application name="aterm"> + <decor>false</decor> + </application> + <application name="Rhythmbox"> + <layer>above</layer> + <position> + <x>700</x> + <y>0</y> + <monitor>1</monitor> + </position> + .. there is a lot more settings available + </application> + </applications> +*/ + +/* Manages settings for individual applications. + Some notes: monitor is the screen number in a multi monitor + (Xinerama) setup (starting from 0), or mouse: the monitor the pointer + is on, active: the active monitor, primary: the primary monitor. + Layer can be three values, above (Always on top), below + (Always on bottom) and everything else (normal behaviour). + Positions can be an integer value or center, which will + center the window in the specified axis. Position is within + the monitor, so <position><x>center</x></position><monitor>2</monitor> + will center the window on the second monitor. +*/ +static void parse_per_app_settings(xmlNodePtr node, gpointer d) +{ + xmlNodePtr app = obt_xml_find_node(node->children, "application"); + gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL, + *type_str = NULL; + gboolean name_set, class_set, type_set, role_set, title_set; + ObClientType type; + gboolean x_pos_given; + + while (app) { + x_pos_given = FALSE; + + class_set = obt_xml_attr_string(app, "class", &class); + name_set = obt_xml_attr_string(app, "name", &name); + type_set = obt_xml_attr_string(app, "type", &type_str); + role_set = obt_xml_attr_string(app, "role", &role); + title_set = obt_xml_attr_string(app, "title", &title); + + /* validate the type tho */ + if (type_set) { + if (!g_ascii_strcasecmp(type_str, "normal")) + type = OB_CLIENT_TYPE_NORMAL; + else if (!g_ascii_strcasecmp(type_str, "dialog")) + type = OB_CLIENT_TYPE_DIALOG; + else if (!g_ascii_strcasecmp(type_str, "splash")) + type = OB_CLIENT_TYPE_SPLASH; + else if (!g_ascii_strcasecmp(type_str, "utility")) + type = OB_CLIENT_TYPE_UTILITY; + else if (!g_ascii_strcasecmp(type_str, "menu")) + type = OB_CLIENT_TYPE_MENU; + else if (!g_ascii_strcasecmp(type_str, "toolbar")) + type = OB_CLIENT_TYPE_TOOLBAR; + else if (!g_ascii_strcasecmp(type_str, "dock")) + type = OB_CLIENT_TYPE_DOCK; + else if (!g_ascii_strcasecmp(type_str, "desktop")) + type = OB_CLIENT_TYPE_DESKTOP; + else + type_set = FALSE; /* not valid! */ + } + + if (class_set || name_set || role_set || title_set || type_set) { + xmlNodePtr n, c; + ObAppSettings *settings = config_create_app_settings(); + + if (name_set) + settings->name = g_pattern_spec_new(name); + + if (class_set) + settings->class = g_pattern_spec_new(class); + + if (role_set) + settings->role = g_pattern_spec_new(role); + + if (title_set) + settings->title = g_pattern_spec_new(title); + + if (type_set) + settings->type = type; + + if ((n = obt_xml_find_node(app->children, "decor"))) + if (!obt_xml_node_contains(n, "default")) + settings->decor = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "shade"))) + if (!obt_xml_node_contains(n, "default")) + settings->shade = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "position"))) { + if ((c = obt_xml_find_node(n->children, "x"))) + if (!obt_xml_node_contains(c, "default")) { + config_parse_gravity_coord(c, &settings->position.x); + x_pos_given = TRUE; + } + + if (x_pos_given && (c = obt_xml_find_node(n->children, "y"))) + if (!obt_xml_node_contains(c, "default")) { + config_parse_gravity_coord(c, &settings->position.y); + settings->pos_given = TRUE; + } + + /* monitor can be set without setting x or y */ + if ((c = obt_xml_find_node(n->children, "monitor"))) + if (!obt_xml_node_contains(c, "default")) { + gchar *s = obt_xml_node_string(c); + if (!g_ascii_strcasecmp(s, "mouse")) + settings->monitor_type = + OB_PLACE_MONITOR_MOUSE; + else if (!g_ascii_strcasecmp(s, "active")) + settings->monitor_type = + OB_PLACE_MONITOR_ACTIVE; + else if (!g_ascii_strcasecmp(s, "primary")) + settings->monitor_type = + OB_PLACE_MONITOR_PRIMARY; + else + settings->monitor = obt_xml_node_int(c); + g_free(s); + } + + obt_xml_attr_bool(n, "force", &settings->pos_force); + } + + if ((n = obt_xml_find_node(app->children, "focus"))) + if (!obt_xml_node_contains(n, "default")) + settings->focus = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "desktop"))) { + if (!obt_xml_node_contains(n, "default")) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "all")) + settings->desktop = DESKTOP_ALL; + else { + gint i = obt_xml_node_int(n); + if (i > 0) + settings->desktop = i; + } + g_free(s); + } + } + + if ((n = obt_xml_find_node(app->children, "layer"))) + if (!obt_xml_node_contains(n, "default")) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "above")) + settings->layer = 1; + else if (!g_ascii_strcasecmp(s, "below")) + settings->layer = -1; + else + settings->layer = 0; + g_free(s); + } + + if ((n = obt_xml_find_node(app->children, "iconic"))) + if (!obt_xml_node_contains(n, "default")) + settings->iconic = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "skip_pager"))) + if (!obt_xml_node_contains(n, "default")) + settings->skip_pager = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "skip_taskbar"))) + if (!obt_xml_node_contains(n, "default")) + settings->skip_taskbar = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "fullscreen"))) + if (!obt_xml_node_contains(n, "default")) + settings->fullscreen = obt_xml_node_bool(n); + + if ((n = obt_xml_find_node(app->children, "maximized"))) + if (!obt_xml_node_contains(n, "default")) { + gchar *s = obt_xml_node_string(n); + if (!g_ascii_strcasecmp(s, "horizontal")) { + settings->max_horz = TRUE; + settings->max_vert = FALSE; + } else if (!g_ascii_strcasecmp(s, "vertical")) { + settings->max_horz = FALSE; + settings->max_vert = TRUE; + } else + settings->max_horz = settings->max_vert = + obt_xml_node_bool(n); + g_free(s); + } + + config_per_app_settings = g_slist_append(config_per_app_settings, + (gpointer) settings); + g_free(name); + g_free(class); + g_free(role); + g_free(title); + g_free(type_str); + name = class = role = title = type_str = NULL; + } + + app = obt_xml_find_node(app->next, "application"); + } +} + +/* + +<keybind key="C-x"> + <action name="ChangeDesktop"> + <desktop>3</desktop> + </action> +</keybind> + +*/ + +static void parse_key(xmlNodePtr node, GList *keylist) +{ + gchar *keystring, **keys, **key; + xmlNodePtr n; + gboolean is_chroot = FALSE; + + if (!obt_xml_attr_string(node, "key", &keystring)) + return; + + obt_xml_attr_bool(node, "chroot", &is_chroot); + + keys = g_strsplit(keystring, " ", 0); + for (key = keys; *key; ++key) { + keylist = g_list_append(keylist, *key); + + if ((n = obt_xml_find_node(node->children, "keybind"))) { + while (n) { + parse_key(n, keylist); + n = obt_xml_find_node(n->next, "keybind"); + } + } + else if ((n = obt_xml_find_node(node->children, "action"))) { + while (n) { + ObActionsAct *action; + + action = actions_parse(n); + if (action) + keyboard_bind(keylist, action); + n = obt_xml_find_node(n->next, "action"); + } + } + + + if (is_chroot) + keyboard_chroot(keylist); + keylist = g_list_delete_link(keylist, g_list_last(keylist)); + } + + g_strfreev(keys); + g_free(keystring); +} + +static void parse_keyboard(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + gchar *key; + + keyboard_unbind_all(); + + if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) { + key = obt_xml_node_string(n); + translate_key(key, &config_keyboard_reset_state, + &config_keyboard_reset_keycode); + g_free(key); + } + + if ((n = obt_xml_find_node(node->children, "keybind"))) + while (n) { + parse_key(n, NULL); + n = obt_xml_find_node(n->next, "keybind"); + } +} + +/* + +<context name="Titlebar"> + <mousebind button="Left" action="Press"> + <action name="Raise"></action> + </mousebind> +</context> + +*/ + +static void parse_mouse(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n, nbut, nact; + gchar *buttonstr; + gchar *cxstr; + ObMouseAction mact; + + mouse_unbind_all(); + + node = node->children; + + if ((n = obt_xml_find_node(node, "dragThreshold"))) + config_mouse_threshold = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "doubleClickTime"))) + config_mouse_dclicktime = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) { + config_mouse_screenedgetime = obt_xml_node_int(n); + /* minimum value of 25 for this property, when it is 1 and you hit the + edge it basically never stops */ + if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25) + config_mouse_screenedgetime = 25; + } + if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse"))) + config_mouse_screenedgewarp = obt_xml_node_bool(n); + + n = obt_xml_find_node(node, "context"); + while (n) { + gchar *modcxstr; + ObFrameContext cx; + + if (!obt_xml_attr_string(n, "name", &cxstr)) + goto next_n; + + modcxstr = g_strdup(cxstr); /* make a copy to mutilate */ + while (frame_next_context_from_string(modcxstr, &cx)) { + if (!cx) { + gchar *s = strchr(modcxstr, ' '); + if (s) { + *s = '\0'; + g_message(_("Invalid context \"%s\" in mouse binding"), + modcxstr); + *s = ' '; + } + continue; + } + + nbut = obt_xml_find_node(n->children, "mousebind"); + while (nbut) { + if (!obt_xml_attr_string(nbut, "button", &buttonstr)) + goto next_nbut; + if (obt_xml_attr_contains(nbut, "action", "press")) + mact = OB_MOUSE_ACTION_PRESS; + else if (obt_xml_attr_contains(nbut, "action", "release")) + mact = OB_MOUSE_ACTION_RELEASE; + else if (obt_xml_attr_contains(nbut, "action", "click")) + mact = OB_MOUSE_ACTION_CLICK; + else if (obt_xml_attr_contains(nbut, "action","doubleclick")) + mact = OB_MOUSE_ACTION_DOUBLE_CLICK; + else if (obt_xml_attr_contains(nbut, "action", "drag")) + mact = OB_MOUSE_ACTION_MOTION; + else + goto next_nbut; + + nact = obt_xml_find_node(nbut->children, "action"); + while (nact) { + ObActionsAct *action; + + if ((action = actions_parse(nact))) + mouse_bind(buttonstr, cx, mact, action); + nact = obt_xml_find_node(nact->next, "action"); + } + g_free(buttonstr); + next_nbut: + nbut = obt_xml_find_node(nbut->next, "mousebind"); + } + } + g_free(modcxstr); + g_free(cxstr); + next_n: + n = obt_xml_find_node(n->next, "context"); + } +} + +static void parse_focus(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "focusNew"))) + config_focus_new = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "followMouse"))) + config_focus_follow = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "focusDelay"))) + config_focus_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "raiseOnFocus"))) + config_focus_raise = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "focusLast"))) + config_focus_last = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "underMouse"))) + config_focus_under_mouse = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "unfocusOnLeave"))) + config_unfocus_leave = obt_xml_node_bool(n); +} + +static void parse_placement(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "policy"))) + if (obt_xml_node_contains(n, "UnderMouse")) + config_place_policy = OB_PLACE_POLICY_MOUSE; + if ((n = obt_xml_find_node(node, "center"))) + config_place_center = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "monitor"))) { + if (obt_xml_node_contains(n, "active")) + config_place_monitor = OB_PLACE_MONITOR_ACTIVE; + else if (obt_xml_node_contains(n, "mouse")) + config_place_monitor = OB_PLACE_MONITOR_MOUSE; + else if (obt_xml_node_contains(n, "any")) + config_place_monitor = OB_PLACE_MONITOR_ANY; + } + if ((n = obt_xml_find_node(node, "primaryMonitor"))) { + config_primary_monitor_index = obt_xml_node_int(n); + if (!config_primary_monitor_index) { + if (obt_xml_node_contains(n, "mouse")) + config_primary_monitor = OB_PLACE_MONITOR_MOUSE; + } + } +} + +static void parse_margins(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "top"))) + config_margins.top = MAX(0, obt_xml_node_int(n)); + if ((n = obt_xml_find_node(node, "left"))) + config_margins.left = MAX(0, obt_xml_node_int(n)); + if ((n = obt_xml_find_node(node, "right"))) + config_margins.right = MAX(0, obt_xml_node_int(n)); + if ((n = obt_xml_find_node(node, "bottom"))) + config_margins.bottom = MAX(0, obt_xml_node_int(n)); +} + +static void parse_theme(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "name"))) { + gchar *c; + + g_free(config_theme); + c = obt_xml_node_string(n); + config_theme = obt_paths_expand_tilde(c); + g_free(c); + } + if ((n = obt_xml_find_node(node, "titleLayout"))) { + gchar *c, *d; + + g_free(config_title_layout); + config_title_layout = obt_xml_node_string(n); + + /* replace duplicates with spaces */ + for (c = config_title_layout; *c != '\0'; ++c) + for (d = c+1; *d != '\0'; ++d) + if (*c == *d) *d = ' '; + } + if ((n = obt_xml_find_node(node, "keepBorder"))) + config_theme_keepborder = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "animateIconify"))) + config_animate_iconify = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "windowListIconSize"))) { + config_theme_window_list_icon_size = obt_xml_node_int(n); + if (config_theme_window_list_icon_size < 16) + config_theme_window_list_icon_size = 16; + else if (config_theme_window_list_icon_size > 96) + config_theme_window_list_icon_size = 96; + } + + n = obt_xml_find_node(node, "font"); + while (n) { + xmlNodePtr fnode; + RrFont **font; + gchar *name = g_strdup(RrDefaultFontFamily); + gint size = RrDefaultFontSize; + RrFontWeight weight = RrDefaultFontWeight; + RrFontSlant slant = RrDefaultFontSlant; + + if (obt_xml_attr_contains(n, "place", "ActiveWindow")) + font = &config_font_activewindow; + else if (obt_xml_attr_contains(n, "place", "InactiveWindow")) + font = &config_font_inactivewindow; + else if (obt_xml_attr_contains(n, "place", "MenuHeader")) + font = &config_font_menutitle; + else if (obt_xml_attr_contains(n, "place", "MenuItem")) + font = &config_font_menuitem; + else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay")) + font = &config_font_activeosd; + else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay")) + font = &config_font_activeosd; + else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay")) + font = &config_font_inactiveosd; + else + goto next_font; + + if ((fnode = obt_xml_find_node(n->children, "name"))) { + g_free(name); + name = obt_xml_node_string(fnode); + } + if ((fnode = obt_xml_find_node(n->children, "size"))) { + int s = obt_xml_node_int(fnode); + if (s > 0) size = s; + } + if ((fnode = obt_xml_find_node(n->children, "weight"))) { + gchar *w = obt_xml_node_string(fnode); + if (!g_ascii_strcasecmp(w, "Bold")) + weight = RR_FONTWEIGHT_BOLD; + g_free(w); + } + if ((fnode = obt_xml_find_node(n->children, "slant"))) { + gchar *s = obt_xml_node_string(fnode); + if (!g_ascii_strcasecmp(s, "Italic")) + slant = RR_FONTSLANT_ITALIC; + if (!g_ascii_strcasecmp(s, "Oblique")) + slant = RR_FONTSLANT_OBLIQUE; + g_free(s); + } + + *font = RrFontOpen(ob_rr_inst, name, size, weight, slant); + g_free(name); + next_font: + n = obt_xml_find_node(n->next, "font"); + } +} + +static void parse_desktops(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "number"))) { + gint d = obt_xml_node_int(n); + if (d > 0) + config_desktops_num = (unsigned) d; + } + if ((n = obt_xml_find_node(node, "firstdesk"))) { + gint d = obt_xml_node_int(n); + if (d > 0) + config_screen_firstdesk = (unsigned) d; + } + if ((n = obt_xml_find_node(node, "names"))) { + GSList *it; + xmlNodePtr nname; + + for (it = config_desktops_names; it; it = it->next) + g_free(it->data); + g_slist_free(config_desktops_names); + config_desktops_names = NULL; + + nname = obt_xml_find_node(n->children, "name"); + while (nname) { + config_desktops_names = + g_slist_append(config_desktops_names, + obt_xml_node_string(nname)); + nname = obt_xml_find_node(nname->next, "name"); + } + } + if ((n = obt_xml_find_node(node, "popupTime"))) + config_desktop_popup_time = obt_xml_node_int(n); +} + +static void parse_resize(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "drawContents"))) + config_resize_redraw = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "popupShow"))) { + config_resize_popup_show = obt_xml_node_int(n); + if (obt_xml_node_contains(n, "Always")) + config_resize_popup_show = 2; + else if (obt_xml_node_contains(n, "Never")) + config_resize_popup_show = 0; + else if (obt_xml_node_contains(n, "Nonpixel")) + config_resize_popup_show = 1; + } + if ((n = obt_xml_find_node(node, "popupPosition"))) { + if (obt_xml_node_contains(n, "Top")) + config_resize_popup_pos = OB_RESIZE_POS_TOP; + else if (obt_xml_node_contains(n, "Center")) + config_resize_popup_pos = OB_RESIZE_POS_CENTER; + else if (obt_xml_node_contains(n, "Fixed")) { + config_resize_popup_pos = OB_RESIZE_POS_FIXED; + + if ((n = obt_xml_find_node(node, "popupFixedPosition"))) { + xmlNodePtr n2; + + if ((n2 = obt_xml_find_node(n->children, "x"))) + config_parse_gravity_coord(n2, + &config_resize_popup_fixed.x); + if ((n2 = obt_xml_find_node(n->children, "y"))) + config_parse_gravity_coord(n2, + &config_resize_popup_fixed.y); + + config_resize_popup_fixed.x.pos = + MAX(config_resize_popup_fixed.x.pos, 0); + config_resize_popup_fixed.y.pos = + MAX(config_resize_popup_fixed.y.pos, 0); + } + } + } +} + +static void parse_dock(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + + if ((n = obt_xml_find_node(node, "position"))) { + if (obt_xml_node_contains(n, "TopLeft")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_NORTHWEST; + else if (obt_xml_node_contains(n, "Top")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_NORTH; + else if (obt_xml_node_contains(n, "TopRight")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_NORTHEAST; + else if (obt_xml_node_contains(n, "Right")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_EAST; + else if (obt_xml_node_contains(n, "BottomRight")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_SOUTHEAST; + else if (obt_xml_node_contains(n, "Bottom")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_SOUTH; + else if (obt_xml_node_contains(n, "BottomLeft")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_SOUTHWEST; + else if (obt_xml_node_contains(n, "Left")) + config_dock_floating = FALSE, + config_dock_pos = OB_DIRECTION_WEST; + else if (obt_xml_node_contains(n, "Floating")) + config_dock_floating = TRUE; + } + if (config_dock_floating) { + if ((n = obt_xml_find_node(node, "floatingX"))) + config_dock_x = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "floatingY"))) + config_dock_y = obt_xml_node_int(n); + } else { + if ((n = obt_xml_find_node(node, "noStrut"))) + config_dock_nostrut = obt_xml_node_bool(n); + } + if ((n = obt_xml_find_node(node, "stacking"))) { + if (obt_xml_node_contains(n, "normal")) + config_dock_layer = OB_STACKING_LAYER_NORMAL; + else if (obt_xml_node_contains(n, "below")) + config_dock_layer = OB_STACKING_LAYER_BELOW; + else if (obt_xml_node_contains(n, "above")) + config_dock_layer = OB_STACKING_LAYER_ABOVE; + } + if ((n = obt_xml_find_node(node, "direction"))) { + if (obt_xml_node_contains(n, "horizontal")) + config_dock_orient = OB_ORIENTATION_HORZ; + else if (obt_xml_node_contains(n, "vertical")) + config_dock_orient = OB_ORIENTATION_VERT; + } + if ((n = obt_xml_find_node(node, "autoHide"))) + config_dock_hide = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "hideDelay"))) + config_dock_hide_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "showDelay"))) + config_dock_show_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "moveButton"))) { + gchar *str = obt_xml_node_string(n); + guint b, s; + if (translate_button(str, &s, &b)) { + config_dock_app_move_button = b; + config_dock_app_move_modifiers = s; + } else { + g_message(_("Invalid button \"%s\" specified in config file"), str); + } + g_free(str); + } +} + +static void parse_menu(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + node = node->children; + + if ((n = obt_xml_find_node(node, "hideDelay"))) + config_menu_hide_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "middle"))) + config_menu_middle = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "submenuShowDelay"))) + config_submenu_show_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "submenuHideDelay"))) + config_submenu_hide_delay = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "manageDesktops"))) + config_menu_manage_desktops = obt_xml_node_bool(n); + if ((n = obt_xml_find_node(node, "showIcons"))) { + config_menu_show_icons = obt_xml_node_bool(n); +#ifndef USE_IMLIB2 + if (config_menu_show_icons) + g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded.")); +#endif + } + + while ((node = obt_xml_find_node(node, "file"))) { + gchar *c = obt_xml_node_string(node); + config_menu_files = g_slist_append(config_menu_files, + obt_paths_expand_tilde(c)); + g_free(c); + node = node->next; + } +} + +static void parse_resistance(xmlNodePtr node, gpointer d) +{ + xmlNodePtr n; + + node = node->children; + if ((n = obt_xml_find_node(node, "strength"))) + config_resist_win = obt_xml_node_int(n); + if ((n = obt_xml_find_node(node, "screen_edge_strength"))) + config_resist_edge = obt_xml_node_int(n); +} + +typedef struct +{ + const gchar *key; + const gchar *actname; +} ObDefKeyBind; + +static void bind_default_keyboard(void) +{ + ObDefKeyBind *it; + ObDefKeyBind binds[] = { + { "A-Tab", "NextWindow" }, + { "S-A-Tab", "PreviousWindow" }, + { "A-F4", "Close" }, + { NULL, NULL } + }; + for (it = binds; it->key; ++it) { + GList *l = g_list_append(NULL, g_strdup(it->key)); + keyboard_bind(l, actions_parse_string(it->actname)); + } +} + +typedef struct +{ + const gchar *button; + const gchar *context; + const ObMouseAction mact; + const gchar *actname; +} ObDefMouseBind; + +static void bind_default_mouse(void) +{ + ObDefMouseBind *it; + ObDefMouseBind binds[] = { + { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" }, + { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" }, + { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" }, + { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" }, + { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" }, + { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" }, + { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" }, + { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" }, + { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" }, + { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" }, + { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" }, + { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" }, + { NULL, NULL, 0, NULL } + }; + + for (it = binds; it->button; ++it) + mouse_bind(it->button, frame_context_from_string(it->context), + it->mact, actions_parse_string(it->actname)); +} + +void config_startup(ObtXmlInst *i) +{ + config_focus_new = TRUE; + config_focus_follow = FALSE; + config_focus_delay = 0; + config_focus_raise = FALSE; + config_focus_last = TRUE; + config_focus_under_mouse = FALSE; + config_unfocus_leave = FALSE; + + obt_xml_register(i, "focus", parse_focus, NULL); + + config_place_policy = OB_PLACE_POLICY_SMART; + config_place_center = TRUE; + config_place_monitor = OB_PLACE_MONITOR_PRIMARY; + + config_primary_monitor_index = 1; + config_primary_monitor = OB_PLACE_MONITOR_ACTIVE; + + obt_xml_register(i, "placement", parse_placement, NULL); + + STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + obt_xml_register(i, "margins", parse_margins, NULL); + + config_theme = NULL; + + config_animate_iconify = TRUE; + config_title_layout = g_strdup("NLIMC"); + config_theme_keepborder = TRUE; + config_theme_window_list_icon_size = 36; + + config_font_activewindow = NULL; + config_font_inactivewindow = NULL; + config_font_menuitem = NULL; + config_font_menutitle = NULL; + config_font_activeosd = NULL; + config_font_inactiveosd = NULL; + + obt_xml_register(i, "theme", parse_theme, NULL); + + config_desktops_num = 4; + config_screen_firstdesk = 1; + config_desktops_names = NULL; + config_desktop_popup_time = 875; + + obt_xml_register(i, "desktops", parse_desktops, NULL); + + config_resize_redraw = TRUE; + config_resize_popup_show = 1; /* nonpixel increments */ + config_resize_popup_pos = OB_RESIZE_POS_CENTER; + GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE); + GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE); + + obt_xml_register(i, "resize", parse_resize, NULL); + + config_dock_layer = OB_STACKING_LAYER_ABOVE; + config_dock_pos = OB_DIRECTION_NORTHEAST; + config_dock_floating = FALSE; + config_dock_nostrut = FALSE; + config_dock_x = 0; + config_dock_y = 0; + config_dock_orient = OB_ORIENTATION_VERT; + config_dock_hide = FALSE; + config_dock_hide_delay = 300; + config_dock_show_delay = 300; + config_dock_app_move_button = 2; /* middle */ + config_dock_app_move_modifiers = 0; + + obt_xml_register(i, "dock", parse_dock, NULL); + + translate_key("C-g", &config_keyboard_reset_state, + &config_keyboard_reset_keycode); + + bind_default_keyboard(); + + obt_xml_register(i, "keyboard", parse_keyboard, NULL); + + config_mouse_threshold = 8; + config_mouse_dclicktime = 500; + config_mouse_screenedgetime = 400; + config_mouse_screenedgewarp = FALSE; + + bind_default_mouse(); + + obt_xml_register(i, "mouse", parse_mouse, NULL); + + config_resist_win = 10; + config_resist_edge = 20; + + obt_xml_register(i, "resistance", parse_resistance, NULL); + + config_menu_hide_delay = 250; + config_menu_middle = FALSE; + config_submenu_show_delay = 100; + config_submenu_hide_delay = 400; + config_menu_manage_desktops = TRUE; + config_menu_files = NULL; + config_menu_show_icons = TRUE; + + obt_xml_register(i, "menu", parse_menu, NULL); + + config_per_app_settings = NULL; + + obt_xml_register(i, "applications", parse_per_app_settings, NULL); +} + +void config_shutdown(void) +{ + GSList *it; + + g_free(config_theme); + + g_free(config_title_layout); + + RrFontClose(config_font_activewindow); + RrFontClose(config_font_inactivewindow); + RrFontClose(config_font_menuitem); + RrFontClose(config_font_menutitle); + RrFontClose(config_font_activeosd); + RrFontClose(config_font_inactiveosd); + + for (it = config_desktops_names; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(config_desktops_names); + + for (it = config_menu_files; it; it = g_slist_next(it)) + g_free(it->data); + g_slist_free(config_menu_files); + + for (it = config_per_app_settings; it; it = g_slist_next(it)) { + ObAppSettings *itd = (ObAppSettings *)it->data; + if (itd->name) g_pattern_spec_free(itd->name); + if (itd->role) g_pattern_spec_free(itd->role); + if (itd->title) g_pattern_spec_free(itd->title); + if (itd->class) g_pattern_spec_free(itd->class); + g_slice_free(ObAppSettings, it->data); + } + g_slist_free(config_per_app_settings); +} diff --git a/openbox/config.h b/openbox/config.h new file mode 100644 index 0000000..730dc39 --- /dev/null +++ b/openbox/config.h @@ -0,0 +1,225 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + config.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __config_h +#define __config_h + +#include "misc.h" +#include "stacking.h" +#include "place.h" +#include "client.h" +#include "geom.h" +#include "moveresize.h" +#include "obrender/render.h" +#include "obt/xml.h" + +#include <glib.h> + +typedef struct _ObAppSettings ObAppSettings; + +struct _ObAppSettings +{ + GPatternSpec *class; + GPatternSpec *name; + GPatternSpec *role; + GPatternSpec *title; + ObClientType type; + + GravityPoint position; + gboolean pos_given; + gboolean pos_force; + + guint desktop; + gint shade; + gint decor; + gint focus; + ObPlaceMonitor monitor_type; + gint monitor; + gint iconic; + gint skip_pager; + gint skip_taskbar; + gint max_horz; + gint max_vert; + gint fullscreen; + + gint layer; +}; + +/*! Should new windows be focused */ +extern gboolean config_focus_new; +/*! Focus windows when the mouse enters them */ +extern gboolean config_focus_follow; +/*! Timeout for focusing windows on focus follows mouse, in milliseconds */ +extern guint config_focus_delay; +/*! If windows should automatically be raised when they are focused in + focus follows mouse */ +extern gboolean config_focus_raise; +/*! Focus the last focused window, not under the mouse, in follow mouse mode */ +extern gboolean config_focus_last; +/*! Try keep focus on the window under the mouse when the mouse is not moving + */ +extern gboolean config_focus_under_mouse; +/*! Remove focus from windows when the mouse leaves them + */ +extern gboolean config_unfocus_leave; + +/*! The algorithm to use for placing new windows */ +extern ObPlacePolicy config_place_policy; +/*! Place windows in the center of the free area */ +extern gboolean config_place_center; +/*! Place windows on the active monitor (unless they are part of an application + already on another monitor) */ +extern ObPlaceMonitor config_place_monitor; + +/*! Place dialogs and stuff on this monitor. Index starts at 1. If this is + 0, then use the config_primary_monitor instead. */ +extern guint config_primary_monitor_index; +/*! Where to place dialogs and stuff if it is not specified by index. */ +extern ObPlaceMonitor config_primary_monitor; + +/*! User-specified margins around the edge of the screen(s) */ +extern StrutPartial config_margins; + +/*! When true windows' contents are refreshed while they are resized; otherwise + they are not updated until the resize is complete */ +extern gboolean config_resize_redraw; +/*! show move/resize popups? 0 = no, 1 = always, 2 = only + resizing !1 increments */ +extern gint config_resize_popup_show; +/*! where to show the resize popup */ +extern ObResizePopupPos config_resize_popup_pos; +/*! where to place the popup if it's in a fixed position */ +extern GravityPoint config_resize_popup_fixed; + +/*! The stacking layer the dock will reside in */ +extern ObStackingLayer config_dock_layer; +/*! Is the dock floating */ +extern gboolean config_dock_floating; +/*! Don't use a strut for the dock */ +extern gboolean config_dock_nostrut; +/*! Where to place the dock if not floating */ +extern ObDirection config_dock_pos; +/*! If config_dock_floating, this is the top-left corner's + position */ +extern gint config_dock_x; +/*! If config_dock_floating, this is the top-left corner's + position */ +extern gint config_dock_y; +/*! Whether the dock places the dockapps in it horizontally or vertically */ +extern ObOrientation config_dock_orient; +/*! Whether to auto-hide the dock when the pointer is not over it */ +extern gboolean config_dock_hide; +/*! The number of milliseconds to wait before hiding the dock */ +extern guint config_dock_hide_delay; +/*! The number of milliseconds to wait before showing the dock */ +extern guint config_dock_show_delay; +/*! The mouse button to be used to move dock apps */ +extern guint config_dock_app_move_button; +/*! The modifiers to be used with the button to move dock apps */ +extern guint config_dock_app_move_modifiers; + +/*! The name of the theme */ +extern gchar *config_theme; + +/*! Show the one-pixel border after toggleDecor */ +extern gboolean config_theme_keepborder; +/*! Titlebar button layout */ +extern gchar *config_title_layout; +/*! Animate windows iconifying and restoring */ +extern gboolean config_animate_iconify; +/*! Size of icons in focus switching dialogs */ +extern guint config_theme_window_list_icon_size; + +/*! The font for the active window's title */ +extern RrFont *config_font_activewindow; +/*! The font for inactive windows' titles */ +extern RrFont *config_font_inactivewindow; +/*! The font for menu titles */ +extern RrFont *config_font_menutitle; +/*! The font for menu items */ +extern RrFont *config_font_menuitem; +/*! The font for on-screen-displays/popups' active text */ +extern RrFont *config_font_activeosd; +/*! The font for on-screen-displays/popups' inactive text */ +extern RrFont *config_font_inactiveosd; + +/*! The number of desktops */ +extern guint config_desktops_num; +/*! Desktop to start on, put 5 to start in the center of a 3x3 grid */ +extern guint config_screen_firstdesk; +/*! Names for the desktops */ +extern GSList *config_desktops_names; +/*! Amount of time to show the desktop switch dialog */ +extern guint config_desktop_popup_time; + +/*! The keycode of the key combo which resets the keybaord chains */ +extern guint config_keyboard_reset_keycode; +/*! The modifiers of the key combo which resets the keybaord chains */ +extern guint config_keyboard_reset_state; + +/*! Number of pixels a drag must go before being considered a drag */ +extern gint config_mouse_threshold; +/*! Number of milliseconds within which 2 clicks must occur to be a + double-click */ +extern gint config_mouse_dclicktime; +/*! Number of milliseconds that the mouse has to be on the screen edge before + a screen edge event is triggered */ +extern gint config_mouse_screenedgetime; +/*! When TRUE, the mouse is warped to the other side of the desktop after + switching desktops from bumping the screen edge */ +extern gboolean config_mouse_screenedgewarp; + +/*! Number of pixels to resist while crossing another window's edge */ +extern gint config_resist_win; +/*! Number of pixels to resist while crossing a screen's edge */ +extern gint config_resist_edge; + +/*! Delay for hiding menu when opening in milliseconds */ +extern guint config_menu_hide_delay; +/*! Center menus vertically about the parent entry */ +extern gboolean config_menu_middle; +/*! Delay before opening a submenu in milliseconds */ +extern guint config_submenu_show_delay; +/*! Delay before closing a submenu in milliseconds */ +extern guint config_submenu_hide_delay; +/*! Show manage desktops in client_list_menu */ +extern gboolean config_menu_manage_desktops; +/*! Load & show icons in user-defined menus */ +extern gboolean config_menu_show_icons; +/*! User-specified menu files */ +extern GSList *config_menu_files; +/*! Per app settings */ +extern GSList *config_per_app_settings; + +void config_startup(ObtXmlInst *i); +void config_shutdown(void); + +/*! Create an ObAppSettings structure with the default values */ +ObAppSettings* config_create_app_settings(void); +/*! Copies any settings in src to dest, if they are their default value in + src. */ +void config_app_settings_copy_non_defaults(const ObAppSettings *src, + ObAppSettings *dest); +/*! Parses an x geometry style position, with some extensions like ratios + and percentages */ +void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c); +/*! Parses a rational number or percentage into num and denom */ +void config_parse_relative_number(gchar *s, gint *num, gint *denom); + +#endif diff --git a/openbox/debug.c b/openbox/debug.c new file mode 100644 index 0000000..ad083b1 --- /dev/null +++ b/openbox/debug.c @@ -0,0 +1,201 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + debug.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "prompt.h" +#include "openbox.h" +#include "gettext.h" +#include "obt/paths.h" + +#include <glib.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +static gboolean enabled_types[OB_DEBUG_TYPE_NUM] = {FALSE}; +static FILE *log_file = NULL; +static guint rr_handler_id = 0; +static guint obt_handler_id = 0; +static guint ob_handler_id = 0; +static guint ob_handler_prompt_id = 0; +static GList *prompt_queue = NULL; +static gboolean allow_prompts = TRUE; + +static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data); +static void prompt_handler(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data); + +void ob_debug_startup(void) +{ + ObtPaths *p = obt_paths_new(); + gchar *dir = g_build_filename(obt_paths_cache_home(p), + "openbox", NULL); + + /* log messages to a log file! fancy, no? */ + if (!obt_paths_mkdir_path(dir, 0777)) + g_message(_("Unable to make directory '%s': %s"), + dir, g_strerror(errno)); + else { + gchar *name = g_build_filename(obt_paths_cache_home(p), + "openbox", "openbox.log", NULL); + /* unlink it before opening to remove competition */ + unlink(name); + log_file = fopen(name, "w"); + g_free(name); + } + + rr_handler_id = + g_log_set_handler("ObRender", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | + G_LOG_FLAG_RECURSION, log_handler, NULL); + obt_handler_id = + g_log_set_handler("Obt", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | + G_LOG_FLAG_RECURSION, log_handler, NULL); + ob_handler_id = + g_log_set_handler("Openbox", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | + G_LOG_FLAG_RECURSION, log_handler, NULL); + ob_handler_prompt_id = + g_log_set_handler("Openbox", G_LOG_LEVEL_MASK & ~G_LOG_LEVEL_DEBUG, + prompt_handler, NULL); + + obt_paths_unref(p); + g_free(dir); +} + +void ob_debug_shutdown(void) +{ + g_log_remove_handler("ObRender", rr_handler_id); + g_log_remove_handler("Obt", obt_handler_id); + g_log_remove_handler("Openbox", ob_handler_id); + g_log_remove_handler("Openbox", ob_handler_prompt_id); + + if (log_file) { + fclose(log_file); + log_file = NULL; + } +} + +void ob_debug_enable(ObDebugType type, gboolean enable) +{ + g_assert(type < OB_DEBUG_TYPE_NUM); + enabled_types[type] = enable; +} + +static inline void log_print(FILE *out, const gchar* log_domain, + const gchar *level, const gchar *message) +{ + fprintf(out, "%s", log_domain); + fprintf(out, "-"); + fprintf(out, "%s", level); + fprintf(out, ": "); + fprintf(out, "%s", message); + fprintf(out, "\n"); + fflush(out); +} + +static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer data) +{ + FILE *out; + const gchar *level; + + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_DEBUG: level = "Debug"; out = stdout; break; + case G_LOG_LEVEL_INFO: level = "Info"; out = stdout; break; + case G_LOG_LEVEL_MESSAGE: level = "Message"; out = stdout; break; + case G_LOG_LEVEL_WARNING: level = "Warning"; out = stderr; break; + case G_LOG_LEVEL_CRITICAL: level = "Critical"; out = stderr; break; + case G_LOG_LEVEL_ERROR: level = "Error"; out = stderr; break; + default: g_assert_not_reached(); /* invalid level.. */ + } + + log_print(out, log_domain, level, message); + if (log_file) log_print(log_file, log_domain, level, message); +} + +static void prompt_handler(const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer data) +{ + if (ob_state() == OB_STATE_RUNNING && allow_prompts) + prompt_queue = g_list_prepend(prompt_queue, g_strdup(message)); + else + log_handler(log_domain, log_level, message, data); +} + +static inline void log_argv(ObDebugType type, + const gchar *format, va_list args) +{ + const gchar *prefix; + gchar *message; + + g_assert(type < OB_DEBUG_TYPE_NUM); + if (!enabled_types[type]) return; + + switch (type) { + case OB_DEBUG_FOCUS: prefix = "(FOCUS) "; break; + case OB_DEBUG_APP_BUGS: prefix = "(APPLICATION BUG) "; break; + case OB_DEBUG_SM: prefix = "(SESSION) "; break; + default: prefix = NULL; break; + } + + message = g_strdup_vprintf(format, args); + if (prefix) { + gchar *a = message; + message = g_strconcat(prefix, message, NULL); + g_free(a); + } + + g_debug("%s", message); + g_free(message); +} + +void ob_debug(const gchar *a, ...) +{ + va_list vl; + + va_start(vl, a); + log_argv(OB_DEBUG_NORMAL, a, vl); + va_end(vl); +} + +void ob_debug_type(ObDebugType type, const gchar *a, ...) +{ + va_list vl; + + va_start(vl, a); + log_argv(type, a, vl); + va_end(vl); +} + +void ob_debug_show_prompts(void) +{ + if (prompt_queue) { + allow_prompts = FALSE; /* avoid recursive prompts */ + while (prompt_queue) { + prompt_show_message(prompt_queue->data, "Openbox", _("Close")); + g_free(prompt_queue->data); + prompt_queue = g_list_delete_link(prompt_queue, prompt_queue); + } + allow_prompts = TRUE; + } +} diff --git a/openbox/debug.h b/openbox/debug.h new file mode 100644 index 0000000..13c5598 --- /dev/null +++ b/openbox/debug.h @@ -0,0 +1,43 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + debug.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ob__debug_h +#define __ob__debug_h + +#include <glib.h> + +void ob_debug_startup(void); +void ob_debug_shutdown(void); + +void ob_debug(const gchar *a, ...); + +typedef enum { + OB_DEBUG_NORMAL, + OB_DEBUG_FOCUS, + OB_DEBUG_APP_BUGS, + OB_DEBUG_SM, + OB_DEBUG_TYPE_NUM +} ObDebugType; + +void ob_debug_type(ObDebugType type, const gchar *a, ...); + +void ob_debug_enable(ObDebugType type, gboolean enable); + +void ob_debug_show_prompts(void); + +#endif diff --git a/openbox/dock.c b/openbox/dock.c new file mode 100644 index 0000000..c26eee6 --- /dev/null +++ b/openbox/dock.c @@ -0,0 +1,693 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + dock.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "dock.h" +#include "screen.h" +#include "config.h" +#include "grab.h" +#include "openbox.h" +#include "obrender/theme.h" +#include "obt/prop.h" + +#define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \ + EnterWindowMask | LeaveWindowMask) +#define DOCKAPP_EVENT_MASK (StructureNotifyMask) +#define DOCK_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \ + ButtonMotionMask) + +static ObDock *dock; +static guint show_timeout_id; +static guint hide_timeout_id; + +StrutPartial dock_strut; + +static void dock_app_grab_button(ObDockApp *app, gboolean grab) +{ + if (grab) { + grab_button_full(config_dock_app_move_button, + config_dock_app_move_modifiers, app->icon_win, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask, + GrabModeAsync, OB_CURSOR_MOVE); + } else { + ungrab_button(config_dock_app_move_button, + config_dock_app_move_modifiers, app->icon_win); + } +} + +static guint window_hash(Window *w) { return *w; } +static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; } + +void dock_startup(gboolean reconfig) +{ + XSetWindowAttributes attrib; + + if (reconfig) { + GList *it; + + XSetWindowBorder(obt_display, dock->frame, + RrColorPixel(ob_rr_theme->osd_border_color)); + XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth); + + RrAppearanceFree(dock->a_frame); + dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg); + + stacking_add(DOCK_AS_WINDOW(dock)); + + dock_configure(); + dock_hide(TRUE); + + for (it = dock->dock_apps; it; it = g_list_next(it)) + dock_app_grab_button(it->data, TRUE); + return; + } + + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + + dock = g_slice_new0(ObDock); + dock->obwin.type = OB_WINDOW_CLASS_DOCK; + + dock->hidden = TRUE; + + dock->dock_map = g_hash_table_new((GHashFunc)window_hash, + (GEqualFunc)window_comp); + + attrib.event_mask = DOCK_EVENT_MASK; + attrib.override_redirect = True; + attrib.do_not_propagate_mask = DOCK_NOPROPAGATEMASK; + dock->frame = XCreateWindow(obt_display, obt_root(ob_screen), + 0, 0, 1, 1, 0, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), + CWOverrideRedirect | CWEventMask | + CWDontPropagate, + &attrib); + dock->a_frame = RrAppearanceCopy(ob_rr_theme->osd_bg); + XSetWindowBorder(obt_display, dock->frame, + RrColorPixel(ob_rr_theme->osd_border_color)); + XSetWindowBorderWidth(obt_display, dock->frame, ob_rr_theme->obwidth); + + /* Setting the window type so xcompmgr can tell what it is */ + OBT_PROP_SET32(dock->frame, NET_WM_WINDOW_TYPE, ATOM, + OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK)); + + window_add(&dock->frame, DOCK_AS_WINDOW(dock)); + stacking_add(DOCK_AS_WINDOW(dock)); +} + +void dock_shutdown(gboolean reconfig) +{ + if (reconfig) { + GList *it; + + stacking_remove(DOCK_AS_WINDOW(dock)); + + for (it = dock->dock_apps; it; it = g_list_next(it)) + dock_app_grab_button(it->data, FALSE); + return; + } + + g_hash_table_destroy(dock->dock_map); + + XDestroyWindow(obt_display, dock->frame); + RrAppearanceFree(dock->a_frame); + window_remove(dock->frame); + stacking_remove(dock); + g_slice_free(ObDock, dock); + dock = NULL; +} + +void dock_manage(Window icon_win, Window name_win) +{ + ObDockApp *app; + XWindowAttributes attrib; + gchar **data; + + app = g_slice_new0(ObDockApp); + app->name_win = name_win; + app->icon_win = icon_win; + + if (OBT_PROP_GETSS_TYPE(app->name_win, WM_CLASS, STRING_NO_CC, &data)) { + if (data[0]) { + app->name = g_strdup(data[0]); + if (data[1]) + app->class = g_strdup(data[1]); + } + g_strfreev(data); + } + + if (app->name == NULL) app->name = g_strdup(""); + if (app->class == NULL) app->class = g_strdup(""); + + if (XGetWindowAttributes(obt_display, app->icon_win, &attrib)) { + app->w = attrib.width; + app->h = attrib.height; + } else { + app->w = app->h = 64; + } + + dock->dock_apps = g_list_append(dock->dock_apps, app); + g_hash_table_insert(dock->dock_map, &app->icon_win, app); + dock_configure(); + + XReparentWindow(obt_display, app->icon_win, dock->frame, app->x, app->y); + /* + This is the same case as in frame.c for client windows. When Openbox is + starting, the window is already mapped so we see unmap events occur for + it. There are 2 unmap events generated that we see, one with the 'event' + member set the root window, and one set to the client, but both get + handled and need to be ignored. + */ + if (ob_state() == OB_STATE_STARTING) + app->ignore_unmaps += 2; + XChangeSaveSet(obt_display, app->icon_win, SetModeInsert); + XMapWindow(obt_display, app->icon_win); + + if (app->name_win != app->icon_win) { + XReparentWindow(obt_display, app->name_win, dock->frame, -1000, -1000); + XChangeSaveSet(obt_display, app->name_win, SetModeInsert); + XMapWindow(obt_display, app->name_win); + } + + XSync(obt_display, False); + + XSelectInput(obt_display, app->icon_win, DOCKAPP_EVENT_MASK); + + dock_app_grab_button(app, TRUE); + + ob_debug("Managed Dock App: 0x%lx 0x%lx (%s)", + app->icon_win, app->name_win, app->class); + + grab_server(FALSE); +} + +void dock_unmanage_all(void) +{ + while (dock->dock_apps) + dock_unmanage(dock->dock_apps->data, TRUE); +} + +void dock_unmanage(ObDockApp *app, gboolean reparent) +{ + dock_app_grab_button(app, FALSE); + XSelectInput(obt_display, app->icon_win, NoEventMask); + /* remove the window from our save set */ + XChangeSaveSet(obt_display, app->icon_win, SetModeDelete); + XSync(obt_display, False); + + if (reparent) { + XReparentWindow(obt_display, app->icon_win, obt_root(ob_screen), 0, 0); + if (app->name_win != app->icon_win) + XReparentWindow(obt_display, app->name_win, + obt_root(ob_screen), 0, 0); + } + + dock->dock_apps = g_list_remove(dock->dock_apps, app); + g_hash_table_remove(dock->dock_map, &app->icon_win); + dock_configure(); + + ob_debug("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class); + + g_free(app->name); + g_free(app->class); + g_slice_free(ObDockApp, app); +} + +void dock_configure(void) +{ + GList *it; + gint hspot, vspot; + gint gravity; + gint l, r, t, b; + gint strw, strh; + const Rect *a; + gint hidesize; + + RrMargins(dock->a_frame, &l, &t, &r, &b); + hidesize = MAX(1, ob_rr_theme->obwidth); + + dock->area.width = dock->area.height = 0; + + /* get the size */ + for (it = dock->dock_apps; it; it = g_list_next(it)) { + ObDockApp *app = it->data; + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + dock->area.width += app->w; + dock->area.height = MAX(dock->area.height, app->h); + break; + case OB_ORIENTATION_VERT: + dock->area.width = MAX(dock->area.width, app->w); + dock->area.height += app->h; + break; + } + } + + if (dock->dock_apps) { + dock->area.width += l + r; + dock->area.height += t + b; + } + + hspot = l; + vspot = t; + + /* position the apps */ + for (it = dock->dock_apps; it; it = g_list_next(it)) { + ObDockApp *app = it->data; + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + app->x = hspot; + app->y = (dock->area.height - app->h) / 2; + hspot += app->w; + break; + case OB_ORIENTATION_VERT: + app->x = (dock->area.width - app->w) / 2; + app->y = vspot; + vspot += app->h; + break; + } + + XMoveWindow(obt_display, app->icon_win, app->x, app->y); + } + + /* used for calculating offsets */ + dock->area.width += ob_rr_theme->obwidth * 2; + dock->area.height += ob_rr_theme->obwidth * 2; + + a = screen_physical_area_all_monitors(); + + /* calculate position */ + if (config_dock_floating) { + dock->area.x = config_dock_x; + dock->area.y = config_dock_y; + gravity = NorthWestGravity; + } else { + switch (config_dock_pos) { + case OB_DIRECTION_NORTHWEST: + dock->area.x = 0; + dock->area.y = 0; + gravity = NorthWestGravity; + break; + case OB_DIRECTION_NORTH: + dock->area.x = a->width / 2; + dock->area.y = 0; + gravity = NorthGravity; + break; + case OB_DIRECTION_NORTHEAST: + dock->area.x = a->width; + dock->area.y = 0; + gravity = NorthEastGravity; + break; + case OB_DIRECTION_WEST: + dock->area.x = 0; + dock->area.y = a->height / 2; + gravity = WestGravity; + break; + case OB_DIRECTION_EAST: + dock->area.x = a->width; + dock->area.y = a->height / 2; + gravity = EastGravity; + break; + case OB_DIRECTION_SOUTHWEST: + dock->area.x = 0; + dock->area.y = a->height; + gravity = SouthWestGravity; + break; + case OB_DIRECTION_SOUTH: + dock->area.x = a->width / 2; + dock->area.y = a->height; + gravity = SouthGravity; + break; + case OB_DIRECTION_SOUTHEAST: + dock->area.x = a->width; + dock->area.y = a->height; + gravity = SouthEastGravity; + break; + default: + g_assert_not_reached(); + } + } + + switch(gravity) { + case NorthGravity: + case CenterGravity: + case SouthGravity: + dock->area.x -= dock->area.width / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + dock->area.x -= dock->area.width; + break; + } + switch(gravity) { + case WestGravity: + case CenterGravity: + case EastGravity: + dock->area.y -= dock->area.height / 2; + break; + case SouthWestGravity: + case SouthGravity: + case SouthEastGravity: + dock->area.y -= dock->area.height; + break; + } + + if (config_dock_hide && dock->hidden) { + if (!config_dock_floating) { + switch (config_dock_pos) { + case OB_DIRECTION_NORTHWEST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + dock->area.y -= dock->area.height - hidesize; + break; + case OB_ORIENTATION_VERT: + dock->area.x -= dock->area.width - hidesize; + break; + } + break; + case OB_DIRECTION_NORTH: + dock->area.y -= dock->area.height - hidesize; + break; + case OB_DIRECTION_NORTHEAST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + dock->area.y -= dock->area.height - hidesize; + break; + case OB_ORIENTATION_VERT: + dock->area.x += dock->area.width - hidesize; + break; + } + break; + case OB_DIRECTION_WEST: + dock->area.x -= dock->area.width - hidesize; + break; + case OB_DIRECTION_EAST: + dock->area.x += dock->area.width - hidesize; + break; + case OB_DIRECTION_SOUTHWEST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + dock->area.y += dock->area.height - hidesize; + break; + case OB_ORIENTATION_VERT: + dock->area.x -= dock->area.width - hidesize; + break; + } break; + case OB_DIRECTION_SOUTH: + dock->area.y += dock->area.height - hidesize; + break; + case OB_DIRECTION_SOUTHEAST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + dock->area.y += dock->area.height - hidesize; + break; + case OB_ORIENTATION_VERT: + dock->area.x += dock->area.width - hidesize; + break; + } + break; + } + } + } + + if (!config_dock_floating && config_dock_hide) { + strw = hidesize; + strh = hidesize; + } else { + strw = dock->area.width; + strh = dock->area.height; + } + + /* set the strut */ + if (!dock->dock_apps) { + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } + else if (config_dock_floating || config_dock_nostrut) { + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0); + } + else { + switch (config_dock_pos) { + case OB_DIRECTION_NORTHWEST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, + 0, 0, dock->area.x, dock->area.x + + dock->area.width - 1, 0, 0, 0, 0); + break; + case OB_ORIENTATION_VERT: + STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, + dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0, 0, 0, 0, 0); + break; + } + break; + case OB_DIRECTION_NORTH: + STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, + 0, 0, dock->area.x, dock->area.x + + dock->area.width - 1, 0, 0, 0, 0); + break; + case OB_DIRECTION_NORTHEAST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, + 0, 0, dock->area.x, dock->area.x + + dock->area.width -1, 0, 0, 0, 0); + break; + case OB_ORIENTATION_VERT: + STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, + 0, 0, 0, 0, dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0); + break; + } + break; + case OB_DIRECTION_WEST: + STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, + dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0, 0, 0, 0, 0); + break; + case OB_DIRECTION_EAST: + STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, + 0, 0, 0, 0, dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0); + break; + case OB_DIRECTION_SOUTHWEST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, + 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x + + dock->area.width - 1); + break; + case OB_ORIENTATION_VERT: + STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, + dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0, 0, 0, 0, 0); + break; + } + break; + case OB_DIRECTION_SOUTH: + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, + 0, 0, 0, 0, 0, 0, dock->area.x, dock->area.x + + dock->area.width - 1); + break; + case OB_DIRECTION_SOUTHEAST: + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, + 0, 0, 0, 0, 0, 0, dock->area.x, + dock->area.x + dock->area.width - 1); + break; + case OB_ORIENTATION_VERT: + STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, + 0, 0, 0, 0, dock->area.y, dock->area.y + + dock->area.height - 1, 0, 0); + break; + } + break; + } + } + + /* not used for actually sizing shit */ + dock->area.width -= ob_rr_theme->obwidth * 2; + dock->area.height -= ob_rr_theme->obwidth * 2; + + if (dock->dock_apps) { + g_assert(dock->area.width > 0); + g_assert(dock->area.height > 0); + + XMoveResizeWindow(obt_display, dock->frame, dock->area.x, dock->area.y, + dock->area.width, dock->area.height); + + RrPaint(dock->a_frame, dock->frame, dock->area.width, + dock->area.height); + XMapWindow(obt_display, dock->frame); + } else + XUnmapWindow(obt_display, dock->frame); + + /* but they are useful outside of this function! but don't add it if the + dock is actually not visible */ + if (dock->dock_apps) { + dock->area.width += ob_rr_theme->obwidth * 2; + dock->area.height += ob_rr_theme->obwidth * 2; + } + + /* screen_resize() depends on this function to call screen_update_areas(), + so if this changes, also update screen_resize(). */ + screen_update_areas(); +} + +void dock_app_configure(ObDockApp *app, gint w, gint h) +{ + app->w = w; + app->h = h; + dock_configure(); +} + +void dock_app_drag(ObDockApp *app, XMotionEvent *e) +{ + ObDockApp *over = NULL; + GList *it; + gint x, y; + gboolean after; + gboolean stop; + + x = e->x_root; + y = e->y_root; + + /* are we on top of the dock? */ + if (!(x >= dock->area.x && + y >= dock->area.y && + x < dock->area.x + dock->area.width && + y < dock->area.y + dock->area.height)) + return; + + x -= dock->area.x; + y -= dock->area.y; + + /* which dock app are we on top of? */ + stop = FALSE; + for (it = dock->dock_apps; it; it = g_list_next(it)) { + over = it->data; + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + if (x >= over->x && x < over->x + over->w) + stop = TRUE; + break; + case OB_ORIENTATION_VERT: + if (y >= over->y && y < over->y + over->h) + stop = TRUE; + break; + } + /* dont go to it->next! */ + if (stop) break; + } + if (!it || app == over) return; + + x -= over->x; + y -= over->y; + + switch (config_dock_orient) { + case OB_ORIENTATION_HORZ: + after = (x > over->w / 2); + break; + case OB_ORIENTATION_VERT: + after = (y > over->h / 2); + break; + default: + g_assert_not_reached(); + } + + /* remove before doing the it->next! */ + dock->dock_apps = g_list_remove(dock->dock_apps, app); + + if (after) it = it->next; + + dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app); + dock_configure(); +} + +static gboolean hide_timeout(gpointer data) +{ + /* hide */ + dock->hidden = TRUE; + dock_configure(); + + hide_timeout_id = 0; + + return FALSE; /* don't repeat */ +} + +static gboolean show_timeout(gpointer data) +{ + /* show */ + dock->hidden = FALSE; + dock_configure(); + + show_timeout_id = 0; + + return FALSE; /* don't repeat */ +} + +void dock_hide(gboolean hide) +{ + if (!hide) { + if (dock->hidden && config_dock_hide) { + show_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + config_dock_show_delay, + show_timeout, NULL, NULL); + } else if (!dock->hidden && config_dock_hide && hide_timeout_id) { + if (hide_timeout_id) g_source_remove(hide_timeout_id); + hide_timeout_id = 0; + } + } else { + if (!dock->hidden && config_dock_hide) { + hide_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + config_dock_show_delay, + hide_timeout, NULL, NULL); + } else if (dock->hidden && config_dock_hide && show_timeout_id) { + if (show_timeout_id) g_source_remove(show_timeout_id); + show_timeout_id = 0; + } + } +} + +void dock_get_area(Rect *a) +{ + RECT_SET(*a, dock->area.x, dock->area.y, + dock->area.width, dock->area.height); +} + +void dock_raise_dock(void) +{ + stacking_raise(DOCK_AS_WINDOW(dock)); +} + +void dock_lower_dock(void) +{ + stacking_lower(DOCK_AS_WINDOW(dock)); +} + +ObDockApp* dock_find_dockapp(Window xwin) +{ + return g_hash_table_lookup(dock->dock_map, &xwin); +} diff --git a/openbox/dock.h b/openbox/dock.h new file mode 100644 index 0000000..cb4fd6c --- /dev/null +++ b/openbox/dock.h @@ -0,0 +1,88 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + dock.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __dock_h +#define __dock_h + +#include "window.h" +#include "stacking.h" +#include "geom.h" +#include "obrender/render.h" + +#include <glib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +typedef struct _ObDock ObDock; +typedef struct _ObDockApp ObDockApp; + +struct _ObDock +{ + ObWindow obwin; + + Window frame; + RrAppearance *a_frame; + + /* actual position (when not auto-hidden) */ + Rect area; + + gboolean hidden; + + GList *dock_apps; + GHashTable *dock_map; +}; + +struct _ObDockApp { + gint ignore_unmaps; + + Window icon_win; + Window name_win; + + gchar *name; + gchar *class; + + gint x; + gint y; + gint w; + gint h; +}; + +extern StrutPartial dock_strut; + +void dock_startup(gboolean reconfig); +void dock_shutdown(gboolean reconfig); + +void dock_configure(void); +void dock_hide(gboolean hide); + +void dock_manage(Window icon_win, Window name_win); + +void dock_unmanage_all(void); +void dock_unmanage(ObDockApp *app, gboolean reparent); + +void dock_app_drag(ObDockApp *app, XMotionEvent *e); +void dock_app_configure(ObDockApp *app, gint w, gint h); + +void dock_get_area(Rect *a); + +void dock_raise_dock(void); +void dock_lower_dock(void); + +ObDockApp* dock_find_dockapp(Window xwin); + +#endif diff --git a/openbox/event.c b/openbox/event.c new file mode 100644 index 0000000..cf089b6 --- /dev/null +++ b/openbox/event.c @@ -0,0 +1,2287 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + event.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "event.h" +#include "debug.h" +#include "window.h" +#include "openbox.h" +#include "dock.h" +#include "actions.h" +#include "client.h" +#include "config.h" +#include "screen.h" +#include "frame.h" +#include "grab.h" +#include "menu.h" +#include "prompt.h" +#include "menuframe.h" +#include "keyboard.h" +#include "mouse.h" +#include "focus.h" +#include "focus_cycle.h" +#include "moveresize.h" +#include "group.h" +#include "stacking.h" +#include "ping.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/prop.h" +#include "obt/keyboard.h" + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <glib.h> + +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> /* for usleep() */ +#endif + +#ifdef USE_SM +#include <X11/ICE/ICElib.h> +#endif + +typedef struct +{ + gboolean ignored; +} ObEventData; + +typedef struct +{ + ObClient *client; + Time time; + gulong serial; +} ObFocusDelayData; + +typedef struct +{ + gulong start; /* inclusive */ + gulong end; /* inclusive */ +} ObSerialRange; + +static void event_process(const XEvent *e, gpointer data); +static void event_handle_root(XEvent *e); +static gboolean event_handle_menu_input(XEvent *e); +static void event_handle_menu(ObMenuFrame *frame, XEvent *e); +static gboolean event_handle_prompt(ObPrompt *p, XEvent *e); +static void event_handle_dock(ObDock *s, XEvent *e); +static void event_handle_dockapp(ObDockApp *app, XEvent *e); +static void event_handle_client(ObClient *c, XEvent *e); +static gboolean event_handle_user_input(ObClient *client, XEvent *e); +static gboolean is_enter_focus_event_ignored(gulong serial); +static void event_ignore_enter_range(gulong start, gulong end); + +static void focus_delay_dest(gpointer data); +static void unfocus_delay_dest(gpointer data); +static gboolean focus_delay_func(gpointer data); +static gboolean unfocus_delay_func(gpointer data); +static void focus_delay_client_dest(ObClient *client, gpointer data); + +Time event_last_user_time = CurrentTime; + +/*! The time of the current X event (if it had a timestamp) */ +static Time event_curtime = CurrentTime; +/*! The source time that started the current X event (user-provided, so not + to be trusted) */ +static Time event_sourcetime = CurrentTime; + +/*! The serial of the current X event */ +static gulong event_curserial; +static gboolean focus_left_screen = FALSE; +static gboolean waiting_for_focusin = FALSE; +/*! A list of ObSerialRanges which are to be ignored for mouse enter events */ +static GSList *ignore_serials = NULL; +static guint focus_delay_timeout_id = 0; +static ObClient *focus_delay_timeout_client = NULL; +static guint unfocus_delay_timeout_id = 0; +static ObClient *unfocus_delay_timeout_client = NULL; + +#ifdef USE_SM +static gboolean ice_handler(GIOChannel *source, GIOCondition cond, + gpointer conn) +{ + Bool b; + IceProcessMessages(conn, NULL, &b); + return TRUE; /* don't remove the event source */ +} + +static void ice_watch(IceConn conn, IcePointer data, Bool opening, + IcePointer *watch_data) +{ + static guint id = 0; + + if (opening) { + GIOChannel *ch; + + ch = g_io_channel_unix_new(IceConnectionNumber(conn)); + id = g_io_add_watch(ch, G_IO_IN, ice_handler, conn); + g_io_channel_unref(ch); + } else if (id) { + g_source_remove(id); + id = 0; + } +} +#endif + +void event_startup(gboolean reconfig) +{ + if (reconfig) return; + + xqueue_add_callback(event_process, NULL); + +#ifdef USE_SM + IceAddConnectionWatch(ice_watch, NULL); +#endif + + client_add_destroy_notify(focus_delay_client_dest, NULL); +} + +void event_shutdown(gboolean reconfig) +{ + if (reconfig) return; + +#ifdef USE_SM + IceRemoveConnectionWatch(ice_watch, NULL); +#endif + + client_remove_destroy_notify(focus_delay_client_dest); +} + +static Window event_get_window(XEvent *e) +{ + Window window; + + /* pick a window */ + switch (e->type) { + case SelectionClear: + window = obt_root(ob_screen); + break; + case CreateNotify: + window = e->xcreatewindow.window; + break; + case MapRequest: + window = e->xmaprequest.window; + break; + case MapNotify: + window = e->xmap.window; + break; + case UnmapNotify: + window = e->xunmap.window; + break; + case DestroyNotify: + window = e->xdestroywindow.window; + break; + case ConfigureRequest: + window = e->xconfigurerequest.window; + break; + case ConfigureNotify: + window = e->xconfigure.window; + break; + default: +#ifdef XKB + if (obt_display_extension_xkb && + e->type == obt_display_extension_xkb_basep) + { + switch (((XkbAnyEvent*)e)->xkb_type) { + case XkbBellNotify: + window = ((XkbBellNotifyEvent*)e)->window; + default: + window = None; + } + } else +#endif +#ifdef SYNC + if (obt_display_extension_sync && + e->type == obt_display_extension_sync_basep + XSyncAlarmNotify) + { + window = None; + } else +#endif + window = e->xany.window; + } + return window; +} + +static inline Time event_get_timestamp(const XEvent *e) +{ + Time t = CurrentTime; + + /* grab the lasttime and hack up the state */ + switch (e->type) { + case ButtonPress: + case ButtonRelease: + t = e->xbutton.time; + break; + case KeyPress: + t = e->xkey.time; + break; + case KeyRelease: + t = e->xkey.time; + break; + case MotionNotify: + t = e->xmotion.time; + break; + case PropertyNotify: + t = e->xproperty.time; + break; + case EnterNotify: + case LeaveNotify: + t = e->xcrossing.time; + break; + default: +#ifdef SYNC + if (obt_display_extension_sync && + e->type == obt_display_extension_sync_basep + XSyncAlarmNotify) + { + t = ((const XSyncAlarmNotifyEvent*)e)->time; + } +#endif + /* if more event types are anticipated, get their timestamp + explicitly */ + break; + } + + return t; +} + +static void event_set_curtime(XEvent *e) +{ + Time t = event_get_timestamp(e); + + /* watch that if we get an event earlier than the last specified user_time, + which can happen if the clock goes backwards, we erase the last + specified user_time */ + if (t && event_last_user_time && event_time_after(event_last_user_time, t)) + event_reset_user_time(); + + event_sourcetime = CurrentTime; + event_curtime = t; +} + +static void event_hack_mods(XEvent *e) +{ + switch (e->type) { + case ButtonPress: + case ButtonRelease: + e->xbutton.state = obt_keyboard_only_modmasks(e->xbutton.state); + break; + case KeyPress: + break; + case KeyRelease: + break; + case MotionNotify: + e->xmotion.state = obt_keyboard_only_modmasks(e->xmotion.state); + /* compress events */ + { + XEvent ce; + ObtXQueueWindowType wt; + + wt.window = e->xmotion.window; + wt.type = MotionNotify; + while (xqueue_remove_local(&ce, xqueue_match_window_type, &wt)) { + e->xmotion.x = ce.xmotion.x; + e->xmotion.y = ce.xmotion.y; + e->xmotion.x_root = ce.xmotion.x_root; + e->xmotion.y_root = ce.xmotion.y_root; + } + } + break; + } +} + +static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only) +{ + gint mode = e->xfocus.mode; + gint detail = e->xfocus.detail; + Window win = e->xany.window; + + if (e->type == FocusIn) { + /* These are ones we never want.. */ + + /* This means focus was given by a keyboard/mouse grab. */ + if (mode == NotifyGrab) + return FALSE; + /* This means focus was given back from a keyboard/mouse grab. */ + if (mode == NotifyUngrab) + return FALSE; + + /* These are the ones we want.. */ + + if (win == obt_root(ob_screen)) { + /* If looking for a focus in on a client, then always return + FALSE for focus in's to the root window */ + if (in_client_only) + return FALSE; + /* This means focus reverted off of a client */ + else if (detail == NotifyPointerRoot || + detail == NotifyDetailNone || + detail == NotifyInferior || + /* This means focus got here from another screen */ + detail == NotifyNonlinear) + return TRUE; + else + return FALSE; + } + + /* It was on a client, was it a valid one? + It's possible to get a FocusIn event for a client that was managed + but has disappeared. + */ + if (in_client_only) { + ObWindow *w = window_find(e->xfocus.window); + if (!w || !WINDOW_IS_CLIENT(w)) + return FALSE; + } + else { + /* This means focus reverted to parent from the client (this + happens often during iconify animation) */ + if (detail == NotifyInferior) + return TRUE; + } + + /* This means focus moved from the root window to a client */ + if (detail == NotifyVirtual) + return TRUE; + /* This means focus moved from one client to another */ + if (detail == NotifyNonlinearVirtual) + return TRUE; + + /* Otherwise.. */ + return FALSE; + } else { + g_assert(e->type == FocusOut); + + /* These are ones we never want.. */ + + /* This means focus was taken by a keyboard/mouse grab. */ + if (mode == NotifyGrab) + return FALSE; + /* This means focus was grabbed on a window and it was released. */ + if (mode == NotifyUngrab) + return FALSE; + + /* Focus left the root window revertedto state */ + if (win == obt_root(ob_screen)) + return FALSE; + + /* These are the ones we want.. */ + + /* This means focus moved from a client to the root window */ + if (detail == NotifyVirtual) + return TRUE; + /* This means focus moved from one client to another */ + if (detail == NotifyNonlinearVirtual) + return TRUE; + + /* Otherwise.. */ + return FALSE; + } +} + +static gboolean event_look_for_focusin(XEvent *e, gpointer data) +{ + return e->type == FocusIn && wanted_focusevent(e, FALSE); +} + +static gboolean event_look_for_focusin_client(XEvent *e, gpointer data) +{ + return e->type == FocusIn && wanted_focusevent(e, TRUE); +} + +static void print_focusevent(XEvent *e) +{ + gint mode = e->xfocus.mode; + gint detail = e->xfocus.detail; + Window win = e->xany.window; + const gchar *modestr, *detailstr; + + switch (mode) { + case NotifyNormal: modestr="NotifyNormal"; break; + case NotifyGrab: modestr="NotifyGrab"; break; + case NotifyUngrab: modestr="NotifyUngrab"; break; + case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break; + default: g_assert_not_reached(); + } + switch (detail) { + case NotifyAncestor: detailstr="NotifyAncestor"; break; + case NotifyVirtual: detailstr="NotifyVirtual"; break; + case NotifyInferior: detailstr="NotifyInferior"; break; + case NotifyNonlinear: detailstr="NotifyNonlinear"; break; + case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break; + case NotifyPointer: detailstr="NotifyPointer"; break; + case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break; + case NotifyDetailNone: detailstr="NotifyDetailNone"; break; + default: g_assert_not_reached(); + } + + if (mode == NotifyGrab || mode == NotifyUngrab) + return; + + g_assert(modestr); + g_assert(detailstr); + ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s", + (e->xfocus.type == FocusIn ? "In" : "Out"), + win, + modestr, detailstr); + +} + +static void event_process(const XEvent *ec, gpointer data) +{ + XEvent ee, *e; + Window window; + ObClient *client = NULL; + ObDock *dock = NULL; + ObDockApp *dockapp = NULL; + ObWindow *obwin = NULL; + ObMenuFrame *menu = NULL; + ObPrompt *prompt = NULL; + gboolean used; + + /* make a copy we can mangle */ + ee = *ec; + e = ⅇ + + window = event_get_window(e); + if (window == obt_root(ob_screen)) + /* don't do any lookups, waste of cpu */; + else if ((obwin = window_find(window))) { + switch (obwin->type) { + case OB_WINDOW_CLASS_DOCK: + dock = WINDOW_AS_DOCK(obwin); + break; + case OB_WINDOW_CLASS_CLIENT: + client = WINDOW_AS_CLIENT(obwin); + /* events on clients can be events on prompt windows too */ + prompt = client->prompt; + break; + case OB_WINDOW_CLASS_MENUFRAME: + menu = WINDOW_AS_MENUFRAME(obwin); + break; + case OB_WINDOW_CLASS_INTERNAL: + /* we don't do anything with events directly on these windows */ + break; + case OB_WINDOW_CLASS_PROMPT: + prompt = WINDOW_AS_PROMPT(obwin); + break; + } + } + else + dockapp = dock_find_dockapp(window); + + event_set_curtime(e); + event_curserial = e->xany.serial; + event_hack_mods(e); + + /* deal with it in the kernel */ + + if (e->type == FocusIn) { + print_focusevent(e); + if (!wanted_focusevent(e, FALSE)) { + if (waiting_for_focusin) { + /* We were waiting for this FocusIn, since we got a FocusOut + earlier, but it went to a window that isn't a client. */ + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to an unmanaged window 0x%x !", + e->xfocus.window); + focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE); + } + } + else if (client && e->xfocus.detail == NotifyInferior) { + ob_debug_type(OB_DEBUG_FOCUS, "Focus went to the frame window"); + + focus_left_screen = FALSE; + + focus_fallback(FALSE, config_focus_under_mouse, TRUE, TRUE); + + /* We don't get a FocusOut for this case, because it's just moving + from our Inferior up to us. This happens when iconifying a + window with RevertToParent focus */ + frame_adjust_focus(client->frame, FALSE); + /* focus_set_client(NULL) has already been called */ + } + else if (e->xfocus.detail == NotifyPointerRoot || + e->xfocus.detail == NotifyDetailNone || + e->xfocus.detail == NotifyInferior || + e->xfocus.detail == NotifyNonlinear) + { + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to root or pointer root/none"); + + if (e->xfocus.detail == NotifyInferior || + e->xfocus.detail == NotifyNonlinear) + { + focus_left_screen = FALSE; + } + + /* If another FocusIn is in the queue then don't fallback yet. This + fixes the fun case of: + window map -> send focusin + window unmap -> get focusout + window map -> send focusin + get first focus out -> fall back to something (new window + hasn't received focus yet, so something else) -> send focusin + which means the "something else" is the last thing to get a + focusin sent to it, so the new window doesn't end up with focus. + + But if the other focus in is something like PointerRoot then we + still want to fall back. + */ + if (xqueue_exists_local(event_look_for_focusin_client, NULL)) { + ob_debug_type(OB_DEBUG_FOCUS, + " but another FocusIn is coming"); + } else { + /* Focus has been reverted. + + FocusOut events come after UnmapNotify, so we don't need to + worry about focusing an invalid window + */ + + if (!focus_left_screen) + focus_fallback(FALSE, config_focus_under_mouse, + TRUE, TRUE); + } + } + else if (!client) + { + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to a window that is already gone"); + + /* If you send focus to a window and then it disappears, you can + get the FocusIn for it, after it is unmanaged. + Just wait for the next FocusOut/FocusIn pair, but make note that + the window that was focused no longer is. */ + focus_set_client(NULL); + } + else if (client != focus_client) { + focus_left_screen = FALSE; + frame_adjust_focus(client->frame, TRUE); + focus_set_client(client); + client_calc_layer(client); + client_bring_helper_windows(client); + } + + waiting_for_focusin = FALSE; + } else if (e->type == FocusOut) { + print_focusevent(e); + if (!wanted_focusevent(e, FALSE)) + ; /* skip this one */ + /* Look for the followup FocusIn */ + else if (!xqueue_exists_local(event_look_for_focusin, NULL)) { + /* There is no FocusIn, this means focus went to a window that + is not being managed, or a window on another screen. */ + Window win, root; + gint i; + guint u; + obt_display_ignore_errors(TRUE); + if (XGetInputFocus(obt_display, &win, &i) && + XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) && + root != obt_root(ob_screen)) + { + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to another screen !"); + focus_left_screen = TRUE; + } + else + ob_debug_type(OB_DEBUG_FOCUS, + "Focus went to a black hole !"); + obt_display_ignore_errors(FALSE); + /* nothing is focused */ + focus_set_client(NULL); + } else { + /* Focus moved, so mark that we are waiting to process that + FocusIn */ + waiting_for_focusin = TRUE; + + /* nothing is focused right now, but will be again shortly */ + focus_set_client(NULL); + } + + if (client && client != focus_client) + frame_adjust_focus(client->frame, FALSE); + } + else if (client) + event_handle_client(client, e); + else if (dockapp) + event_handle_dockapp(dockapp, e); + else if (dock) + event_handle_dock(dock, e); + else if (menu) + event_handle_menu(menu, e); + else if (window == obt_root(ob_screen)) + event_handle_root(e); + else if (e->type == MapRequest) + window_manage(window); + else if (e->type == MappingNotify) { + /* keyboard layout changes for modifier mapping changes. reload the + modifier map, and rebind all the key bindings as appropriate */ + ob_debug("Keyboard map changed. Reloading keyboard bindings."); + ob_set_state(OB_STATE_RECONFIGURING); + obt_keyboard_reload(); + keyboard_rebind(); + ob_set_state(OB_STATE_RUNNING); + } + else if (e->type == ClientMessage) { + /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for + windows that are not managed yet. */ + if (e->xclient.message_type == + OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS)) + { + /* Pretend to manage the client, getting information used to + determine its decorations */ + ObClient *c = client_fake_manage(e->xclient.window); + gulong vals[4]; + + /* set the frame extents on the window */ + vals[0] = c->frame->size.left; + vals[1] = c->frame->size.right; + vals[2] = c->frame->size.top; + vals[3] = c->frame->size.bottom; + OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS, + CARDINAL, vals, 4); + + /* Free the pretend client */ + client_fake_unmanage(c); + } + } + else if (e->type == ConfigureRequest) { + /* unhandled configure requests must be used to configure the + window directly */ + XWindowChanges xwc; + + xwc.x = e->xconfigurerequest.x; + xwc.y = e->xconfigurerequest.y; + xwc.width = e->xconfigurerequest.width; + xwc.height = e->xconfigurerequest.height; + xwc.border_width = e->xconfigurerequest.border_width; + xwc.sibling = e->xconfigurerequest.above; + xwc.stack_mode = e->xconfigurerequest.detail; + + /* we are not to be held responsible if someone sends us an + invalid request! */ + obt_display_ignore_errors(TRUE); + XConfigureWindow(obt_display, window, + e->xconfigurerequest.value_mask, &xwc); + obt_display_ignore_errors(FALSE); + } +#ifdef SYNC + else if (obt_display_extension_sync && + e->type == obt_display_extension_sync_basep + XSyncAlarmNotify) + { + XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e; + if (se->alarm == moveresize_alarm && moveresize_in_progress) + moveresize_event(e); + } +#endif + + if (e->type == ButtonPress || e->type == ButtonRelease) { + ObWindow *w; + static guint pressed = 0; + static Window pressed_win = None; + + event_sourcetime = event_curtime; + + /* If the button press was on some non-root window, or was physically + on the root window... */ + if (window != obt_root(ob_screen) || + e->xbutton.subwindow == None || + /* ...or if it is related to the last button press we handled... */ + pressed == e->xbutton.button || + /* ...or it if it was physically on an openbox + internal window... */ + ((w = window_find(e->xbutton.subwindow)) && + (WINDOW_IS_INTERNAL(w) || WINDOW_IS_DOCK(w)))) + /* ...then process the event, otherwise ignore it */ + { + used = event_handle_user_input(client, e); + + if (prompt && !used) + used = event_handle_prompt(prompt, e); + + if (e->type == ButtonPress) { + pressed = e->xbutton.button; + pressed_win = e->xbutton.subwindow; + } + } + } + else if (e->type == KeyPress || e->type == KeyRelease || + e->type == MotionNotify) + { + event_sourcetime = event_curtime; + + used = event_handle_user_input(client, e); + + if (prompt && !used) + used = event_handle_prompt(prompt, e); + } + + /* show any debug prompts that are queued */ + ob_debug_show_prompts(); + + /* if something happens and it's not from an XEvent, then we don't know + the time, so clear it here until the next event is handled */ + event_curtime = event_sourcetime = CurrentTime; + event_curserial = 0; +} + +static void event_handle_root(XEvent *e) +{ + Atom msgtype; + + switch(e->type) { + case SelectionClear: + ob_debug("Another WM has requested to replace us. Exiting."); + ob_exit_replace(); + break; + + case ClientMessage: + if (e->xclient.format != 32) break; + + msgtype = e->xclient.message_type; + if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) { + guint d = e->xclient.data.l[0]; + if (d < screen_num_desktops) { + if (e->xclient.data.l[1] == 0) + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_CURRENT_DESKTOP message is missing " + "a timestamp"); + else + event_sourcetime = e->xclient.data.l[1]; + screen_set_desktop(d, TRUE); + } + } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) { + guint d = e->xclient.data.l[0]; + if (d > 0 && d <= 1000) + screen_set_num_desktops(d); + } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) { + screen_show_desktop(e->xclient.data.l[0] != 0, NULL); + } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) { + ob_debug("OB_CONTROL: %d", e->xclient.data.l[0]); + if (e->xclient.data.l[0] == 1) + ob_reconfigure(); + else if (e->xclient.data.l[0] == 2) + ob_restart(); + else if (e->xclient.data.l[0] == 3) + ob_exit(0); + } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) { + if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING)) + ping_got_pong(e->xclient.data.l[1]); + } + break; + case PropertyNotify: + if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) { + ob_debug("UPDATE DESKTOP NAMES"); + screen_update_desktop_names(); + } + else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT)) + screen_update_layout(); + break; + case ConfigureNotify: +#ifdef XRANDR + XRRUpdateConfiguration(e); +#endif + screen_resize(); + break; + default: + ; + } +} + +void event_enter_client(ObClient *client) +{ + g_assert(config_focus_follow); + + if (is_enter_focus_event_ignored(event_curserial)) { + ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n" + "on client 0x%x", event_curserial, client->window); + return; + } + + if (client_enter_focusable(client) && client_can_focus(client)) { + if (config_focus_delay) { + ObFocusDelayData *data; + + if (focus_delay_timeout_id) + g_source_remove(focus_delay_timeout_id); + + data = g_slice_new(ObFocusDelayData); + data->client = client; + data->time = event_time(); + data->serial = event_curserial; + + focus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + config_focus_delay, + focus_delay_func, + data, + focus_delay_dest); + focus_delay_timeout_client = client; + } else { + ObFocusDelayData data; + data.client = client; + data.time = event_time(); + data.serial = event_curserial; + focus_delay_func(&data); + } + } +} + +void event_leave_client(ObClient *client) +{ + g_assert(config_focus_follow); + + if (is_enter_focus_event_ignored(event_curserial)) { + ob_debug_type(OB_DEBUG_FOCUS, "Ignoring leave event with serial %lu\n" + "on client 0x%x", event_curserial, client->window); + return; + } + + if (client == focus_client) { + if (config_focus_delay) { + ObFocusDelayData *data; + + if (unfocus_delay_timeout_id) + g_source_remove(unfocus_delay_timeout_id); + + data = g_slice_new(ObFocusDelayData); + data->client = client; + data->time = event_time(); + data->serial = event_curserial; + + unfocus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + config_focus_delay, + unfocus_delay_func, + data, + unfocus_delay_dest); + unfocus_delay_timeout_client = client; + } else { + ObFocusDelayData data; + data.client = client; + data.time = event_time(); + data.serial = event_curserial; + unfocus_delay_func(&data); + } + } +} + +static gboolean *context_to_button(ObFrame *f, ObFrameContext con, gboolean press) +{ + if (press) { + switch (con) { + case OB_FRAME_CONTEXT_MAXIMIZE: + return &f->max_press; + case OB_FRAME_CONTEXT_CLOSE: + return &f->close_press; + case OB_FRAME_CONTEXT_ICONIFY: + return &f->iconify_press; + case OB_FRAME_CONTEXT_ALLDESKTOPS: + return &f->desk_press; + case OB_FRAME_CONTEXT_SHADE: + return &f->shade_press; + default: + return NULL; + } + } else { + switch (con) { + case OB_FRAME_CONTEXT_MAXIMIZE: + return &f->max_hover; + case OB_FRAME_CONTEXT_CLOSE: + return &f->close_hover; + case OB_FRAME_CONTEXT_ICONIFY: + return &f->iconify_hover; + case OB_FRAME_CONTEXT_ALLDESKTOPS: + return &f->desk_hover; + case OB_FRAME_CONTEXT_SHADE: + return &f->shade_hover; + default: + return NULL; + } + } +} + +static gboolean more_client_message_event(Window window, Atom msgtype) +{ + ObtXQueueWindowMessage wm; + wm.window = window; + wm.message = msgtype; + return xqueue_exists_local(xqueue_match_window_message, &wm); +} + +struct ObSkipPropertyChange { + Window window; + Atom prop; +}; + +static gboolean skip_property_change(XEvent *e, gpointer data) +{ + const struct ObSkipPropertyChange s = *(struct ObSkipPropertyChange*)data; + + if (e->type == PropertyNotify && e->xproperty.window == s.window) { + const Atom a = e->xproperty.atom; + const Atom b = s.prop; + + /* these are all updated together */ + if ((a == OBT_PROP_ATOM(NET_WM_NAME) || + a == OBT_PROP_ATOM(WM_NAME) || + a == OBT_PROP_ATOM(NET_WM_ICON_NAME) || + a == OBT_PROP_ATOM(WM_ICON_NAME)) + && + (b == OBT_PROP_ATOM(NET_WM_NAME) || + b == OBT_PROP_ATOM(WM_NAME) || + b == OBT_PROP_ATOM(NET_WM_ICON_NAME) || + b == OBT_PROP_ATOM(WM_ICON_NAME))) + { + return TRUE; + } + else if (a == b && a == OBT_PROP_ATOM(NET_WM_ICON)) + return TRUE; + } + return FALSE; +} + +static void event_handle_client(ObClient *client, XEvent *e) +{ + Atom msgtype; + ObFrameContext con; + gboolean *but; + static gint px = -1, py = -1; + static guint pb = 0; + static ObFrameContext pcon = OB_FRAME_CONTEXT_NONE; + + switch (e->type) { + case ButtonPress: + /* save where the press occured for the first button pressed */ + if (!pb) { + pb = e->xbutton.button; + px = e->xbutton.x; + py = e->xbutton.y; + + pcon = frame_context(client, e->xbutton.window, px, py); + pcon = mouse_button_frame_context(pcon, e->xbutton.button, + e->xbutton.state); + } + case ButtonRelease: + /* Wheel buttons don't draw because they are an instant click, so it + is a waste of resources to go drawing it. + if the user is doing an interactive thing, or has a menu open then + the mouse is grabbed (possibly) and if we get these events we don't + want to deal with them + */ + if (!(e->xbutton.button == 4 || e->xbutton.button == 5) && + !grab_on_keyboard()) + { + /* use where the press occured */ + con = frame_context(client, e->xbutton.window, px, py); + con = mouse_button_frame_context(con, e->xbutton.button, + e->xbutton.state); + + /* button presses on CLIENT_CONTEXTs are not accompanied by a + release because they are Replayed to the client */ + if ((e->type == ButtonRelease || CLIENT_CONTEXT(con, client)) && + e->xbutton.button == pb) + pb = 0, px = py = -1, pcon = OB_FRAME_CONTEXT_NONE; + + but = context_to_button(client->frame, con, TRUE); + if (but) { + *but = (e->type == ButtonPress); + frame_adjust_state(client->frame); + } + } + break; + case MotionNotify: + /* when there is a grab on the pointer, we won't get enter/leave + notifies, but we still get motion events */ + if (grab_on_pointer()) break; + + con = frame_context(client, e->xmotion.window, + e->xmotion.x, e->xmotion.y); + switch (con) { + case OB_FRAME_CONTEXT_TITLEBAR: + case OB_FRAME_CONTEXT_TLCORNER: + case OB_FRAME_CONTEXT_TRCORNER: + /* we've left the button area inside the titlebar */ + if (client->frame->max_hover || client->frame->desk_hover || + client->frame->shade_hover || client->frame->iconify_hover || + client->frame->close_hover) + { + client->frame->max_hover = + client->frame->desk_hover = + client->frame->shade_hover = + client->frame->iconify_hover = + client->frame->close_hover = FALSE; + frame_adjust_state(client->frame); + } + break; + default: + but = context_to_button(client->frame, con, FALSE); + if (but && !*but && !pb) { + *but = TRUE; + frame_adjust_state(client->frame); + } + break; + } + break; + case LeaveNotify: + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); + switch (con) { + case OB_FRAME_CONTEXT_TITLEBAR: + case OB_FRAME_CONTEXT_TLCORNER: + case OB_FRAME_CONTEXT_TRCORNER: + /* we've left the button area inside the titlebar */ + client->frame->max_hover = + client->frame->desk_hover = + client->frame->shade_hover = + client->frame->iconify_hover = + client->frame->close_hover = FALSE; + if (e->xcrossing.mode == NotifyGrab) { + client->frame->max_press = + client->frame->desk_press = + client->frame->shade_press = + client->frame->iconify_press = + client->frame->close_press = FALSE; + } + break; + case OB_FRAME_CONTEXT_FRAME: + /* When the mouse leaves an animating window, don't use the + corresponding enter events. Pretend like the animating window + doesn't even exist..! */ + if (frame_iconify_animating(client->frame)) + event_end_ignore_all_enters(event_start_ignore_all_enters()); + + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d on %lx", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, (client?client->window:0)); + if (grab_on_keyboard()) + break; + if (config_focus_follow && + /* leave inferior events can happen when the mouse goes onto + the window's border and then into the window before the + delay is up */ + e->xcrossing.detail != NotifyInferior) + { + if (config_focus_delay && focus_delay_timeout_id) + g_source_remove(focus_delay_timeout_id); + if (config_unfocus_leave) + event_leave_client(client); + } + break; + default: + but = context_to_button(client->frame, con, FALSE); + if (but) { + *but = FALSE; + if (e->xcrossing.mode == NotifyGrab) { + but = context_to_button(client->frame, con, TRUE); + *but = FALSE; + } + frame_adjust_state(client->frame); + } + break; + } + break; + case EnterNotify: + { + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); + switch (con) { + case OB_FRAME_CONTEXT_FRAME: + if (grab_on_keyboard()) + break; + if (e->xcrossing.mode == NotifyGrab || + (e->xcrossing.mode == NotifyUngrab && + /* ungrab enters are used when _under_ mouse is being used */ + !(config_focus_follow && config_focus_under_mouse)) || + /*ignore enters when we're already in the window */ + e->xcrossing.detail == NotifyInferior) + { + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d serial %lu on %lx " + "IGNORED", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, + e->xcrossing.serial, + client?client->window:0); + } + else { + ob_debug_type(OB_DEBUG_FOCUS, + "%sNotify mode %d detail %d serial %lu on %lx, " + "focusing window", + (e->type == EnterNotify ? "Enter" : "Leave"), + e->xcrossing.mode, + e->xcrossing.detail, + e->xcrossing.serial, + (client?client->window:0)); + if (config_focus_follow) { + if (config_focus_delay && unfocus_delay_timeout_id) + g_source_remove(unfocus_delay_timeout_id); + event_enter_client(client); + } + } + break; + default: + but = context_to_button(client->frame, con, FALSE); + if (but) { + *but = TRUE; + if (e->xcrossing.mode == NotifyUngrab) { + but = context_to_button(client->frame, con, TRUE); + *but = (con == pcon); + } + frame_adjust_state(client->frame); + } + break; + } + break; + } + case ConfigureRequest: + { + /* dont compress these unless you're going to watch for property + notifies in between (these can change what the configure would + do to the window). + also you can't compress stacking events + */ + + gint x, y, w, h; + gboolean move = FALSE; + gboolean resize = FALSE; + + /* get the current area */ + RECT_TO_DIMS(client->area, x, y, w, h); + + ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d " + "visible %d", + client->title, + screen_desktop, client->wmstate, client->frame->visible); + ob_debug(" x %d y %d w %d h %d b %d", + x, y, w, h, client->border_width); + + if (e->xconfigurerequest.value_mask & CWBorderWidth) + if (client->border_width != e->xconfigurerequest.border_width) { + client->border_width = e->xconfigurerequest.border_width; + + /* if the border width is changing then that is the same + as requesting a resize, but we don't actually change + the client's border, so it will change their root + coordinates (since they include the border width) and + we need to a notify then */ + move = TRUE; + } + + if (e->xconfigurerequest.value_mask & CWStackMode) { + ObClient *sibling = NULL; + gulong ignore_start; + gboolean ok = TRUE; + + /* get the sibling */ + if (e->xconfigurerequest.value_mask & CWSibling) { + ObWindow *win; + win = window_find(e->xconfigurerequest.above); + if (win && WINDOW_IS_CLIENT(win) && + WINDOW_AS_CLIENT(win) != client) + { + sibling = WINDOW_AS_CLIENT(win); + } + else + /* an invalid sibling was specified so don't restack at + all, it won't make sense no matter what we do */ + ok = FALSE; + } + + if (ok) { + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + stacking_restack_request(client, sibling, + e->xconfigurerequest.detail); + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + } + + /* a stacking change moves the window without resizing */ + move = TRUE; + } + + if ((e->xconfigurerequest.value_mask & CWX) || + (e->xconfigurerequest.value_mask & CWY) || + (e->xconfigurerequest.value_mask & CWWidth) || + (e->xconfigurerequest.value_mask & CWHeight)) + { + /* don't allow clients to move shaded windows (fvwm does this) + */ + if (e->xconfigurerequest.value_mask & CWX) { + if (!client->shaded) + x = e->xconfigurerequest.x; + move = TRUE; + } + if (e->xconfigurerequest.value_mask & CWY) { + if (!client->shaded) + y = e->xconfigurerequest.y; + move = TRUE; + } + + if (e->xconfigurerequest.value_mask & CWWidth) { + w = e->xconfigurerequest.width; + resize = TRUE; + } + if (e->xconfigurerequest.value_mask & CWHeight) { + h = e->xconfigurerequest.height; + resize = TRUE; + } + } + + ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d " + "move %d resize %d", + e->xconfigurerequest.value_mask & CWX, x, + e->xconfigurerequest.value_mask & CWY, y, + e->xconfigurerequest.value_mask & CWWidth, w, + e->xconfigurerequest.value_mask & CWHeight, h, + move, resize); + + /* check for broken apps moving to their root position + + XXX remove this some day...that would be nice. right now all + kde apps do this when they try activate themselves on another + desktop. eg. open amarok window on desktop 1, switch to desktop + 2, click amarok tray icon. it will move by its decoration size. + */ + if (x != client->area.x && + x == (client->frame->area.x + client->frame->size.left - + (gint)client->border_width) && + y != client->area.y && + y == (client->frame->area.y + client->frame->size.top - + (gint)client->border_width) && + w == client->area.width && + h == client->area.height) + { + ob_debug_type(OB_DEBUG_APP_BUGS, + "Application %s is trying to move via " + "ConfigureRequest to it's root window position " + "but it is not using StaticGravity", + client->title); + /* don't move it */ + x = client->area.x; + y = client->area.y; + + /* they still requested a move, so don't change whether a + notify is sent or not */ + } + + /* check for broken apps (java swing) moving to 0,0 when there is a + strut there. + + XXX remove this some day...that would be nice. but really unexpected + from Sun Microsystems. + */ + if (x == 0 && y == 0 && client->gravity == NorthWestGravity && + client_normal(client)) + { + const Rect to = { x, y, w, h }; + + /* oldschool fullscreen windows are allowed */ + if (!client_is_oldfullscreen(client, &to)) { + Rect *r; + + r = screen_area(client->desktop, SCREEN_AREA_ALL_MONITORS, + NULL); + if (r->x || r->y) { + /* move the window only to the corner outside struts */ + x = r->x; + y = r->y; + + ob_debug_type(OB_DEBUG_APP_BUGS, + "Application %s is trying to move via " + "ConfigureRequest to 0,0 using " + "NorthWestGravity, while there is a " + "strut there. " + "Moving buggy app from (0,0) to (%d,%d)", + client->title, r->x, r->y); + } + + g_slice_free(Rect, r); + + /* they still requested a move, so don't change whether a + notify is sent or not */ + } + } + + { + gint lw, lh; + + client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE); + + /* if x was not given, then use gravity to figure out the new + x. the reference point should not be moved */ + if ((e->xconfigurerequest.value_mask & CWWidth && + !(e->xconfigurerequest.value_mask & CWX))) + client_gravity_resize_w(client, &x, client->area.width, w); + /* same for y */ + if ((e->xconfigurerequest.value_mask & CWHeight && + !(e->xconfigurerequest.value_mask & CWY))) + client_gravity_resize_h(client, &y, client->area.height,h); + + client_find_onscreen(client, &x, &y, w, h, FALSE); + + ob_debug("Granting ConfigureRequest x %d y %d w %d h %d", + x, y, w, h); + client_configure(client, x, y, w, h, FALSE, TRUE, TRUE); + } + break; + } + case UnmapNotify: + ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d " + "ignores left %d", + client->window, e->xunmap.event, e->xunmap.from_configure, + client->ignore_unmaps); + if (client->ignore_unmaps) { + client->ignore_unmaps--; + break; + } + client_unmanage(client); + break; + case DestroyNotify: + ob_debug("DestroyNotify for window 0x%x", client->window); + client_unmanage(client); + break; + case ReparentNotify: + /* this is when the client is first taken captive in the frame */ + if (e->xreparent.parent == client->frame->window) break; + + /* + This event is quite rare and is usually handled in unmapHandler. + However, if the window is unmapped when the reparent event occurs, + the window manager never sees it because an unmap event is not sent + to an already unmapped window. + */ + + ob_debug("ReparentNotify for window 0x%x", client->window); + client_unmanage(client); + break; + case MapRequest: + ob_debug("MapRequest for 0x%lx", client->window); + if (!client->iconic) break; /* this normally doesn't happen, but if it + does, we don't want it! + it can happen now when the window is on + another desktop, but we still don't + want it! */ + client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE); + break; + case ClientMessage: + /* validate cuz we query stuff off the client here */ + if (!client_validate(client)) break; + + if (e->xclient.format != 32) return; + + msgtype = e->xclient.message_type; + if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) { + if (!more_client_message_event(client->window, msgtype)) + client_set_wm_state(client, e->xclient.data.l[0]); + } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) { + if (!more_client_message_event(client->window, msgtype) && + ((unsigned)e->xclient.data.l[0] < screen_num_desktops || + (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)) + { + client_set_desktop(client, (unsigned)e->xclient.data.l[0], + FALSE, FALSE); + } + } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) { + gulong ignore_start; + + /* can't compress these */ + ob_debug("net_wm_state %s %ld %ld for 0x%lx", + (e->xclient.data.l[0] == 0 ? "Remove" : + e->xclient.data.l[0] == 1 ? "Add" : + e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"), + e->xclient.data.l[1], e->xclient.data.l[2], + client->window); + + /* ignore enter events caused by these like ob actions do */ + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + client_set_state(client, e->xclient.data.l[0], + e->xclient.data.l[1], e->xclient.data.l[2]); + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) { + ob_debug("net_close_window for 0x%lx", client->window); + client_close(client); + } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) { + ob_debug("net_active_window for 0x%lx source=%s", + client->window, + (e->xclient.data.l[0] == 0 ? "unknown" : + (e->xclient.data.l[0] == 1 ? "application" : + (e->xclient.data.l[0] == 2 ? "user" : "INVALID")))); + /* XXX make use of data.l[2] !? */ + if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) { + /* we can not trust the timestamp from applications. + e.g. chromium passes a very old timestamp. openbox thinks + the window will get focus and calls XSetInputFocus with the + (old) timestamp, which doesn't end up moving focus at all. + but the window is raised, not hilited, etc, as if it was + really going to get focus. + + so do not use this timestamp in event_curtime, as this would + be used in XSetInputFocus. + */ + event_sourcetime = e->xclient.data.l[1]; + if (e->xclient.data.l[1] == 0) + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_ACTIVE_WINDOW message for window %s is" + " missing a timestamp", client->title); + } else + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_ACTIVE_WINDOW message for window %s is " + "missing source indication", client->title); + /* TODO(danakj) This should use + (e->xclient.data.l[0] == 0 || + e->xclient.data.l[0] == 2) + to determine if a user requested the activation, however GTK+ + applications seem unable to make this distinction ever + (including panels such as xfce4-panel and gnome-panel). + So we are left just assuming all activations are from the user. + */ + client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE); + } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) { + ob_debug("net_wm_moveresize for 0x%lx direction %d", + client->window, e->xclient.data.l[2]); + if ((Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) || + (Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD)) + { + moveresize_start(client, e->xclient.data.l[0], + e->xclient.data.l[1], e->xclient.data.l[3], + e->xclient.data.l[2]); + } + else if ((Atom)e->xclient.data.l[2] == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL)) + moveresize_end(TRUE); + } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) { + gint ograv, x, y, w, h; + + ograv = client->gravity; + + if (e->xclient.data.l[0] & 0xff) + client->gravity = e->xclient.data.l[0] & 0xff; + + if (e->xclient.data.l[0] & 1 << 8) + x = e->xclient.data.l[1]; + else + x = client->area.x; + if (e->xclient.data.l[0] & 1 << 9) + y = e->xclient.data.l[2]; + else + y = client->area.y; + + if (e->xclient.data.l[0] & 1 << 10) { + w = e->xclient.data.l[3]; + + /* if x was not given, then use gravity to figure out the new + x. the reference point should not be moved */ + if (!(e->xclient.data.l[0] & 1 << 8)) + client_gravity_resize_w(client, &x, client->area.width, w); + } + else + w = client->area.width; + + if (e->xclient.data.l[0] & 1 << 11) { + h = e->xclient.data.l[4]; + + /* same for y */ + if (!(e->xclient.data.l[0] & 1 << 9)) + client_gravity_resize_h(client, &y, client->area.height,h); + } + else + h = client->area.height; + + ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)", + e->xclient.data.l[0] & 1 << 8, x, + e->xclient.data.l[0] & 1 << 9, y, + client->gravity); + + client_find_onscreen(client, &x, &y, w, h, FALSE); + + client_configure(client, x, y, w, h, FALSE, TRUE, FALSE); + + client->gravity = ograv; + } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) { + if (e->xclient.data.l[0] != 2) { + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s with " + "invalid source indication %ld", + client->title, e->xclient.data.l[0]); + } else { + ObClient *sibling = NULL; + if (e->xclient.data.l[1]) { + ObWindow *win = window_find(e->xclient.data.l[1]); + if (WINDOW_IS_CLIENT(win) && + WINDOW_AS_CLIENT(win) != client) + { + sibling = WINDOW_AS_CLIENT(win); + } + if (sibling == NULL) + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s " + "with invalid sibling 0x%x", + client->title, e->xclient.data.l[1]); + } + if (e->xclient.data.l[2] == Below || + e->xclient.data.l[2] == BottomIf || + e->xclient.data.l[2] == Above || + e->xclient.data.l[2] == TopIf || + e->xclient.data.l[2] == Opposite) + { + gulong ignore_start; + + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + /* just raise, don't activate */ + stacking_restack_request(client, sibling, + e->xclient.data.l[2]); + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); + + /* send a synthetic ConfigureNotify, cuz this is supposed + to be like a ConfigureRequest. */ + client_reconfigure(client, TRUE); + } else + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s " + "with invalid detail %d", + client->title, e->xclient.data.l[2]); + } + } + break; + case PropertyNotify: + /* validate cuz we query stuff off the client here */ + if (!client_validate(client)) break; + + msgtype = e->xproperty.atom; + + /* ignore changes to some properties if there is another change + coming in the queue */ + { + struct ObSkipPropertyChange s; + s.window = client->window; + s.prop = msgtype; + if (xqueue_exists_local(skip_property_change, &s)) + break; + } + + msgtype = e->xproperty.atom; + if (msgtype == XA_WM_NORMAL_HINTS) { + int x, y, w, h, lw, lh; + + ob_debug("Update NORMAL hints"); + client_update_normal_hints(client); + /* normal hints can make a window non-resizable */ + client_setup_decor_and_functions(client, FALSE); + + x = client->area.x; + y = client->area.y; + w = client->area.width; + h = client->area.height; + + /* apply the new normal hints */ + client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE); + /* make sure the window is visible, and if the window is resized + off-screen due to the normal hints changing then this will push + it back onto the screen. */ + client_find_onscreen(client, &x, &y, w, h, FALSE); + + /* make sure the client's sizes are within its bounds, but don't + make it reply with a configurenotify unless something changed. + emacs will update its normal hints every time it receives a + configurenotify */ + client_configure(client, x, y, w, h, FALSE, TRUE, FALSE); + } else if (msgtype == OBT_PROP_ATOM(MOTIF_WM_HINTS)) { + client_get_mwm_hints(client); + /* This can override some mwm hints */ + client_get_type_and_transientness(client); + + /* Apply the changes to the window */ + client_setup_decor_and_functions(client, TRUE); + } else if (msgtype == XA_WM_HINTS) { + client_update_wmhints(client); + } else if (msgtype == XA_WM_TRANSIENT_FOR) { + /* get the transient-ness first, as this affects if the client + decides to be transient for the group or not in + client_update_transient_for() */ + client_get_type_and_transientness(client); + client_update_transient_for(client); + /* type may have changed, so update the layer */ + client_calc_layer(client); + client_setup_decor_and_functions(client, TRUE); + } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) || + msgtype == OBT_PROP_ATOM(WM_NAME) || + msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) || + msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) { + client_update_title(client); + } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) { + client_update_protocols(client); + } + else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT) || + msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) { + client_update_strut(client); + } + else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) { + client_update_icons(client); + } + else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) { + client_update_icon_geometry(client); + } + else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) { + guint32 t; + if (client == focus_client && + OBT_PROP_GET32(client->window, NET_WM_USER_TIME, CARDINAL, &t) + && t && !event_time_after(t, e->xproperty.time) && + (!event_last_user_time || + event_time_after(t, event_last_user_time))) + { + event_last_user_time = t; + } + } + else if (msgtype == OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY)) { + client_update_opacity(client); + } +#ifdef SYNC + else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) { + /* if they are resizing right now this would cause weird behaviour. + if one day a user reports clients stop resizing, then handle + this better by resetting a new XSync alarm and stuff on the + new counter, but I expect it will never happen */ + if (moveresize_client == client) + moveresize_end(FALSE); + client_update_sync_request_counter(client); + } +#endif + break; + case ColormapNotify: + client_update_colormap(client, e->xcolormap.colormap); + break; + default: + ; +#ifdef SHAPE + { + int kind; + if (obt_display_extension_shape && + e->type == obt_display_extension_shape_basep) + { + switch (((XShapeEvent*)e)->kind) { + case ShapeBounding: + case ShapeClip: + client->shaped = ((XShapeEvent*)e)->shaped; + kind = ShapeBounding; + break; +#ifdef ShapeInput + case ShapeInput: + client->shaped_input = ((XShapeEvent*)e)->shaped; + kind = ShapeInput; + break; +#endif + default: + g_assert_not_reached(); + } + frame_adjust_shape_kind(client->frame, kind); + } + } +#endif + } +} + +static void event_handle_dock(ObDock *s, XEvent *e) +{ + switch (e->type) { + case EnterNotify: + dock_hide(FALSE); + break; + case LeaveNotify: + /* don't hide when moving into a dock app */ + if (e->xcrossing.detail != NotifyInferior) + dock_hide(TRUE); + break; + } +} + +static void event_handle_dockapp(ObDockApp *app, XEvent *e) +{ + switch (e->type) { + case MotionNotify: + dock_app_drag(app, &e->xmotion); + break; + case UnmapNotify: + if (app->ignore_unmaps) { + app->ignore_unmaps--; + break; + } + dock_unmanage(app, TRUE); + break; + case DestroyNotify: + case ReparentNotify: + dock_unmanage(app, FALSE); + break; + case ConfigureNotify: + dock_app_configure(app, e->xconfigure.width, e->xconfigure.height); + break; + } +} + +static ObMenuFrame* find_active_menu(void) +{ + GList *it; + ObMenuFrame *ret = NULL; + + for (it = menu_frame_visible; it; it = g_list_next(it)) { + ret = it->data; + if (ret->selected) + break; + ret = NULL; + } + return ret; +} + +static ObMenuFrame* find_active_or_last_menu(void) +{ + ObMenuFrame *ret = NULL; + + ret = find_active_menu(); + if (!ret && menu_frame_visible) + ret = menu_frame_visible->data; + return ret; +} + +static gboolean event_handle_prompt(ObPrompt *p, XEvent *e) +{ + switch (e->type) { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + return prompt_mouse_event(p, e); + break; + case KeyPress: + return prompt_key_event(p, e); + break; + } + return FALSE; +} + +static gboolean event_handle_menu_input(XEvent *ev) +{ + gboolean ret = FALSE; + + if (ev->type == ButtonRelease || ev->type == ButtonPress) { + ObMenuEntryFrame *e; + + if ((ev->xbutton.button < 4 || ev->xbutton.button > 5) && + ((ev->type == ButtonRelease && menu_hide_delay_reached()) || + ev->type == ButtonPress)) + { + if ((e = menu_entry_frame_under(ev->xbutton.x_root, + ev->xbutton.y_root))) + { + if (ev->type == ButtonPress && e->frame->child) + menu_frame_select(e->frame->child, NULL, TRUE); + menu_frame_select(e->frame, e, TRUE); + if (ev->type == ButtonRelease) + menu_entry_frame_execute(e, ev->xbutton.state); + } + else + menu_frame_hide_all(); + } + ret = TRUE; + } + else if (ev->type == KeyPress || ev->type == KeyRelease) { + guint mods; + ObMenuFrame *frame; + + /* get the modifiers */ + mods = obt_keyboard_only_modmasks(ev->xkey.state); + + frame = find_active_or_last_menu(); + if (frame == NULL) + g_assert_not_reached(); /* there is no active menu */ + + /* Allow control while going thru the menu */ + else if (ev->type == KeyPress && (mods & ~ControlMask) == 0) { + gunichar unikey; + KeySym sym; + + frame->got_press = TRUE; + frame->press_keycode = ev->xkey.keycode; + frame->press_doexec = FALSE; + + sym = obt_keyboard_keypress_to_keysym(ev); + + if (sym == XK_Escape) { + menu_frame_hide_all(); + ret = TRUE; + } + + else if (sym == XK_Left) { + /* Left goes to the parent menu */ + if (frame->parent) { + /* remove focus from the child */ + menu_frame_select(frame, NULL, TRUE); + /* and put it in the parent */ + menu_frame_select(frame->parent, frame->parent->selected, + TRUE); + } + ret = TRUE; + } + + else if (sym == XK_Right || sym == XK_Return || sym == XK_KP_Enter) + { + /* Right and enter goes to the selected submenu. + Enter executes instead if it's not on a submenu. */ + + if (frame->selected) { + const ObMenuEntryType t = frame->selected->entry->type; + + if (t == OB_MENU_ENTRY_TYPE_SUBMENU) { + /* make sure it is visible */ + menu_frame_select(frame, frame->selected, TRUE); + /* move focus to the child menu */ + menu_frame_select_next(frame->child); + } + else if (sym != XK_Right) { + frame->press_doexec = TRUE; + } + } + ret = TRUE; + } + + else if (sym == XK_Up) { + menu_frame_select_previous(frame); + ret = TRUE; + } + + else if (sym == XK_Down) { + menu_frame_select_next(frame); + ret = TRUE; + } + + else if (sym == XK_Home) { + menu_frame_select_first(frame); + ret = TRUE; + } + + else if (sym == XK_End) { + menu_frame_select_last(frame); + ret = TRUE; + } + + /* keyboard accelerator shortcuts. (if it was a valid key) */ + else if (frame->entries && + (unikey = + obt_keyboard_keypress_to_unichar(menu_frame_ic(frame), + ev))) + { + GList *start; + GList *it; + ObMenuEntryFrame *found = NULL; + guint num_found = 0; + + /* start after the selected one */ + start = frame->entries; + if (frame->selected) { + for (it = start; frame->selected != it->data; + it = g_list_next(it)) + g_assert(it != NULL); /* nothing was selected? */ + /* next with wraparound */ + start = g_list_next(it); + if (start == NULL) start = frame->entries; + } + + it = start; + do { + ObMenuEntryFrame *e = it->data; + gunichar entrykey = 0; + + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) + entrykey = e->entry->data.normal.shortcut; + else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) + entrykey = e->entry->data.submenu.submenu->shortcut; + + if (unikey == entrykey) { + if (found == NULL) found = e; + ++num_found; + } + + /* next with wraparound */ + it = g_list_next(it); + if (it == NULL) it = frame->entries; + } while (it != start); + + if (found) { + menu_frame_select(frame, found, TRUE); + + if (num_found == 1) { + if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + /* move focus to the child menu */ + menu_frame_select_next(frame->child); + } + else { + frame->press_doexec = TRUE; + } + } + ret = TRUE; + } + } + } + + /* Use KeyRelease events for running things so that the key release + doesn't get sent to the focused application. + + Allow ControlMask only, and don't bother if the menu is empty */ + else if (ev->type == KeyRelease && (mods & ~ControlMask) == 0) { + if (frame->press_keycode == ev->xkey.keycode && + frame->got_press && + frame->press_doexec) + { + if (frame->selected) + menu_entry_frame_execute(frame->selected, ev->xkey.state); + } + } + } + + return ret; +} + +static gboolean event_look_for_menu_enter(XEvent *ev, gpointer data) +{ + const ObMenuFrame *f = (ObMenuFrame*)data; + ObMenuEntryFrame *e; + return ev->type == EnterNotify && + (e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) && + e->frame == f && !e->ignore_enters; +} + +static void event_handle_menu(ObMenuFrame *frame, XEvent *ev) +{ + ObMenuFrame *f; + ObMenuEntryFrame *e; + + switch (ev->type) { + case EnterNotify: + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) { + if (e->ignore_enters) + --e->ignore_enters; + else if (!(f = find_active_menu()) || + f == e->frame || + f->parent == e->frame || + f->child == e->frame) + menu_frame_select(e->frame, e, FALSE); + } + break; + case LeaveNotify: + /*ignore leaves when we're already in the window */ + if (ev->xcrossing.detail == NotifyInferior) + break; + + if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) { + /* check if an EnterNotify event is coming, and if not, then select + nothing in the menu */ + if (!xqueue_exists_local(event_look_for_menu_enter, e->frame)) + menu_frame_select(e->frame, NULL, FALSE); + } + break; + } +} + +static gboolean event_handle_user_input(ObClient *client, XEvent *e) +{ + g_assert(e->type == ButtonPress || e->type == ButtonRelease || + e->type == MotionNotify || e->type == KeyPress || + e->type == KeyRelease); + + if (menu_frame_visible) { + if (event_handle_menu_input(e)) + /* don't use the event if the menu used it, but if the menu + didn't use it and it's a keypress that is bound, it will + close the menu and be used */ + return TRUE; + } + + /* if the keyboard interactive action uses the event then dont + use it for bindings. likewise is moveresize uses the event. */ + if (actions_interactive_input_event(e) || moveresize_event(e)) + return TRUE; + + if (moveresize_in_progress) + /* make further actions work on the client being + moved/resized */ + client = moveresize_client; + + if (e->type == ButtonPress || + e->type == ButtonRelease || + e->type == MotionNotify) + { + /* the frame may not be "visible" but they can still click on it + in the case where it is animating before disappearing */ + if (!client || !frame_iconify_animating(client->frame)) + return mouse_event(client, e); + } else + return keyboard_event((focus_cycle_target ? focus_cycle_target : + (client ? client : focus_client)), e); + + return FALSE; +} + +static void focus_delay_dest(gpointer data) +{ + g_slice_free(ObFocusDelayData, data); + focus_delay_timeout_id = 0; + focus_delay_timeout_client = NULL; +} + +static void unfocus_delay_dest(gpointer data) +{ + g_slice_free(ObFocusDelayData, data); + unfocus_delay_timeout_id = 0; + unfocus_delay_timeout_client = NULL; +} + +static gboolean focus_delay_func(gpointer data) +{ + ObFocusDelayData *d = data; + Time old = event_curtime; /* save the curtime */ + + event_curtime = d->time; + event_curserial = d->serial; + if (client_focus(d->client) && config_focus_raise) + stacking_raise(CLIENT_AS_WINDOW(d->client)); + event_curtime = old; + return FALSE; /* no repeat */ +} + +static gboolean unfocus_delay_func(gpointer data) +{ + ObFocusDelayData *d = data; + Time old = event_curtime; /* save the curtime */ + + event_curtime = d->time; + event_curserial = d->serial; + focus_nothing(); + event_curtime = old; + return FALSE; /* no repeat */ +} + +static void focus_delay_client_dest(ObClient *client, gpointer data) +{ + if (focus_delay_timeout_client == client && focus_delay_timeout_id) + g_source_remove(focus_delay_timeout_id); + if (unfocus_delay_timeout_client == client && unfocus_delay_timeout_id) + g_source_remove(unfocus_delay_timeout_id); +} + +void event_halt_focus_delay(void) +{ + /* ignore all enter events up till the event which caused this to occur */ + if (event_curserial) event_ignore_enter_range(1, event_curserial); + if (focus_delay_timeout_id) g_source_remove(focus_delay_timeout_id); + if (unfocus_delay_timeout_id) g_source_remove(unfocus_delay_timeout_id); +} + +gulong event_start_ignore_all_enters(void) +{ + return NextRequest(obt_display); +} + +static void event_ignore_enter_range(gulong start, gulong end) +{ + ObSerialRange *r; + + g_assert(start != 0); + g_assert(end != 0); + + r = g_slice_new(ObSerialRange); + r->start = start; + r->end = end; + ignore_serials = g_slist_prepend(ignore_serials, r); + + ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu", + r->start, r->end); + + /* increment the serial so we don't ignore events we weren't meant to */ + OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS); +} + +void event_end_ignore_all_enters(gulong start) +{ + /* Use (NextRequest-1) so that we ignore up to the current serial only. + Inside event_ignore_enter_range, we increment the serial by one, but if + we ignore that serial too, then any enter events generated by mouse + movement will be ignored until we create some further network traffic. + Instead ignore up to NextRequest-1, then when we increment the serial, + we will be *past* the range of ignored serials */ + event_ignore_enter_range(start, NextRequest(obt_display)-1); +} + +static gboolean is_enter_focus_event_ignored(gulong serial) +{ + GSList *it, *next; + + for (it = ignore_serials; it; it = next) { + ObSerialRange *r = it->data; + + next = g_slist_next(it); + + if ((glong)(serial - r->end) > 0) { + /* past the end */ + ignore_serials = g_slist_delete_link(ignore_serials, it); + g_slice_free(ObSerialRange, r); + } + else if ((glong)(serial - r->start) >= 0) + return TRUE; + } + return FALSE; +} + +void event_cancel_all_key_grabs(void) +{ + if (actions_interactive_act_running()) { + actions_interactive_cancel_act(); + ob_debug("KILLED interactive action"); + } + else if (menu_frame_visible) { + menu_frame_hide_all(); + ob_debug("KILLED open menus"); + } + else if (moveresize_in_progress) { + moveresize_end(TRUE); + ob_debug("KILLED interactive moveresize"); + } + else if (grab_on_keyboard()) { + ungrab_keyboard(); + ob_debug("KILLED active grab on keyboard"); + } + else + ungrab_passive_key(); + + XSync(obt_display, FALSE); +} + +gboolean event_time_after(guint32 t1, guint32 t2) +{ + g_assert(t1 != CurrentTime); + g_assert(t2 != CurrentTime); + + /* + Timestamp values wrap around (after about 49.7 days). The server, given + its current time is represented by timestamp T, always interprets + timestamps from clients by treating half of the timestamp space as being + later in time than T. + - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html + */ + + /* TIME_HALF is not half of the number space of a Time type variable. + * Rather, it is half the number space of a timestamp value, which is + * always 32 bits. */ +#define TIME_HALF (guint32)(1 << 31) + + if (t2 >= TIME_HALF) + /* t2 is in the second half so t1 might wrap around and be smaller than + t2 */ + return t1 >= t2 || t1 < (t2 + TIME_HALF); + else + /* t2 is in the first half so t1 has to come after it */ + return t1 >= t2 && t1 < (t2 + TIME_HALF); +} + +gboolean find_timestamp(XEvent *e, gpointer data) +{ + const Time t = event_get_timestamp(e); + if (t && t >= event_curtime) { + event_curtime = t; + return TRUE; + } + else + return FALSE; +} + +static Time next_time(void) +{ + /* Some events don't come with timestamps :( + ...but we can get one anyways >:) */ + + /* Generate a timestamp so there is guaranteed at least one in the queue + eventually */ + XChangeProperty(obt_display, screen_support_win, + OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING), + 8, PropModeAppend, NULL, 0); + + /* Grab the first timestamp available */ + xqueue_exists(find_timestamp, NULL); + + /*g_assert(event_curtime != CurrentTime);*/ + + /* Save the time so we don't have to do this again for this event */ + return event_curtime; +} + +Time event_time(void) +{ + if (event_curtime) return event_curtime; + + return next_time(); +} + +Time event_source_time(void) +{ + return event_sourcetime; +} + +void event_reset_time(void) +{ + next_time(); +} + +void event_update_user_time(void) +{ + event_last_user_time = event_time(); +} + +void event_reset_user_time(void) +{ + event_last_user_time = CurrentTime; +} diff --git a/openbox/event.h b/openbox/event.h new file mode 100644 index 0000000..4d9984e --- /dev/null +++ b/openbox/event.h @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + event.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __events_h +#define __events_h + +#include <X11/Xlib.h> +#include <glib.h> + +struct _ObClient; + +/*! The amount of time before a window appears that is checked for user input + to determine if the user is working in another window */ +#define OB_EVENT_USER_TIME_DELAY (1000) /* 1.0 seconds */ + +/*! The last user-interaction time, as given by the clients */ +extern Time event_last_user_time; + +void event_startup(gboolean reconfig); +void event_shutdown(gboolean reconfig); + +/*! Make as if the mouse just entered the client, use only when using focus + follows mouse */ +void event_enter_client(struct _ObClient *client); + +/*! Make as if the mouse just left the client, use only when using focus + follows mouse */ +void event_leave_client(struct _ObClient *client); + +/*! Make mouse focus not move at all from the stuff that happens between these + two function calls. */ +gulong event_start_ignore_all_enters(void); +void event_end_ignore_all_enters(gulong start); + +/*! End *all* active and passive grabs on the keyboard + This is called in situations where if there is a grab going on, then + we need to cancel it. If we move focus during the grab, applications + will get NotifyWhileGrabbed events and ignore them! + + Actions should not rely on being able to move focus during an + interactive grab. */ +void event_cancel_all_key_grabs(void); + +/* Halts any focus delay in progress, use this when the user is selecting a + window for focus */ +void event_halt_focus_delay(void); + +/*! Compare t1 and t2, taking into account wraparound. True if t1 + comes at the same time or later than t2. */ +gboolean event_time_after(guint32 t1, guint32 t2); + +/*! Time at which the current event occured. If this is not known, this + is a time at or after it, but at or before any other events we will process +*/ +Time event_time(void); + +/*! Force event_time() to skip the current timestamp and look for the next + one. */ +void event_reset_time(void); + +/*! A time at which an event happened that caused this current event to be + generated. This is a user-provided time and not to be trusted. + Returns CurrentTime if there was no source time provided. +*/ +Time event_source_time(void); + +/*! Update the timestamp for when the user has last used the focused window. + This updates the timestamp to the time of the last event, given by + event_time(). +*/ +void event_update_user_time(void); + +/*! Reset the timestamp for when the user has last used the focused window. */ +void event_reset_user_time(void); + +#endif diff --git a/openbox/focus.c b/openbox/focus.c new file mode 100644 index 0000000..a4626bf --- /dev/null +++ b/openbox/focus.c @@ -0,0 +1,389 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "event.h" +#include "openbox.h" +#include "grab.h" +#include "client.h" +#include "config.h" +#include "group.h" +#include "focus_cycle.h" +#include "screen.h" +#include "keyboard.h" +#include "focus.h" +#include "stacking.h" +#include "obt/prop.h" + +#include <X11/Xlib.h> +#include <glib.h> + +#define FOCUS_INDICATOR_WIDTH 6 + +ObClient *focus_client = NULL; +GList *focus_order = NULL; + +void focus_startup(gboolean reconfig) +{ + if (reconfig) return; + + /* start with nothing focused */ + focus_nothing(); +} + +void focus_shutdown(gboolean reconfig) +{ + if (reconfig) return; + + /* reset focus to root */ + XSetInputFocus(obt_display, PointerRoot, RevertToNone, CurrentTime); +} + +static void push_to_top(ObClient *client) +{ + ObClient *p; + + /* if it is modal for a single window, then put that window at the top + of the focus order first, so it will be right after ours. the same is + done with stacking */ + if (client->modal && (p = client_direct_parent(client))) + push_to_top(p); + + focus_order = g_list_remove(focus_order, client); + focus_order = g_list_prepend(focus_order, client); +} + +void focus_set_client(ObClient *client) +{ + Window active; + + ob_debug_type(OB_DEBUG_FOCUS, + "focus_set_client 0x%lx", client ? client->window : 0); + + if (focus_client == client) + return; + + /* uninstall the old colormap, and install the new one */ + screen_install_colormap(focus_client, FALSE); + screen_install_colormap(client, TRUE); + + focus_client = client; + + if (client != NULL) { + /* move to the top of the list */ + push_to_top(client); + /* remove hiliting from the window when it gets focused */ + client_hilite(client, FALSE); + + /* make sure the focus cycle popup shows things in the right order */ + focus_cycle_reorder(); + } + + /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */ + if (ob_state() != OB_STATE_EXITING) { + active = client ? client->window : None; + OBT_PROP_SET32(obt_root(ob_screen), NET_ACTIVE_WINDOW, WINDOW, active); + } + + /* when focus is moved to a new window, the last_user_time timestamp would + no longer be valid, as it applies for the focused window */ + event_reset_user_time(); +} + +static ObClient* focus_fallback_target(gboolean allow_refocus, + gboolean allow_pointer, + gboolean allow_omnipresent, + ObClient *old) +{ + GList *it; + ObClient *c; + + ob_debug_type(OB_DEBUG_FOCUS, "trying pointer stuff"); + if (allow_pointer && config_focus_follow) + if ((c = client_under_pointer()) && + (allow_refocus || client_focus_target(c) != old) && + (client_normal(c) && + client_focus(c))) + { + ob_debug_type(OB_DEBUG_FOCUS, "found in pointer stuff"); + return c; + } + + ob_debug_type(OB_DEBUG_FOCUS, "trying the focus order"); + for (it = focus_order; it; it = g_list_next(it)) { + c = it->data; + /* fallback focus to a window if: + 1. it is on the current desktop. this ignores omnipresent + windows, which are problematic in their own rite, unless they are + specifically allowed + 2. it is a valid auto-focus target + 3. it is not shaded + */ + if ((allow_omnipresent || c->desktop == screen_desktop) && + focus_valid_target(c, screen_desktop, + TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, + FALSE) && + !c->shaded && + (allow_refocus || client_focus_target(c) != old) && + client_focus(c)) + { + ob_debug_type(OB_DEBUG_FOCUS, "found in focus order"); + return c; + } + } + + ob_debug_type(OB_DEBUG_FOCUS, "trying a desktop window"); + for (it = focus_order; it; it = g_list_next(it)) { + c = it->data; + /* fallback focus to a window if: + 1. it is on the current desktop. this ignores omnipresent + windows, which are problematic in their own rite. + 2. it is a normal type window, don't fall back onto a dock or + a splashscreen or a desktop window (save the desktop as a + backup fallback though) + */ + if (focus_valid_target(c, screen_desktop, + TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE) && + (allow_refocus || client_focus_target(c) != old) && + client_focus(c)) + { + ob_debug_type(OB_DEBUG_FOCUS, "found a desktop window"); + return c; + } + } + + return NULL; +} + +ObClient* focus_fallback(gboolean allow_refocus, gboolean allow_pointer, + gboolean allow_omnipresent, gboolean focus_lost) +{ + ObClient *new; + ObClient *old = focus_client; + + /* unfocus any focused clients.. they can be focused by Pointer events + and such, and then when we try focus them, we won't get a FocusIn + event at all for them. */ + if (focus_lost) + focus_nothing(); + + new = focus_fallback_target(allow_refocus, allow_pointer, + allow_omnipresent, old); + /* get what was really focused */ + if (new) new = client_focus_target(new); + + return new; +} + +void focus_nothing(void) +{ + /* nothing is focused, update the colormap and _the root property_ */ + focus_set_client(NULL); + + /* when nothing will be focused, send focus to the backup target */ + XSetInputFocus(obt_display, screen_support_win, RevertToPointerRoot, + event_time()); +} + +void focus_order_add_new(ObClient *c) +{ + if (c->iconic) + focus_order_to_top(c); + else { + g_assert(!g_list_find(focus_order, c)); + /* if there are only iconic windows, put this above them in the order, + but if there are not, then put it under the currently focused one */ + if (focus_order && ((ObClient*)focus_order->data)->iconic) + focus_order = g_list_insert(focus_order, c, 0); + else + focus_order = g_list_insert(focus_order, c, 1); + } + + focus_cycle_addremove(c, TRUE); +} + +void focus_order_remove(ObClient *c) +{ + focus_order = g_list_remove(focus_order, c); + + focus_cycle_addremove(c, TRUE); +} + +void focus_order_like_new(struct _ObClient *c) +{ + focus_order = g_list_remove(focus_order, c); + focus_order_add_new(c); +} + +void focus_order_to_top(ObClient *c) +{ + focus_order = g_list_remove(focus_order, c); + if (!c->iconic) { + focus_order = g_list_prepend(focus_order, c); + } else { + GList *it; + + /* insert before first iconic window */ + for (it = focus_order; + it && !((ObClient*)it->data)->iconic; it = g_list_next(it)); + focus_order = g_list_insert_before(focus_order, it, c); + } + + focus_cycle_reorder(); +} + +void focus_order_to_bottom(ObClient *c) +{ + focus_order = g_list_remove(focus_order, c); + if (c->iconic) { + focus_order = g_list_append(focus_order, c); + } else { + GList *it; + + /* insert before first iconic window */ + for (it = focus_order; + it && !((ObClient*)it->data)->iconic; it = g_list_next(it)); + focus_order = g_list_insert_before(focus_order, it, c); + } + + focus_cycle_reorder(); +} + +ObClient *focus_order_find_first(guint desktop) +{ + GList *it; + for (it = focus_order; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (c->desktop == desktop || c->desktop == DESKTOP_ALL) + return c; + } + return NULL; +} + +/*! Returns if a focus target has valid group siblings that can be cycled + to in its place */ +static gboolean focus_target_has_siblings(ObClient *ft, + gboolean iconic_windows, + gboolean all_desktops) + +{ + GSList *it; + + if (!ft->group) return FALSE; + + for (it = ft->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + /* check that it's not a helper window to avoid infinite recursion */ + if (c != ft && c->type == OB_CLIENT_TYPE_NORMAL && + focus_valid_target(c, screen_desktop, + TRUE, iconic_windows, all_desktops, + TRUE, FALSE, FALSE, FALSE)) + { + return TRUE; + } + } + return FALSE; +} + +gboolean focus_valid_target(ObClient *ft, + guint desktop, + gboolean helper_windows, + gboolean iconic_windows, + gboolean all_desktops, + gboolean nonhilite_windows, + gboolean dock_windows, + gboolean desktop_windows, + gboolean user_request) +{ + /* NOTE: if any of these things change on a client, then they should call + focus_cycle_addremove() to make sure the client is not shown/hidden + when it should not be */ + + gboolean ok = FALSE; + + /* see if the window is still managed or is going away */ + if (!ft->managed) return FALSE; + + /* it's on this desktop unless you want all desktops. + + do this check first because it will usually filter out the most + windows */ + ok = (all_desktops || ft->desktop == desktop || + ft->desktop == DESKTOP_ALL); + + /* if we only include hilited windows, check if the window is */ + ok = ok && (nonhilite_windows || ft->demands_attention); + + /* the window can receive focus somehow */ + ok = ok && (ft->can_focus || ft->focus_notify); + + /* the window is not iconic, or we're allowed to go to iconic ones */ + ok = ok && (iconic_windows || !ft->iconic); + + /* it's the right type of window */ + if (dock_windows || desktop_windows) + ok = ok && ((dock_windows && ft->type == OB_CLIENT_TYPE_DOCK) || + (desktop_windows && ft->type == OB_CLIENT_TYPE_DESKTOP)); + /* modal windows are important and can always get focus if they are + visible and stuff, so don't change 'ok' based on their type */ + else if (!ft->modal) + /* normal non-helper windows are valid targets */ + ok = ok && + ((client_normal(ft) && !client_helper(ft)) + || + /* helper windows are valid targets if... */ + (client_helper(ft) && + /* ...a window in its group already has focus and we want to + include helper windows ... */ + ((focus_client && ft->group == focus_client->group && + helper_windows) || + /* ... or if there are no other windows in its group + that can be focused instead */ + !focus_target_has_siblings(ft, iconic_windows, all_desktops)))); + + /* it's not set to skip the taskbar (but this is overridden if the + window is modal or if the user asked for this window to be focused, + or if the window is iconified (and does not have any parents with + which to uniconify it), and it is not used for windows which are + hilited, or dialog windows as these need user interaction and should + not be long-lasting windows */ + ok = ok && (!ft->skip_taskbar || + (ft->modal || user_request || + (ft->iconic && !ft->parents) || + ft->demands_attention || + ft->type == OB_CLIENT_TYPE_DIALOG)); + + /* it's not going to just send focus off somewhere else (modal window), + unless that modal window is not one of our valid targets, then let + you choose this window and bring the modal one here */ + { + ObClient *cft = client_focus_target(ft); + ok = ok && (ft == cft || !focus_valid_target(cft, + screen_desktop, + TRUE, + iconic_windows, + all_desktops, + nonhilite_windows, + dock_windows, + desktop_windows, + FALSE)); + } + + return ok; +} diff --git a/openbox/focus.h b/openbox/focus.h new file mode 100644 index 0000000..1fc1eb5 --- /dev/null +++ b/openbox/focus.h @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __focus_h +#define __focus_h + +#include "misc.h" + +#include <X11/Xlib.h> +#include <glib.h> + +struct _ObClient; + +/*! The client which is currently focused */ +extern struct _ObClient *focus_client; + +/*! The recent focus order on each desktop */ +extern GList *focus_order; + +void focus_startup(gboolean reconfig); +void focus_shutdown(gboolean reconfig); + +/*! Specify which client is currently focused, this doesn't actually + send focus anywhere, its called by the Focus event handlers */ +void focus_set_client(struct _ObClient *client); + +/*! Focus nothing, but let keyboard events be caught. */ +void focus_nothing(void); + +/*! Call this when you need to focus something! */ +struct _ObClient* focus_fallback(gboolean allow_refocus, + gboolean allow_pointer, + gboolean allow_omnipresent, + gboolean focus_lost); + +/*! Add a new client into the focus order */ +void focus_order_add_new(struct _ObClient *c); + +/*! Remove a client from the focus order */ +void focus_order_remove(struct _ObClient *c); + +/*! Move a client to the top of the focus order */ +void focus_order_to_top(struct _ObClient *c); + +/*! Move a client to where it would be if it was newly added to the focus order + */ +void focus_order_like_new(struct _ObClient *c); + +/*! Move a client to the bottom of the focus order (keeps iconic windows at the + very bottom always though). */ +void focus_order_to_bottom(struct _ObClient *c); + +struct _ObClient *focus_order_find_first(guint desktop); + +gboolean focus_valid_target(struct _ObClient *ft, + guint desktop, + gboolean helper_windows, + gboolean iconic_windows, + gboolean all_desktops, + gboolean nonhilite_windows, + gboolean dock_windows, + gboolean desktop_windows, + gboolean user_request); + +#endif diff --git a/openbox/focus_cycle.c b/openbox/focus_cycle.c new file mode 100644 index 0000000..6d4cc2a --- /dev/null +++ b/openbox/focus_cycle.c @@ -0,0 +1,364 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "focus_cycle.h" +#include "focus_cycle_indicator.h" +#include "client.h" +#include "frame.h" +#include "focus.h" +#include "screen.h" +#include "openbox.h" +#include "debug.h" + +#include <X11/Xlib.h> +#include <glib.h> + +typedef enum { + OB_CYCLE_NONE = 0, + OB_CYCLE_NORMAL, + OB_CYCLE_DIRECTIONAL +} ObCycleType; + +ObClient *focus_cycle_target = NULL; +static ObCycleType focus_cycle_type = OB_CYCLE_NONE; +static gboolean focus_cycle_linear; +static gboolean focus_cycle_iconic_windows; +static gboolean focus_cycle_all_desktops; +static gboolean focus_cycle_nonhilite_windows; +static gboolean focus_cycle_dock_windows; +static gboolean focus_cycle_desktop_windows; + +static ObClient *focus_find_directional(ObClient *c, + ObDirection dir, + gboolean dock_windows, + gboolean desktop_windows); + +void focus_cycle_startup(gboolean reconfig) +{ + if (reconfig) return; +} + +void focus_cycle_shutdown(gboolean reconfig) +{ + if (reconfig) return; +} + +void focus_cycle_addremove(ObClient *c, gboolean redraw) +{ + if (!focus_cycle_type) + return; + + if (focus_cycle_type == OB_CYCLE_DIRECTIONAL) { + if (c && focus_cycle_target == c) { + focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE); + } + } + else if (c && redraw) { + gboolean v, s; + + v = focus_cycle_valid(c); + s = focus_cycle_popup_is_showing(c); + + if (v != s) + focus_cycle_reorder(); + } + else if (redraw) { + focus_cycle_reorder(); + } +} + +void focus_cycle_reorder() +{ + if (focus_cycle_type == OB_CYCLE_NORMAL) { + focus_cycle_target = focus_cycle_popup_refresh(focus_cycle_target, + TRUE, + focus_cycle_linear); + focus_cycle_update_indicator(focus_cycle_target); + if (!focus_cycle_target) + focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, OB_FOCUS_CYCLE_POPUP_MODE_NONE, + TRUE, TRUE); + } +} + +ObClient* focus_cycle(gboolean forward, gboolean all_desktops, + gboolean nonhilite_windows, + gboolean dock_windows, gboolean desktop_windows, + gboolean linear, gboolean interactive, + gboolean showbar, ObFocusCyclePopupMode mode, + gboolean done, gboolean cancel) +{ + static GList *order = NULL; + GList *it, *start, *list; + ObClient *ft = NULL; + ObClient *ret = NULL; + + if (interactive) { + if (cancel) { + focus_cycle_target = NULL; + goto done_cycle; + } else if (done) + goto done_cycle; + + if (!focus_order) + goto done_cycle; + + if (linear) list = client_list; + else list = focus_order; + } else { + if (!focus_order) + goto done_cycle; + list = client_list; + } + + if (focus_cycle_target == NULL) { + focus_cycle_linear = linear; + focus_cycle_iconic_windows = TRUE; + focus_cycle_all_desktops = all_desktops; + focus_cycle_nonhilite_windows = nonhilite_windows; + focus_cycle_dock_windows = dock_windows; + focus_cycle_desktop_windows = desktop_windows; + start = it = g_list_find(list, focus_client); + } else + start = it = g_list_find(list, focus_cycle_target); + + if (!start) /* switched desktops or something? */ + start = it = forward ? g_list_last(list) : g_list_first(list); + if (!start) goto done_cycle; + + do { + if (forward) { + it = it->next; + if (it == NULL) it = g_list_first(list); + } else { + it = it->prev; + if (it == NULL) it = g_list_last(list); + } + ft = it->data; + if (focus_cycle_valid(ft)) { + if (interactive) { + if (ft != focus_cycle_target) { /* prevents flicker */ + focus_cycle_target = ft; + focus_cycle_type = OB_CYCLE_NORMAL; + focus_cycle_draw_indicator(showbar ? ft : NULL); + } + /* same arguments as focus_target_valid */ + focus_cycle_popup_show(ft, mode, focus_cycle_linear); + return focus_cycle_target; + } else if (ft != focus_cycle_target) { + focus_cycle_target = ft; + focus_cycle_type = OB_CYCLE_NORMAL; + done = TRUE; + break; + } + } + } while (it != start); + +done_cycle: + if (done && !cancel) ret = focus_cycle_target; + + focus_cycle_target = NULL; + focus_cycle_type = OB_CYCLE_NONE; + g_list_free(order); + order = NULL; + + if (interactive) { + focus_cycle_draw_indicator(NULL); + focus_cycle_popup_hide(); + } + + return ret; +} + +/* this be mostly ripped from fvwm */ +static ObClient *focus_find_directional(ObClient *c, ObDirection dir, + gboolean dock_windows, + gboolean desktop_windows) +{ + gint my_cx, my_cy, his_cx, his_cy; + gint offset = 0; + gint distance = 0; + gint score, best_score; + ObClient *best_client, *cur; + GList *it; + + if (!client_list) + return NULL; + + /* first, find the centre coords of the currently focused window */ + my_cx = c->frame->area.x + c->frame->area.width / 2; + my_cy = c->frame->area.y + c->frame->area.height / 2; + + best_score = -1; + best_client = c; + + for (it = g_list_first(client_list); it; it = g_list_next(it)) { + cur = it->data; + + /* the currently selected window isn't interesting */ + if (cur == c) + continue; + if (!focus_cycle_valid(it->data)) + continue; + + /* find the centre coords of this window, from the + * currently focused window's point of view */ + his_cx = (cur->frame->area.x - my_cx) + + cur->frame->area.width / 2; + his_cy = (cur->frame->area.y - my_cy) + + cur->frame->area.height / 2; + + if (dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST || + dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) + { + gint tx; + /* Rotate the diagonals 45 degrees counterclockwise. + * To do this, multiply the matrix /+h +h\ with the + * vector (x y). \-h +h/ + * h = sqrt(0.5). We can set h := 1 since absolute + * distance doesn't matter here. */ + tx = his_cx + his_cy; + his_cy = -his_cx + his_cy; + his_cx = tx; + } + + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_NORTHEAST: + case OB_DIRECTION_SOUTHWEST: + offset = (his_cx < 0) ? -his_cx : his_cx; + distance = ((dir == OB_DIRECTION_NORTH || + dir == OB_DIRECTION_NORTHEAST) ? + -his_cy : his_cy); + break; + case OB_DIRECTION_EAST: + case OB_DIRECTION_WEST: + case OB_DIRECTION_SOUTHEAST: + case OB_DIRECTION_NORTHWEST: + offset = (his_cy < 0) ? -his_cy : his_cy; + distance = ((dir == OB_DIRECTION_WEST || + dir == OB_DIRECTION_NORTHWEST) ? + -his_cx : his_cx); + break; + } + + /* the target must be in the requested direction */ + if (distance <= 0) + continue; + + /* Calculate score for this window. The smaller the better. */ + score = distance + offset; + + /* windows more than 45 degrees off the direction are + * heavily penalized and will only be chosen if nothing + * else within a million pixels */ + if (offset > distance) + score += 1000000; + + if (best_score == -1 || score < best_score) { + best_client = cur; + best_score = score; + } + } + + return best_client; +} + +ObClient* focus_directional_cycle(ObDirection dir, gboolean dock_windows, + gboolean desktop_windows, + gboolean interactive, + gboolean showbar, gboolean dialog, + gboolean done, gboolean cancel) +{ + static ObClient *first = NULL; + ObClient *ft = NULL; + ObClient *ret = NULL; + + if (cancel) { + focus_cycle_target = NULL; + goto done_cycle; + } else if (done && interactive) + goto done_cycle; + + if (!focus_order) + goto done_cycle; + + if (focus_cycle_target == NULL) { + focus_cycle_linear = FALSE; + focus_cycle_iconic_windows = FALSE; + focus_cycle_all_desktops = FALSE; + focus_cycle_nonhilite_windows = TRUE; + focus_cycle_dock_windows = dock_windows; + focus_cycle_desktop_windows = desktop_windows; + } + + if (!first) first = focus_client; + + if (focus_cycle_target) + ft = focus_find_directional(focus_cycle_target, dir, dock_windows, + desktop_windows); + else if (first) + ft = focus_find_directional(first, dir, dock_windows, desktop_windows); + else { + GList *it; + + for (it = focus_order; it; it = g_list_next(it)) + if (focus_cycle_valid(it->data)) { + ft = it->data; + break; + } + } + + if (ft && ft != focus_cycle_target) {/* prevents flicker */ + focus_cycle_target = ft; + focus_cycle_type = OB_CYCLE_DIRECTIONAL; + if (!interactive) + goto done_cycle; + focus_cycle_draw_indicator(showbar ? ft : NULL); + } + if (focus_cycle_target && dialog) + /* same arguments as focus_target_valid */ + focus_cycle_popup_single_show(focus_cycle_target); + return focus_cycle_target; + +done_cycle: + if (done && !cancel) ret = focus_cycle_target; + + first = NULL; + focus_cycle_target = NULL; + focus_cycle_type = OB_CYCLE_NONE; + + focus_cycle_draw_indicator(NULL); + focus_cycle_popup_single_hide(); + + return ret; +} + +gboolean focus_cycle_valid(struct _ObClient *client) +{ + return focus_valid_target(client, screen_desktop, TRUE, + focus_cycle_iconic_windows, + focus_cycle_all_desktops, + focus_cycle_nonhilite_windows, + focus_cycle_dock_windows, + focus_cycle_desktop_windows, + FALSE); +} diff --git a/openbox/focus_cycle.h b/openbox/focus_cycle.h new file mode 100644 index 0000000..9394b3d --- /dev/null +++ b/openbox/focus_cycle.h @@ -0,0 +1,58 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __focus_cycle_h +#define __focus_cycle_h + +#include "misc.h" +#include "focus_cycle_popup.h" + +#include <X11/Xlib.h> +#include <glib.h> + +struct _ObClient; + +/*! The client which appears focused during a focus cycle operation */ +extern struct _ObClient *focus_cycle_target; + +void focus_cycle_startup(gboolean reconfig); +void focus_cycle_shutdown(gboolean reconfig); + +/*! Cycle focus amongst windows. */ +struct _ObClient* focus_cycle(gboolean forward, gboolean all_desktops, + gboolean nonhilite_windows, + gboolean dock_windows, gboolean desktop_windows, + gboolean linear, gboolean interactive, + gboolean showbar, ObFocusCyclePopupMode mode, + gboolean done, gboolean cancel); +struct _ObClient* focus_directional_cycle(ObDirection dir, + gboolean dock_windows, + gboolean desktop_windows, + gboolean interactive, + gboolean showbar, + gboolean dialog, + gboolean done, gboolean cancel); + +/*! Set @redraw to FALSE if there are more clients to be added/removed first */ +void focus_cycle_addremove(struct _ObClient *ifclient, gboolean redraw); +void focus_cycle_reorder(); + +gboolean focus_cycle_valid(struct _ObClient *client); + +#endif diff --git a/openbox/focus_cycle_indicator.c b/openbox/focus_cycle_indicator.c new file mode 100644 index 0000000..8952790 --- /dev/null +++ b/openbox/focus_cycle_indicator.c @@ -0,0 +1,287 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle_indicator.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "focus_cycle.h" +#include "focus_cycle_indicator.h" +#include "client.h" +#include "openbox.h" +#include "frame.h" +#include "event.h" +#include "obrender/render.h" + +#include <X11/Xlib.h> +#include <glib.h> + +#define FOCUS_INDICATOR_WIDTH 6 + +static struct +{ + ObInternalWindow top; + ObInternalWindow left; + ObInternalWindow right; + ObInternalWindow bottom; +} focus_indicator; + +static RrAppearance *a_focus_indicator; +static RrColor *color_white; +static gboolean visible; + +static Window create_window(Window parent, gulong mask, + XSetWindowAttributes *attrib) +{ + return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), mask, attrib); + +} + +void focus_cycle_indicator_startup(gboolean reconfig) +{ + XSetWindowAttributes attr; + + visible = FALSE; + + if (reconfig) return; + + focus_indicator.top.type = OB_WINDOW_CLASS_INTERNAL; + focus_indicator.left.type = OB_WINDOW_CLASS_INTERNAL; + focus_indicator.right.type = OB_WINDOW_CLASS_INTERNAL; + focus_indicator.bottom.type = OB_WINDOW_CLASS_INTERNAL; + + attr.override_redirect = True; + attr.background_pixel = BlackPixel(obt_display, ob_screen); + focus_indicator.top.window = + create_window(obt_root(ob_screen), + CWOverrideRedirect | CWBackPixel, &attr); + focus_indicator.left.window = + create_window(obt_root(ob_screen), + CWOverrideRedirect | CWBackPixel, &attr); + focus_indicator.right.window = + create_window(obt_root(ob_screen), + CWOverrideRedirect | CWBackPixel, &attr); + focus_indicator.bottom.window = + create_window(obt_root(ob_screen), + CWOverrideRedirect | CWBackPixel, &attr); + + stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.top)); + stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.left)); + stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.right)); + stacking_add(INTERNAL_AS_WINDOW(&focus_indicator.bottom)); + window_add(&focus_indicator.top.window, + INTERNAL_AS_WINDOW(&focus_indicator.top)); + window_add(&focus_indicator.left.window, + INTERNAL_AS_WINDOW(&focus_indicator.left)); + window_add(&focus_indicator.right.window, + INTERNAL_AS_WINDOW(&focus_indicator.right)); + window_add(&focus_indicator.bottom.window, + INTERNAL_AS_WINDOW(&focus_indicator.bottom)); + + color_white = RrColorNew(ob_rr_inst, 0xff, 0xff, 0xff); + + a_focus_indicator = RrAppearanceNew(ob_rr_inst, 4); + a_focus_indicator->surface.grad = RR_SURFACE_SOLID; + a_focus_indicator->surface.relief = RR_RELIEF_FLAT; + a_focus_indicator->surface.primary = RrColorNew(ob_rr_inst, + 0, 0, 0); + a_focus_indicator->texture[0].type = RR_TEXTURE_LINE_ART; + a_focus_indicator->texture[0].data.lineart.color = color_white; + a_focus_indicator->texture[1].type = RR_TEXTURE_LINE_ART; + a_focus_indicator->texture[1].data.lineart.color = color_white; + a_focus_indicator->texture[2].type = RR_TEXTURE_LINE_ART; + a_focus_indicator->texture[2].data.lineart.color = color_white; + a_focus_indicator->texture[3].type = RR_TEXTURE_LINE_ART; + a_focus_indicator->texture[3].data.lineart.color = color_white; +} + +void focus_cycle_indicator_shutdown(gboolean reconfig) +{ + if (reconfig) return; + + RrColorFree(color_white); + + RrAppearanceFree(a_focus_indicator); + + window_remove(focus_indicator.top.window); + window_remove(focus_indicator.left.window); + window_remove(focus_indicator.right.window); + window_remove(focus_indicator.bottom.window); + + stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.top)); + stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.left)); + stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.right)); + stacking_remove(INTERNAL_AS_WINDOW(&focus_indicator.bottom)); + + XDestroyWindow(obt_display, focus_indicator.top.window); + XDestroyWindow(obt_display, focus_indicator.left.window); + XDestroyWindow(obt_display, focus_indicator.right.window); + XDestroyWindow(obt_display, focus_indicator.bottom.window); +} + +void focus_cycle_update_indicator(ObClient *c) +{ + if (visible) + focus_cycle_draw_indicator(c); +} + +void focus_cycle_draw_indicator(ObClient *c) +{ + if (!c && visible) { + gulong ignore_start; + + /* kill enter events cause by this unmapping */ + ignore_start = event_start_ignore_all_enters(); + + XUnmapWindow(obt_display, focus_indicator.top.window); + XUnmapWindow(obt_display, focus_indicator.left.window); + XUnmapWindow(obt_display, focus_indicator.right.window); + XUnmapWindow(obt_display, focus_indicator.bottom.window); + + event_end_ignore_all_enters(ignore_start); + + visible = FALSE; + } + else if (c) { + /* + if (c) + frame_adjust_focus(c->frame, FALSE); + frame_adjust_focus(c->frame, TRUE); + */ + gint x, y, w, h; + gint wt, wl, wr, wb; + gulong ignore_start; + + wt = wl = wr = wb = FOCUS_INDICATOR_WIDTH; + + x = c->frame->area.x; + y = c->frame->area.y; + w = c->frame->area.width; + h = wt; + + /* kill enter events cause by this moving */ + ignore_start = event_start_ignore_all_enters(); + + XMoveResizeWindow(obt_display, focus_indicator.top.window, + x, y, w, h); + a_focus_indicator->texture[0].data.lineart.x1 = 0; + a_focus_indicator->texture[0].data.lineart.y1 = h-1; + a_focus_indicator->texture[0].data.lineart.x2 = 0; + a_focus_indicator->texture[0].data.lineart.y2 = 0; + a_focus_indicator->texture[1].data.lineart.x1 = 0; + a_focus_indicator->texture[1].data.lineart.y1 = 0; + a_focus_indicator->texture[1].data.lineart.x2 = w-1; + a_focus_indicator->texture[1].data.lineart.y2 = 0; + a_focus_indicator->texture[2].data.lineart.x1 = w-1; + a_focus_indicator->texture[2].data.lineart.y1 = 0; + a_focus_indicator->texture[2].data.lineart.x2 = w-1; + a_focus_indicator->texture[2].data.lineart.y2 = h-1; + a_focus_indicator->texture[3].data.lineart.x1 = (wl-1); + a_focus_indicator->texture[3].data.lineart.y1 = h-1; + a_focus_indicator->texture[3].data.lineart.x2 = w - wr; + a_focus_indicator->texture[3].data.lineart.y2 = h-1; + RrPaint(a_focus_indicator, focus_indicator.top.window, + w, h); + + x = c->frame->area.x; + y = c->frame->area.y; + w = wl; + h = c->frame->area.height; + + XMoveResizeWindow(obt_display, focus_indicator.left.window, + x, y, w, h); + a_focus_indicator->texture[0].data.lineart.x1 = w-1; + a_focus_indicator->texture[0].data.lineart.y1 = 0; + a_focus_indicator->texture[0].data.lineart.x2 = 0; + a_focus_indicator->texture[0].data.lineart.y2 = 0; + a_focus_indicator->texture[1].data.lineart.x1 = 0; + a_focus_indicator->texture[1].data.lineart.y1 = 0; + a_focus_indicator->texture[1].data.lineart.x2 = 0; + a_focus_indicator->texture[1].data.lineart.y2 = h-1; + a_focus_indicator->texture[2].data.lineart.x1 = 0; + a_focus_indicator->texture[2].data.lineart.y1 = h-1; + a_focus_indicator->texture[2].data.lineart.x2 = w-1; + a_focus_indicator->texture[2].data.lineart.y2 = h-1; + a_focus_indicator->texture[3].data.lineart.x1 = w-1; + a_focus_indicator->texture[3].data.lineart.y1 = wt-1; + a_focus_indicator->texture[3].data.lineart.x2 = w-1; + a_focus_indicator->texture[3].data.lineart.y2 = h - wb; + RrPaint(a_focus_indicator, focus_indicator.left.window, + w, h); + + x = c->frame->area.x + c->frame->area.width - wr; + y = c->frame->area.y; + w = wr; + h = c->frame->area.height ; + + XMoveResizeWindow(obt_display, focus_indicator.right.window, + x, y, w, h); + a_focus_indicator->texture[0].data.lineart.x1 = 0; + a_focus_indicator->texture[0].data.lineart.y1 = 0; + a_focus_indicator->texture[0].data.lineart.x2 = w-1; + a_focus_indicator->texture[0].data.lineart.y2 = 0; + a_focus_indicator->texture[1].data.lineart.x1 = w-1; + a_focus_indicator->texture[1].data.lineart.y1 = 0; + a_focus_indicator->texture[1].data.lineart.x2 = w-1; + a_focus_indicator->texture[1].data.lineart.y2 = h-1; + a_focus_indicator->texture[2].data.lineart.x1 = w-1; + a_focus_indicator->texture[2].data.lineart.y1 = h-1; + a_focus_indicator->texture[2].data.lineart.x2 = 0; + a_focus_indicator->texture[2].data.lineart.y2 = h-1; + a_focus_indicator->texture[3].data.lineart.x1 = 0; + a_focus_indicator->texture[3].data.lineart.y1 = wt-1; + a_focus_indicator->texture[3].data.lineart.x2 = 0; + a_focus_indicator->texture[3].data.lineart.y2 = h - wb; + RrPaint(a_focus_indicator, focus_indicator.right.window, + w, h); + + x = c->frame->area.x; + y = c->frame->area.y + c->frame->area.height - wb; + w = c->frame->area.width; + h = wb; + + XMoveResizeWindow(obt_display, focus_indicator.bottom.window, + x, y, w, h); + a_focus_indicator->texture[0].data.lineart.x1 = 0; + a_focus_indicator->texture[0].data.lineart.y1 = 0; + a_focus_indicator->texture[0].data.lineart.x2 = 0; + a_focus_indicator->texture[0].data.lineart.y2 = h-1; + a_focus_indicator->texture[1].data.lineart.x1 = 0; + a_focus_indicator->texture[1].data.lineart.y1 = h-1; + a_focus_indicator->texture[1].data.lineart.x2 = w-1; + a_focus_indicator->texture[1].data.lineart.y2 = h-1; + a_focus_indicator->texture[2].data.lineart.x1 = w-1; + a_focus_indicator->texture[2].data.lineart.y1 = h-1; + a_focus_indicator->texture[2].data.lineart.x2 = w-1; + a_focus_indicator->texture[2].data.lineart.y2 = 0; + a_focus_indicator->texture[3].data.lineart.x1 = wl-1; + a_focus_indicator->texture[3].data.lineart.y1 = 0; + a_focus_indicator->texture[3].data.lineart.x2 = w - wr; + a_focus_indicator->texture[3].data.lineart.y2 = 0; + RrPaint(a_focus_indicator, focus_indicator.bottom.window, + w, h); + + XMapWindow(obt_display, focus_indicator.top.window); + XMapWindow(obt_display, focus_indicator.left.window); + XMapWindow(obt_display, focus_indicator.right.window); + XMapWindow(obt_display, focus_indicator.bottom.window); + + event_end_ignore_all_enters(ignore_start); + + visible = TRUE; + } +} diff --git a/openbox/focus_cycle_indicator.h b/openbox/focus_cycle_indicator.h new file mode 100644 index 0000000..3077f7c --- /dev/null +++ b/openbox/focus_cycle_indicator.h @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle_indicator.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __focus_cycle_indicator_h +#define __focus_cycle_indicator_h + +struct _ObClient; + +void focus_cycle_indicator_startup(gboolean reconfig); +void focus_cycle_indicator_shutdown(gboolean reconfig); + +void focus_cycle_update_indicator(struct _ObClient *c); +void focus_cycle_draw_indicator(struct _ObClient *c); + +#endif diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c new file mode 100644 index 0000000..e1ea848 --- /dev/null +++ b/openbox/focus_cycle_popup.c @@ -0,0 +1,847 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle_popup.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "focus_cycle_popup.h" +#include "focus_cycle.h" +#include "popup.h" +#include "client.h" +#include "screen.h" +#include "focus.h" +#include "openbox.h" +#include "config.h" +#include "window.h" +#include "event.h" +#include "obrender/render.h" + +#include <X11/Xlib.h> +#include <glib.h> + +/* Size of the icons, which can appear inside or outside of a hilite box */ +#define ICON_SIZE (gint)config_theme_window_list_icon_size +/* Size of the hilite box around a window's icon */ +#define HILITE_SIZE (ICON_SIZE + 2*HILITE_OFFSET) +/* Width of the outer ring around the hilite box */ +#define HILITE_WIDTH 2 +/* Space between the outer ring around the hilite box and the icon inside it */ +#define HILITE_MARGIN 1 +/* Total distance from the edge of the hilite box to the icon inside it */ +#define HILITE_OFFSET (HILITE_WIDTH + HILITE_MARGIN) +/* Margin area around the outside of the dialog */ +#define OUTSIDE_BORDER 3 +/* Margin area around the text */ +#define TEXT_BORDER 2 +/* Scroll the list-mode list when the cursor gets within this many rows of the + top or bottom */ +#define SCROLL_MARGIN 4 + +typedef struct _ObFocusCyclePopup ObFocusCyclePopup; +typedef struct _ObFocusCyclePopupTarget ObFocusCyclePopupTarget; + +struct _ObFocusCyclePopupTarget +{ + ObClient *client; + RrImage *icon; + gchar *text; + Window iconwin; + /* This is used when the popup is in list mode */ + Window textwin; +}; + +struct _ObFocusCyclePopup +{ + ObWindow obwin; + Window bg; + + /* This is used when the popup is in icon mode */ + Window icon_mode_text; + + Window list_mode_up; + Window list_mode_down; + + GList *targets; + gint n_targets; + + const ObFocusCyclePopupTarget *last_target; + + gint maxtextw; + + /* How are the list is scrolled, in scroll mode */ + gint scroll; + + RrAppearance *a_bg; + RrAppearance *a_text; + RrAppearance *a_hilite_text; + RrAppearance *a_icon; + RrAppearance *a_arrow; + + gboolean mapped; + ObFocusCyclePopupMode mode; +}; + +/*! This popup shows all possible windows */ +static ObFocusCyclePopup popup; +/*! This popup shows a single window */ +static ObIconPopup *single_popup; + +static gchar *popup_get_name (ObClient *c); +static gboolean popup_setup (ObFocusCyclePopup *p, + gboolean create_targets, + gboolean refresh_targets, + gboolean linear); +static void popup_render (ObFocusCyclePopup *p, + const ObClient *c); + +static Window create_window(Window parent, guint bwidth, gulong mask, + XSetWindowAttributes *attr) +{ + return XCreateWindow(obt_display, parent, 0, 0, 1, 1, bwidth, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), mask, attr); +} + +void focus_cycle_popup_startup(gboolean reconfig) +{ + XSetWindowAttributes attrib; + RrPixel32 *p; + + single_popup = icon_popup_new(); + + popup.obwin.type = OB_WINDOW_CLASS_INTERNAL; + popup.a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg); + popup.a_hilite_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); + popup.a_text = RrAppearanceCopy(ob_rr_theme->osd_unhilite_label); + popup.a_icon = RrAppearanceCopy(ob_rr_theme->a_clear); + popup.a_arrow = RrAppearanceCopy(ob_rr_theme->a_clear_tex); + + popup.a_hilite_text->surface.parent = popup.a_bg; + popup.a_text->surface.parent = popup.a_bg; + popup.a_icon->surface.parent = popup.a_bg; + + popup.a_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT; + popup.a_hilite_text->texture[0].data.text.justify = RR_JUSTIFY_LEFT; + + /* 2 textures. texture[0] is the icon. texture[1] is the hilight, and + may or may not be used */ + RrAppearanceAddTextures(popup.a_icon, 2); + + RrAppearanceClearTextures(popup.a_icon); + popup.a_icon->texture[0].type = RR_TEXTURE_IMAGE; + + RrAppearanceClearTextures(popup.a_arrow); + popup.a_arrow->texture[0].type = RR_TEXTURE_MASK; + popup.a_arrow->texture[0].data.mask.color = + ob_rr_theme->osd_text_active_color; + + attrib.override_redirect = True; + attrib.border_pixel=RrColorPixel(ob_rr_theme->osd_border_color); + popup.bg = create_window(obt_root(ob_screen), ob_rr_theme->obwidth, + CWOverrideRedirect | CWBorderPixel, &attrib); + + /* create the text window used for the icon-mode popup */ + popup.icon_mode_text = create_window(popup.bg, 0, 0, NULL); + + /* create the windows for the up and down arrows */ + popup.list_mode_up = create_window(popup.bg, 0, 0, NULL); + popup.list_mode_down = create_window(popup.bg, 0, 0, NULL); + + popup.targets = NULL; + popup.n_targets = 0; + popup.last_target = NULL; + + /* set up the hilite texture for the icon */ + popup.a_icon->texture[1].data.rgba.width = HILITE_SIZE; + popup.a_icon->texture[1].data.rgba.height = HILITE_SIZE; + popup.a_icon->texture[1].data.rgba.alpha = 0xff; + p = g_new(RrPixel32, HILITE_SIZE * HILITE_SIZE); + popup.a_icon->texture[1].data.rgba.data = p; + + /* create the hilite under the target icon */ + { + RrPixel32 color; + RrColor *tc; + gint x, y, o; + + tc = ob_rr_theme->osd_text_active_color; + color = ((tc->r & 0xff) << RrDefaultRedOffset) + + ((tc->g & 0xff) << RrDefaultGreenOffset) + + ((tc->b & 0xff) << RrDefaultBlueOffset); + + o = 0; + for (x = 0; x < HILITE_SIZE; x++) + for (y = 0; y < HILITE_SIZE; y++) { + guchar a; + + if (x < HILITE_WIDTH || + x >= HILITE_SIZE - HILITE_WIDTH || + y < HILITE_WIDTH || + y >= HILITE_SIZE - HILITE_WIDTH) + { + /* the border of the target */ + a = 0x88; + } else { + /* the background of the target */ + a = 0x22; + } + + p[o++] = color + (a << RrDefaultAlphaOffset); + } + } + + stacking_add(INTERNAL_AS_WINDOW(&popup)); + window_add(&popup.bg, INTERNAL_AS_WINDOW(&popup)); +} + +void focus_cycle_popup_shutdown(gboolean reconfig) +{ + icon_popup_free(single_popup); + + window_remove(popup.bg); + stacking_remove(INTERNAL_AS_WINDOW(&popup)); + + while(popup.targets) { + ObFocusCyclePopupTarget *t = popup.targets->data; + + RrImageUnref(t->icon); + g_free(t->text); + XDestroyWindow(obt_display, t->iconwin); + XDestroyWindow(obt_display, t->textwin); + g_slice_free(ObFocusCyclePopupTarget, t); + + popup.targets = g_list_delete_link(popup.targets, popup.targets); + } + + g_free(popup.a_icon->texture[1].data.rgba.data); + popup.a_icon->texture[1].data.rgba.data = NULL; + + XDestroyWindow(obt_display, popup.list_mode_up); + XDestroyWindow(obt_display, popup.list_mode_down); + XDestroyWindow(obt_display, popup.icon_mode_text); + XDestroyWindow(obt_display, popup.bg); + + RrAppearanceFree(popup.a_arrow); + RrAppearanceFree(popup.a_icon); + RrAppearanceFree(popup.a_hilite_text); + RrAppearanceFree(popup.a_text); + RrAppearanceFree(popup.a_bg); +} + +static void popup_target_free(ObFocusCyclePopupTarget *t) +{ + RrImageUnref(t->icon); + g_free(t->text); + XDestroyWindow(obt_display, t->iconwin); + XDestroyWindow(obt_display, t->textwin); + g_slice_free(ObFocusCyclePopupTarget, t); +} + +static gboolean popup_setup(ObFocusCyclePopup *p, gboolean create_targets, + gboolean refresh_targets, gboolean linear) +{ + gint maxwidth, n; + GList *it; + GList *rtargets; /* old targets for refresh */ + GList *rtlast; + gboolean change; + + if (refresh_targets) { + rtargets = p->targets; + rtlast = g_list_last(rtargets); + p->targets = NULL; + p->n_targets = 0; + change = FALSE; + } + else { + rtargets = rtlast = NULL; + change = TRUE; + } + + g_assert(p->targets == NULL); + g_assert(p->n_targets == 0); + + /* make its width to be the width of all the possible titles */ + + /* build a list of all the valid focus targets and measure their strings, + and count them */ + maxwidth = 0; + n = 0; + for (it = g_list_last(linear ? client_list : focus_order); + it; + it = g_list_previous(it)) + { + ObClient *ft = it->data; + + if (focus_cycle_valid(ft)) { + GList *rit; + + /* reuse the target if possible during refresh */ + for (rit = rtlast; rit; rit = g_list_previous(rit)) { + ObFocusCyclePopupTarget *t = rit->data; + if (t->client == ft) { + if (rit == rtlast) + rtlast = g_list_previous(rit); + rtargets = g_list_remove_link(rtargets, rit); + + p->targets = g_list_concat(rit, p->targets); + ++n; + + if (rit != rtlast) + change = TRUE; /* order changed */ + break; + } + } + + if (!rit) { + gchar *text = popup_get_name(ft); + + /* measure */ + p->a_text->texture[0].data.text.string = text; + maxwidth = MAX(maxwidth, RrMinWidth(p->a_text)); + + if (!create_targets) { + g_free(text); + } else { + ObFocusCyclePopupTarget *t = + g_slice_new(ObFocusCyclePopupTarget); + + t->client = ft; + t->text = text; + t->icon = client_icon(t->client); + RrImageRef(t->icon); /* own the icon so it won't go away */ + t->iconwin = create_window(p->bg, 0, 0, NULL); + t->textwin = create_window(p->bg, 0, 0, NULL); + + p->targets = g_list_prepend(p->targets, t); + ++n; + + change = TRUE; /* added a window */ + } + } + } + } + + if (rtargets) { + change = TRUE; /* removed a window */ + + while (rtargets) { + popup_target_free(rtargets->data); + rtargets = g_list_delete_link(rtargets, rtargets); + } + } + + p->n_targets = n; + if (refresh_targets) + /* don't shrink when refreshing */ + p->maxtextw = MAX(p->maxtextw, maxwidth); + else + p->maxtextw = maxwidth; + + return change; +} + +static void popup_cleanup(void) +{ + while(popup.targets) { + popup_target_free(popup.targets->data); + popup.targets = g_list_delete_link(popup.targets, popup.targets); + } + popup.n_targets = 0; + popup.last_target = NULL; +} + +static gchar *popup_get_name(ObClient *c) +{ + ObClient *p; + gchar *title; + const gchar *desk = NULL; + gchar *ret; + + /* find our highest direct parent */ + p = client_search_top_direct_parent(c); + + if (c->desktop != DESKTOP_ALL && c->desktop != screen_desktop) + desk = screen_desktop_names[c->desktop]; + + title = c->iconic ? c->icon_title : c->title; + + /* use the transient's parent's title/icon if we don't have one */ + if (p != c && title[0] == '\0') + title = p->iconic ? p->icon_title : p->title; + + if (desk) + ret = g_strdup_printf("%s [%s]", title, desk); + else + ret = g_strdup(title); + + return ret; +} + +static void popup_render(ObFocusCyclePopup *p, const ObClient *c) +{ + gint ml, mt, mr, mb; + gint l, t, r, b; + gint x, y, w, h; + const Rect *screen_area = NULL; + gint i; + GList *it; + const ObFocusCyclePopupTarget *newtarget; + ObFocusCyclePopupMode mode = p->mode; + gint icons_per_row; + gint icon_rows; + gint textw, texth; + gint selected_pos; + gint last_scroll; + + /* vars for icon mode */ + gint icon_mode_textx; + gint icon_mode_texty; + gint icons_center_x; + + /* vars for list mode */ + gint list_mode_icon_column_w = HILITE_SIZE + OUTSIDE_BORDER; + gint up_arrow_x, down_arrow_x; + gint up_arrow_y, down_arrow_y; + gboolean showing_arrows = FALSE; + + g_assert(mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS || + mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST); + + screen_area = screen_physical_area_primary(FALSE); + + /* get the outside margins */ + RrMargins(p->a_bg, &ml, &mt, &mr, &mb); + + /* get our outside borders */ + l = ml + OUTSIDE_BORDER; + r = mr + OUTSIDE_BORDER; + t = mt + OUTSIDE_BORDER; + b = mb + OUTSIDE_BORDER; + + /* get the width from the text and keep it within limits */ + w = l + r + p->maxtextw; + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + /* when in list mode, there are icons down the side */ + w += list_mode_icon_column_w; + w = MIN(w, MAX(screen_area->width/3, POPUP_WIDTH)); /* max width */ + w = MAX(w, POPUP_WIDTH); /* min width */ + + /* get the text height */ + texth = RrMinHeight(p->a_hilite_text); + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + texth = MAX(MAX(texth, RrMinHeight(p->a_text)), HILITE_SIZE); + else + texth += TEXT_BORDER * 2; + + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) { + /* how many icons will fit in that row? make the width fit that */ + w -= l + r; + icons_per_row = (w + HILITE_SIZE - 1) / HILITE_SIZE; + w = icons_per_row * HILITE_SIZE + l + r; + + /* how many rows do we need? */ + icon_rows = (p->n_targets-1) / icons_per_row + 1; + } else { + /* in list mode, there is one column of icons.. */ + icons_per_row = 1; + /* maximum is 80% of the screen height */ + icon_rows = MIN(p->n_targets, + (4*screen_area->height/5) /* 80% of the screen */ + / + MAX(HILITE_SIZE, texth)); /* height of each row */ + /* but make sure there is always one */ + icon_rows = MAX(icon_rows, 1); + } + + /* get the text width */ + textw = w - l - r; + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + /* leave space on the side for the icons */ + textw -= list_mode_icon_column_w; + + if (!p->mapped) + /* reset the scrolling when the dialog is first shown */ + p->scroll = 0; + + /* find the height of the dialog */ + h = t + b + (icon_rows * MAX(HILITE_SIZE, texth)); + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) + /* in icon mode the text sits below the icons, so make some space */ + h += OUTSIDE_BORDER + texth; + + /* find the focused target */ + newtarget = NULL; + for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) { + const ObFocusCyclePopupTarget *target = it->data; + if (target->client == c) { + /* save the target */ + newtarget = target; + break; + } + } + selected_pos = i; + g_assert(newtarget != NULL); + + /* scroll the list if needed */ + last_scroll = p->scroll; + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) { + const gint top = p->scroll + SCROLL_MARGIN; + const gint bottom = p->scroll + icon_rows - SCROLL_MARGIN; + const gint min_scroll = 0; + const gint max_scroll = p->n_targets - icon_rows; + + if (top - selected_pos >= 0) { + p->scroll -= top - selected_pos + 1; + p->scroll = MAX(p->scroll, min_scroll); + } else if (selected_pos - bottom >= 0) { + p->scroll += selected_pos - bottom + 1; + p->scroll = MIN(p->scroll, max_scroll); + } + } + + /* show the scroll arrows when appropriate */ + if (p->scroll && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) { + XMapWindow(obt_display, p->list_mode_up); + showing_arrows = TRUE; + } else + XUnmapWindow(obt_display, p->list_mode_up); + + if (p->scroll < p->n_targets - icon_rows && + mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + { + XMapWindow(obt_display, p->list_mode_down); + showing_arrows = TRUE; + } else + XUnmapWindow(obt_display, p->list_mode_down); + + /* make space for the arrows */ + if (showing_arrows) + h += ob_rr_theme->up_arrow_mask->height + OUTSIDE_BORDER + + ob_rr_theme->down_arrow_mask->height + OUTSIDE_BORDER; + + /* center the icons if there is less than one row */ + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS && icon_rows == 1) + icons_center_x = (w - p->n_targets * HILITE_SIZE) / 2; + else + icons_center_x = 0; + + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) { + /* get the position of the text */ + icon_mode_textx = l; + icon_mode_texty = h - texth - b; + } + + /* find the position for the popup (include the outer borders) */ + x = screen_area->x + (screen_area->width - + (w + ob_rr_theme->obwidth * 2)) / 2; + y = screen_area->y + (screen_area->height - + (h + ob_rr_theme->obwidth * 2)) / 2; + + if (!p->mapped) { + /* position the background but don't draw it */ + XMoveResizeWindow(obt_display, p->bg, x, y, w, h); + + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS) { + /* position the text */ + XMoveResizeWindow(obt_display, p->icon_mode_text, + icon_mode_textx, icon_mode_texty, textw, texth); + XMapWindow(obt_display, popup.icon_mode_text); + } else { + XUnmapWindow(obt_display, popup.icon_mode_text); + + up_arrow_x = (w - ob_rr_theme->up_arrow_mask->width) / 2; + up_arrow_y = t; + + down_arrow_x = (w - ob_rr_theme->down_arrow_mask->width) / 2; + down_arrow_y = h - b - ob_rr_theme->down_arrow_mask->height; + + /* position the arrows */ + XMoveResizeWindow(obt_display, p->list_mode_up, + up_arrow_x, up_arrow_y, + ob_rr_theme->up_arrow_mask->width, + ob_rr_theme->up_arrow_mask->height); + XMoveResizeWindow(obt_display, p->list_mode_down, + down_arrow_x, down_arrow_y, + ob_rr_theme->down_arrow_mask->width, + ob_rr_theme->down_arrow_mask->height); + } + } + + /* * * draw everything * * */ + + /* draw the background */ + if (!p->mapped) + RrPaint(p->a_bg, p->bg, w, h); + + /* draw the scroll arrows */ + if (!p->mapped && mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) { + p->a_arrow->texture[0].data.mask.mask = + ob_rr_theme->up_arrow_mask; + p->a_arrow->surface.parent = p->a_bg; + p->a_arrow->surface.parentx = up_arrow_x; + p->a_arrow->surface.parenty = up_arrow_y; + RrPaint(p->a_arrow, p->list_mode_up, + ob_rr_theme->up_arrow_mask->width, + ob_rr_theme->up_arrow_mask->height); + + p->a_arrow->texture[0].data.mask.mask = + ob_rr_theme->down_arrow_mask; + p->a_arrow->surface.parent = p->a_bg; + p->a_arrow->surface.parentx = down_arrow_x; + p->a_arrow->surface.parenty = down_arrow_y; + RrPaint(p->a_arrow, p->list_mode_down, + ob_rr_theme->down_arrow_mask->width, + ob_rr_theme->down_arrow_mask->height); + } + + /* draw the icons and text */ + for (i = 0, it = p->targets; it; ++i, it = g_list_next(it)) { + const ObFocusCyclePopupTarget *target = it->data; + + /* have to redraw the targetted icon and last targetted icon + * to update the hilite */ + if (!p->mapped || newtarget == target || p->last_target == target || + last_scroll != p->scroll) + { + /* row and column start from 0 */ + const gint row = i / icons_per_row - p->scroll; + const gint col = i % icons_per_row; + gint iconx, icony; + gint list_mode_textx, list_mode_texty; + RrAppearance *text; + + /* find the coordinates for the icon */ + iconx = icons_center_x + l + (col * HILITE_SIZE); + icony = t + (showing_arrows ? ob_rr_theme->up_arrow_mask->height + + OUTSIDE_BORDER + : 0) + + (row * MAX(texth, HILITE_SIZE)) + + MAX(texth - HILITE_SIZE, 0) / 2; + + /* find the dimensions of the text box */ + list_mode_textx = iconx + HILITE_SIZE + TEXT_BORDER; + list_mode_texty = icony; + + /* position the icon */ + XMoveResizeWindow(obt_display, target->iconwin, + iconx, icony, HILITE_SIZE, HILITE_SIZE); + + /* position the text */ + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + XMoveResizeWindow(obt_display, target->textwin, + list_mode_textx, list_mode_texty, + textw, texth); + + /* show/hide the right windows */ + if (row >= 0 && row < icon_rows) { + XMapWindow(obt_display, target->iconwin); + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + XMapWindow(obt_display, target->textwin); + else + XUnmapWindow(obt_display, target->textwin); + } else { + XUnmapWindow(obt_display, target->textwin); + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST) + XUnmapWindow(obt_display, target->iconwin); + else + XMapWindow(obt_display, target->iconwin); + } + + /* get the icon from the client */ + p->a_icon->texture[0].data.image.twidth = ICON_SIZE; + p->a_icon->texture[0].data.image.theight = ICON_SIZE; + p->a_icon->texture[0].data.image.tx = HILITE_OFFSET; + p->a_icon->texture[0].data.image.ty = HILITE_OFFSET; + p->a_icon->texture[0].data.image.alpha = + target->client->iconic ? OB_ICONIC_ALPHA : 0xff; + p->a_icon->texture[0].data.image.image = target->icon; + + /* Draw the hilite? */ + p->a_icon->texture[1].type = (target == newtarget) ? + RR_TEXTURE_RGBA : RR_TEXTURE_NONE; + + /* draw the icon */ + p->a_icon->surface.parentx = iconx; + p->a_icon->surface.parenty = icony; + RrPaint(p->a_icon, target->iconwin, HILITE_SIZE, HILITE_SIZE); + + /* draw the text */ + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_LIST || + target == newtarget) + { + text = (target == newtarget) ? p->a_hilite_text : p->a_text; + text->texture[0].data.text.string = target->text; + text->surface.parentx = + mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ? + icon_mode_textx : list_mode_textx; + text->surface.parenty = + mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ? + icon_mode_texty : list_mode_texty; + RrPaint(text, + (mode == OB_FOCUS_CYCLE_POPUP_MODE_ICONS ? + p->icon_mode_text : target->textwin), + textw, texth); + } + } + } + + p->last_target = newtarget; + + XFlush(obt_display); +} + +void focus_cycle_popup_show(ObClient *c, ObFocusCyclePopupMode mode, + gboolean linear) +{ + g_assert(c != NULL); + + if (mode == OB_FOCUS_CYCLE_POPUP_MODE_NONE) { + focus_cycle_popup_hide(); + return; + } + + /* do this stuff only when the dialog is first showing */ + if (!popup.mapped) { + popup_setup(&popup, TRUE, FALSE, linear); + /* this is fixed once the dialog is shown */ + popup.mode = mode; + } + g_assert(popup.targets != NULL); + + popup_render(&popup, c); + + if (!popup.mapped) { + /* show the dialog */ + XMapWindow(obt_display, popup.bg); + XFlush(obt_display); + popup.mapped = TRUE; + screen_hide_desktop_popup(); + } +} + +void focus_cycle_popup_hide(void) +{ + gulong ignore_start; + + ignore_start = event_start_ignore_all_enters(); + + XUnmapWindow(obt_display, popup.bg); + XFlush(obt_display); + + event_end_ignore_all_enters(ignore_start); + + popup.mapped = FALSE; + + popup_cleanup(); +} + +void focus_cycle_popup_single_show(struct _ObClient *c) +{ + gchar *text; + + g_assert(c != NULL); + + /* do this stuff only when the dialog is first showing */ + if (!single_popup->popup->mapped) { + const Rect *a; + + /* position the popup */ + a = screen_physical_area_primary(FALSE); + icon_popup_position(single_popup, CenterGravity, + a->x + a->width / 2, a->y + a->height / 2); + icon_popup_height(single_popup, POPUP_HEIGHT); + icon_popup_min_width(single_popup, POPUP_WIDTH); + icon_popup_max_width(single_popup, MAX(a->width/3, POPUP_WIDTH)); + icon_popup_text_width(single_popup, popup.maxtextw); + } + + text = popup_get_name(c); + icon_popup_show(single_popup, text, client_icon(c)); + g_free(text); + screen_hide_desktop_popup(); +} + +void focus_cycle_popup_single_hide(void) +{ + icon_popup_hide(single_popup); +} + +gboolean focus_cycle_popup_is_showing(ObClient *c) +{ + if (popup.mapped) { + GList *it; + + for (it = popup.targets; it; it = g_list_next(it)) { + ObFocusCyclePopupTarget *t = it->data; + if (t->client == c) + return TRUE; + } + } + return FALSE; +} + +static ObClient* popup_revert(ObClient *target) +{ + GList *it, *itt; + + for (it = popup.targets; it; it = g_list_next(it)) { + ObFocusCyclePopupTarget *t = it->data; + if (t->client == target) { + /* move to a previous window if possible */ + for (itt = it->prev; itt; itt = g_list_previous(itt)) { + ObFocusCyclePopupTarget *t2 = itt->data; + if (focus_cycle_valid(t2->client)) + return t2->client; + } + + /* otherwise move to a following window if possible */ + for (itt = it->next; itt; itt = g_list_next(itt)) { + ObFocusCyclePopupTarget *t2 = itt->data; + if (focus_cycle_valid(t2->client)) + return t2->client; + } + + /* otherwise, we can't go anywhere there is nowhere valid to go */ + return NULL; + } + } + return NULL; +} + +ObClient* focus_cycle_popup_refresh(ObClient *target, + gboolean redraw, + gboolean linear) +{ + if (!popup.mapped) return NULL; + + if (!focus_cycle_valid(target)) + target = popup_revert(target); + + redraw = popup_setup(&popup, TRUE, TRUE, linear) && redraw; + + if (!target && popup.targets) + target = ((ObFocusCyclePopupTarget*)popup.targets->data)->client; + + if (target && redraw) { + popup.mapped = FALSE; + popup_render(&popup, target); + XFlush(obt_display); + popup.mapped = TRUE; + } + + return target; +} diff --git a/openbox/focus_cycle_popup.h b/openbox/focus_cycle_popup.h new file mode 100644 index 0000000..8c80bfc --- /dev/null +++ b/openbox/focus_cycle_popup.h @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focus_cycle.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __focus_cycle_popup_h +#define __focus_cycle_popup_h + +struct _ObClient; + +#include <glib.h> + +typedef enum { + OB_FOCUS_CYCLE_POPUP_MODE_NONE, + OB_FOCUS_CYCLE_POPUP_MODE_ICONS, + OB_FOCUS_CYCLE_POPUP_MODE_LIST +} ObFocusCyclePopupMode; + +void focus_cycle_popup_startup(gboolean reconfig); +void focus_cycle_popup_shutdown(gboolean reconfig); + +void focus_cycle_popup_show(struct _ObClient *c, ObFocusCyclePopupMode mode, + gboolean linear); +void focus_cycle_popup_hide(void); + +void focus_cycle_popup_single_show(struct _ObClient *c); +void focus_cycle_popup_single_hide(void); + +gboolean focus_cycle_popup_is_showing(struct _ObClient *c); + +/*! Redraws the focus cycle popup, and returns the current target. If + the target given to the function is no longer valid, this will return + a different target that is valid, and which should be considered the + current focus cycling target. */ +struct _ObClient *focus_cycle_popup_refresh(struct _ObClient *target, + gboolean redraw, + gboolean linear); + +#endif diff --git a/openbox/frame.c b/openbox/frame.c new file mode 100644 index 0000000..48dda24 --- /dev/null +++ b/openbox/frame.c @@ -0,0 +1,1866 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + frame.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "frame.h" +#include "client.h" +#include "openbox.h" +#include "grab.h" +#include "debug.h" +#include "config.h" +#include "framerender.h" +#include "focus_cycle.h" +#include "focus_cycle_indicator.h" +#include "moveresize.h" +#include "screen.h" +#include "obrender/theme.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/prop.h" + +#define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \ + ButtonPressMask | ButtonReleaseMask | \ + SubstructureRedirectMask | FocusChangeMask) +#define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \ + ButtonMotionMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask) + +#define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */ +#define FRAME_ANIMATE_ICONIFY_STEP_TIME (1000 / 60) /* 60 Hz */ + +#define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b) + +static void flash_done(gpointer data); +static gboolean flash_timeout(gpointer data); + +static void layout_title(ObFrame *self); +static void set_theme_statics(ObFrame *self); +static void free_theme_statics(ObFrame *self); +static gboolean frame_animate_iconify(gpointer self); +static void frame_adjust_cursors(ObFrame *self); + +static Window createWindow(Window parent, Visual *visual, + gulong mask, XSetWindowAttributes *attrib) +{ + return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, + (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput, + (visual ? visual : RrVisual(ob_rr_inst)), + mask, attrib); + +} + +static Visual *check_32bit_client(ObClient *c) +{ + XWindowAttributes wattrib; + Status ret; + + /* we're already running at 32 bit depth, yay. we don't need to use their + visual */ + if (RrDepth(ob_rr_inst) == 32) + return NULL; + + ret = XGetWindowAttributes(obt_display, c->window, &wattrib); + g_assert(ret != BadDrawable); + g_assert(ret != BadWindow); + + if (wattrib.depth == 32) + return wattrib.visual; + return NULL; +} + +ObFrame *frame_new(ObClient *client) +{ + XSetWindowAttributes attrib; + gulong mask; + ObFrame *self; + Visual *visual; + + self = g_slice_new0(ObFrame); + self->client = client; + + visual = check_32bit_client(client); + + /* create the non-visible decor windows */ + + mask = 0; + if (visual) { + /* client has a 32-bit visual */ + mask = CWColormap | CWBackPixel | CWBorderPixel; + /* create a colormap with the visual */ + self->colormap = attrib.colormap = + XCreateColormap(obt_display, obt_root(ob_screen), + visual, AllocNone); + attrib.background_pixel = BlackPixel(obt_display, ob_screen); + attrib.border_pixel = BlackPixel(obt_display, ob_screen); + } + self->window = createWindow(obt_root(ob_screen), visual, + mask, &attrib); + + /* create the visible decor windows */ + + mask = 0; + if (visual) { + /* client has a 32-bit visual */ + mask = CWColormap | CWBackPixel | CWBorderPixel; + attrib.colormap = RrColormap(ob_rr_inst); + } + + self->backback = createWindow(self->window, NULL, mask, &attrib); + self->backfront = createWindow(self->backback, NULL, mask, &attrib); + + mask |= CWEventMask; + attrib.event_mask = ELEMENT_EVENTMASK; + self->innerleft = createWindow(self->window, NULL, mask, &attrib); + self->innertop = createWindow(self->window, NULL, mask, &attrib); + self->innerright = createWindow(self->window, NULL, mask, &attrib); + self->innerbottom = createWindow(self->window, NULL, mask, &attrib); + + self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib); + self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib); + self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib); + self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib); + + self->title = createWindow(self->window, NULL, mask, &attrib); + self->titleleft = createWindow(self->window, NULL, mask, &attrib); + self->titletop = createWindow(self->window, NULL, mask, &attrib); + self->titletopleft = createWindow(self->window, NULL, mask, &attrib); + self->titletopright = createWindow(self->window, NULL, mask, &attrib); + self->titleright = createWindow(self->window, NULL, mask, &attrib); + self->titlebottom = createWindow(self->window, NULL, mask, &attrib); + + self->topresize = createWindow(self->title, NULL, mask, &attrib); + self->tltresize = createWindow(self->title, NULL, mask, &attrib); + self->tllresize = createWindow(self->title, NULL, mask, &attrib); + self->trtresize = createWindow(self->title, NULL, mask, &attrib); + self->trrresize = createWindow(self->title, NULL, mask, &attrib); + + self->left = createWindow(self->window, NULL, mask, &attrib); + self->right = createWindow(self->window, NULL, mask, &attrib); + + self->label = createWindow(self->title, NULL, mask, &attrib); + self->max = createWindow(self->title, NULL, mask, &attrib); + self->close = createWindow(self->title, NULL, mask, &attrib); + self->desk = createWindow(self->title, NULL, mask, &attrib); + self->shade = createWindow(self->title, NULL, mask, &attrib); + self->icon = createWindow(self->title, NULL, mask, &attrib); + self->iconify = createWindow(self->title, NULL, mask, &attrib); + + self->handle = createWindow(self->window, NULL, mask, &attrib); + self->lgrip = createWindow(self->handle, NULL, mask, &attrib); + self->rgrip = createWindow(self->handle, NULL, mask, &attrib); + + self->handleleft = createWindow(self->handle, NULL, mask, &attrib); + self->handleright = createWindow(self->handle, NULL, mask, &attrib); + + self->handletop = createWindow(self->window, NULL, mask, &attrib); + self->handlebottom = createWindow(self->window, NULL, mask, &attrib); + self->lgripleft = createWindow(self->window, NULL, mask, &attrib); + self->lgriptop = createWindow(self->window, NULL, mask, &attrib); + self->lgripbottom = createWindow(self->window, NULL, mask, &attrib); + self->rgripright = createWindow(self->window, NULL, mask, &attrib); + self->rgriptop = createWindow(self->window, NULL, mask, &attrib); + self->rgripbottom = createWindow(self->window, NULL, mask, &attrib); + + self->focused = FALSE; + + /* the other stuff is shown based on decor settings */ + XMapWindow(obt_display, self->label); + XMapWindow(obt_display, self->backback); + XMapWindow(obt_display, self->backfront); + + self->max_press = self->close_press = self->desk_press = + self->iconify_press = self->shade_press = FALSE; + self->max_hover = self->close_hover = self->desk_hover = + self->iconify_hover = self->shade_hover = FALSE; + + /* make sure the size will be different the first time, so the extent hints + will be set */ + STRUT_SET(self->oldsize, -1, -1, -1, -1); + + set_theme_statics(self); + + return self; +} + +static void set_theme_statics(ObFrame *self) +{ + /* set colors/appearance/sizes for stuff that doesn't change */ + XResizeWindow(obt_display, self->max, + ob_rr_theme->button_size, ob_rr_theme->button_size); + XResizeWindow(obt_display, self->iconify, + ob_rr_theme->button_size, ob_rr_theme->button_size); + XResizeWindow(obt_display, self->icon, + ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2); + XResizeWindow(obt_display, self->close, + ob_rr_theme->button_size, ob_rr_theme->button_size); + XResizeWindow(obt_display, self->desk, + ob_rr_theme->button_size, ob_rr_theme->button_size); + XResizeWindow(obt_display, self->shade, + ob_rr_theme->button_size, ob_rr_theme->button_size); + XResizeWindow(obt_display, self->tltresize, + ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + XResizeWindow(obt_display, self->trtresize, + ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + XResizeWindow(obt_display, self->tllresize, + ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); + XResizeWindow(obt_display, self->trrresize, + ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); +} + +static void free_theme_statics(ObFrame *self) +{ +} + +void frame_free(ObFrame *self) +{ + free_theme_statics(self); + + XDestroyWindow(obt_display, self->window); + if (self->colormap) + XFreeColormap(obt_display, self->colormap); + + g_slice_free(ObFrame, self); +} + +void frame_show(ObFrame *self) +{ + if (!self->visible) { + self->visible = TRUE; + framerender_frame(self); + /* Grab the server to make sure that the frame window is mapped before + the client gets its MapNotify, i.e. to make sure the client is + _visible_ when it gets MapNotify. */ + grab_server(TRUE); + XMapWindow(obt_display, self->client->window); + XMapWindow(obt_display, self->window); + grab_server(FALSE); + } +} + +void frame_hide(ObFrame *self) +{ + if (self->visible) { + self->visible = FALSE; + if (!frame_iconify_animating(self)) + XUnmapWindow(obt_display, self->window); + /* we unmap the client itself so that we can get MapRequest + events, and because the ICCCM tells us to! */ + XUnmapWindow(obt_display, self->client->window); + self->client->ignore_unmaps += 1; + } +} + +void frame_adjust_theme(ObFrame *self) +{ + free_theme_statics(self); + set_theme_statics(self); +} + +#ifdef SHAPE +void frame_adjust_shape_kind(ObFrame *self, int kind) +{ + gint num; + XRectangle xrect[2]; + gboolean shaped; + + shaped = (kind == ShapeBounding && self->client->shaped); +#ifdef ShapeInput + shaped |= (kind == ShapeInput && self->client->shaped_input); +#endif + + if (!shaped) { + /* clear the shape on the frame window */ + XShapeCombineMask(obt_display, self->window, kind, + self->size.left, + self->size.top, + None, ShapeSet); + } else { + /* make the frame's shape match the clients */ + XShapeCombineShape(obt_display, self->window, kind, + self->size.left, + self->size.top, + self->client->window, + kind, ShapeSet); + + num = 0; + if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { + xrect[0].x = 0; + xrect[0].y = 0; + xrect[0].width = self->area.width; + xrect[0].height = self->size.top; + ++num; + } + + if (self->decorations & OB_FRAME_DECOR_HANDLE && + ob_rr_theme->handle_height > 0) + { + xrect[1].x = 0; + xrect[1].y = FRAME_HANDLE_Y(self); + xrect[1].width = self->area.width; + xrect[1].height = ob_rr_theme->handle_height + + self->bwidth * 2; + ++num; + } + + XShapeCombineRectangles(obt_display, self->window, + ShapeBounding, 0, 0, xrect, num, + ShapeUnion, Unsorted); + } +} +#endif + +void frame_adjust_shape(ObFrame *self) +{ +#ifdef SHAPE + frame_adjust_shape_kind(self, ShapeBounding); +#ifdef ShapeInput + frame_adjust_shape_kind(self, ShapeInput); +#endif +#endif +} + +void frame_adjust_area(ObFrame *self, gboolean moved, + gboolean resized, gboolean fake) +{ + if (resized) { + /* do this before changing the frame's status like max_horz max_vert */ + frame_adjust_cursors(self); + + self->functions = self->client->functions; + self->decorations = self->client->decorations; + self->max_horz = self->client->max_horz; + self->max_vert = self->client->max_vert; + self->shaded = self->client->shaded; + + if (self->decorations & OB_FRAME_DECOR_BORDER) + self->bwidth = self->client->undecorated ? + ob_rr_theme->ubwidth : ob_rr_theme->fbwidth; + else + self->bwidth = 0; + + if (self->decorations & OB_FRAME_DECOR_BORDER && + !self->client->undecorated) + { + self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx; + self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy; + } else + self->cbwidth_l = self->cbwidth_t = + self->cbwidth_r = self->cbwidth_b = 0; + + if (self->max_horz) { + self->cbwidth_l = self->cbwidth_r = 0; + self->width = self->client->area.width; + if (self->max_vert) + self->cbwidth_b = 0; + } else + self->width = self->client->area.width + + self->cbwidth_l + self->cbwidth_r; + + /* some elements are sized based of the width, so don't let them have + negative values */ + self->width = MAX(self->width, + (ob_rr_theme->grip_width + self->bwidth) * 2 + 1); + + STRUT_SET(self->size, + self->cbwidth_l + (!self->max_horz ? self->bwidth : 0), + self->cbwidth_t + self->bwidth, + self->cbwidth_r + (!self->max_horz ? self->bwidth : 0), + self->cbwidth_b + + (!self->max_horz || !self->max_vert ? self->bwidth : 0)); + + if (self->decorations & OB_FRAME_DECOR_TITLEBAR) + self->size.top += ob_rr_theme->title_height + self->bwidth; + if (self->decorations & OB_FRAME_DECOR_HANDLE && + ob_rr_theme->handle_height > 0) + { + self->size.bottom += ob_rr_theme->handle_height + self->bwidth; + } + + /* position/size and map/unmap all the windows */ + + if (!fake) { + gint innercornerheight = + ob_rr_theme->grip_width - self->size.bottom; + + if (self->cbwidth_l) { + XMoveResizeWindow(obt_display, self->innerleft, + self->size.left - self->cbwidth_l, + self->size.top, + self->cbwidth_l, self->client->area.height); + + XMapWindow(obt_display, self->innerleft); + } else + XUnmapWindow(obt_display, self->innerleft); + + if (self->cbwidth_l && innercornerheight > 0) { + XMoveResizeWindow(obt_display, self->innerbll, + 0, + self->client->area.height - + (ob_rr_theme->grip_width - + self->size.bottom), + self->cbwidth_l, + ob_rr_theme->grip_width - self->size.bottom); + + XMapWindow(obt_display, self->innerbll); + } else + XUnmapWindow(obt_display, self->innerbll); + + if (self->cbwidth_r) { + XMoveResizeWindow(obt_display, self->innerright, + self->size.left + self->client->area.width, + self->size.top, + self->cbwidth_r, self->client->area.height); + + XMapWindow(obt_display, self->innerright); + } else + XUnmapWindow(obt_display, self->innerright); + + if (self->cbwidth_r && innercornerheight > 0) { + XMoveResizeWindow(obt_display, self->innerbrr, + 0, + self->client->area.height - + (ob_rr_theme->grip_width - + self->size.bottom), + self->cbwidth_r, + ob_rr_theme->grip_width - self->size.bottom); + + XMapWindow(obt_display, self->innerbrr); + } else + XUnmapWindow(obt_display, self->innerbrr); + + if (self->cbwidth_t) { + XMoveResizeWindow(obt_display, self->innertop, + self->size.left - self->cbwidth_l, + self->size.top - self->cbwidth_t, + self->client->area.width + + self->cbwidth_l + self->cbwidth_r, + self->cbwidth_t); + + XMapWindow(obt_display, self->innertop); + } else + XUnmapWindow(obt_display, self->innertop); + + if (self->cbwidth_b) { + XMoveResizeWindow(obt_display, self->innerbottom, + self->size.left - self->cbwidth_l, + self->size.top + self->client->area.height, + self->client->area.width + + self->cbwidth_l + self->cbwidth_r, + self->cbwidth_b); + + XMoveResizeWindow(obt_display, self->innerblb, + 0, 0, + ob_rr_theme->grip_width + self->bwidth, + self->cbwidth_b); + XMoveResizeWindow(obt_display, self->innerbrb, + self->client->area.width + + self->cbwidth_l + self->cbwidth_r - + (ob_rr_theme->grip_width + self->bwidth), + 0, + ob_rr_theme->grip_width + self->bwidth, + self->cbwidth_b); + + XMapWindow(obt_display, self->innerbottom); + XMapWindow(obt_display, self->innerblb); + XMapWindow(obt_display, self->innerbrb); + } else { + XUnmapWindow(obt_display, self->innerbottom); + XUnmapWindow(obt_display, self->innerblb); + XUnmapWindow(obt_display, self->innerbrb); + } + + if (self->bwidth) { + gint titlesides; + + /* height of titleleft and titleright */ + titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0); + + XMoveResizeWindow(obt_display, self->titletop, + ob_rr_theme->grip_width + self->bwidth, 0, + /* width + bwidth*2 - bwidth*2 - grips*2 */ + self->width - ob_rr_theme->grip_width * 2, + self->bwidth); + XMoveResizeWindow(obt_display, self->titletopleft, + 0, 0, + ob_rr_theme->grip_width + self->bwidth, + self->bwidth); + XMoveResizeWindow(obt_display, self->titletopright, + self->client->area.width + + self->size.left + self->size.right - + ob_rr_theme->grip_width - self->bwidth, + 0, + ob_rr_theme->grip_width + self->bwidth, + self->bwidth); + + if (titlesides > 0) { + XMoveResizeWindow(obt_display, self->titleleft, + 0, self->bwidth, + self->bwidth, + titlesides); + XMoveResizeWindow(obt_display, self->titleright, + self->client->area.width + + self->size.left + self->size.right - + self->bwidth, + self->bwidth, + self->bwidth, + titlesides); + + XMapWindow(obt_display, self->titleleft); + XMapWindow(obt_display, self->titleright); + } else { + XUnmapWindow(obt_display, self->titleleft); + XUnmapWindow(obt_display, self->titleright); + } + + XMapWindow(obt_display, self->titletop); + XMapWindow(obt_display, self->titletopleft); + XMapWindow(obt_display, self->titletopright); + + if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { + XMoveResizeWindow(obt_display, self->titlebottom, + (self->max_horz ? 0 : self->bwidth), + ob_rr_theme->title_height + self->bwidth, + self->width, + self->bwidth); + + XMapWindow(obt_display, self->titlebottom); + } else + XUnmapWindow(obt_display, self->titlebottom); + } else { + XUnmapWindow(obt_display, self->titlebottom); + + XUnmapWindow(obt_display, self->titletop); + XUnmapWindow(obt_display, self->titletopleft); + XUnmapWindow(obt_display, self->titletopright); + XUnmapWindow(obt_display, self->titleleft); + XUnmapWindow(obt_display, self->titleright); + } + + if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { + XMoveResizeWindow(obt_display, self->title, + (self->max_horz ? 0 : self->bwidth), + self->bwidth, + self->width, ob_rr_theme->title_height); + + XMapWindow(obt_display, self->title); + + if (self->decorations & OB_FRAME_DECOR_GRIPS) { + XMoveResizeWindow(obt_display, self->topresize, + ob_rr_theme->grip_width, + 0, + self->width - ob_rr_theme->grip_width *2, + ob_rr_theme->paddingy + 1); + + XMoveWindow(obt_display, self->tltresize, 0, 0); + XMoveWindow(obt_display, self->tllresize, 0, 0); + XMoveWindow(obt_display, self->trtresize, + self->width - ob_rr_theme->grip_width, 0); + XMoveWindow(obt_display, self->trrresize, + self->width - ob_rr_theme->paddingx - 1, 0); + + XMapWindow(obt_display, self->topresize); + XMapWindow(obt_display, self->tltresize); + XMapWindow(obt_display, self->tllresize); + XMapWindow(obt_display, self->trtresize); + XMapWindow(obt_display, self->trrresize); + } else { + XUnmapWindow(obt_display, self->topresize); + XUnmapWindow(obt_display, self->tltresize); + XUnmapWindow(obt_display, self->tllresize); + XUnmapWindow(obt_display, self->trtresize); + XUnmapWindow(obt_display, self->trrresize); + } + } else + XUnmapWindow(obt_display, self->title); + } + + if ((self->decorations & OB_FRAME_DECOR_TITLEBAR)) + /* layout the title bar elements */ + layout_title(self); + + if (!fake) { + gint sidebwidth = self->max_horz ? 0 : self->bwidth; + + if (self->bwidth && self->size.bottom) { + XMoveResizeWindow(obt_display, self->handlebottom, + ob_rr_theme->grip_width + + self->bwidth + sidebwidth, + self->size.top + self->client->area.height + + self->size.bottom - self->bwidth, + self->width - (ob_rr_theme->grip_width + + sidebwidth) * 2, + self->bwidth); + + + if (sidebwidth) { + XMoveResizeWindow(obt_display, self->lgripleft, + 0, + self->size.top + + self->client->area.height + + self->size.bottom - + (!self->max_horz ? + ob_rr_theme->grip_width : + self->size.bottom - self->cbwidth_b), + self->bwidth, + (!self->max_horz ? + ob_rr_theme->grip_width : + self->size.bottom - self->cbwidth_b)); + XMoveResizeWindow(obt_display, self->rgripright, + self->size.left + + self->client->area.width + + self->size.right - self->bwidth, + self->size.top + + self->client->area.height + + self->size.bottom - + (!self->max_horz ? + ob_rr_theme->grip_width : + self->size.bottom - self->cbwidth_b), + self->bwidth, + (!self->max_horz ? + ob_rr_theme->grip_width : + self->size.bottom - self->cbwidth_b)); + + XMapWindow(obt_display, self->lgripleft); + XMapWindow(obt_display, self->rgripright); + } else { + XUnmapWindow(obt_display, self->lgripleft); + XUnmapWindow(obt_display, self->rgripright); + } + + XMoveResizeWindow(obt_display, self->lgripbottom, + sidebwidth, + self->size.top + self->client->area.height + + self->size.bottom - self->bwidth, + ob_rr_theme->grip_width + self->bwidth, + self->bwidth); + XMoveResizeWindow(obt_display, self->rgripbottom, + self->size.left + self->client->area.width + + self->size.right - self->bwidth - sidebwidth- + ob_rr_theme->grip_width, + self->size.top + self->client->area.height + + self->size.bottom - self->bwidth, + ob_rr_theme->grip_width + self->bwidth, + self->bwidth); + + XMapWindow(obt_display, self->handlebottom); + XMapWindow(obt_display, self->lgripbottom); + XMapWindow(obt_display, self->rgripbottom); + + if (self->decorations & OB_FRAME_DECOR_HANDLE && + ob_rr_theme->handle_height > 0) + { + XMoveResizeWindow(obt_display, self->handletop, + ob_rr_theme->grip_width + + self->bwidth + sidebwidth, + FRAME_HANDLE_Y(self), + self->width - (ob_rr_theme->grip_width + + sidebwidth) * 2, + self->bwidth); + XMapWindow(obt_display, self->handletop); + + if (self->decorations & OB_FRAME_DECOR_GRIPS) { + XMoveResizeWindow(obt_display, self->handleleft, + ob_rr_theme->grip_width, + 0, + self->bwidth, + ob_rr_theme->handle_height); + XMoveResizeWindow(obt_display, self->handleright, + self->width - + ob_rr_theme->grip_width - + self->bwidth, + 0, + self->bwidth, + ob_rr_theme->handle_height); + + XMoveResizeWindow(obt_display, self->lgriptop, + sidebwidth, + FRAME_HANDLE_Y(self), + ob_rr_theme->grip_width + + self->bwidth, + self->bwidth); + XMoveResizeWindow(obt_display, self->rgriptop, + self->size.left + + self->client->area.width + + self->size.right - self->bwidth - + sidebwidth - ob_rr_theme->grip_width, + FRAME_HANDLE_Y(self), + ob_rr_theme->grip_width + + self->bwidth, + self->bwidth); + + XMapWindow(obt_display, self->handleleft); + XMapWindow(obt_display, self->handleright); + XMapWindow(obt_display, self->lgriptop); + XMapWindow(obt_display, self->rgriptop); + } else { + XUnmapWindow(obt_display, self->handleleft); + XUnmapWindow(obt_display, self->handleright); + XUnmapWindow(obt_display, self->lgriptop); + XUnmapWindow(obt_display, self->rgriptop); + } + } else { + XUnmapWindow(obt_display, self->handleleft); + XUnmapWindow(obt_display, self->handleright); + XUnmapWindow(obt_display, self->lgriptop); + XUnmapWindow(obt_display, self->rgriptop); + + XUnmapWindow(obt_display, self->handletop); + } + } else { + XUnmapWindow(obt_display, self->handleleft); + XUnmapWindow(obt_display, self->handleright); + XUnmapWindow(obt_display, self->lgriptop); + XUnmapWindow(obt_display, self->rgriptop); + + XUnmapWindow(obt_display, self->handletop); + + XUnmapWindow(obt_display, self->handlebottom); + XUnmapWindow(obt_display, self->lgripleft); + XUnmapWindow(obt_display, self->rgripright); + XUnmapWindow(obt_display, self->lgripbottom); + XUnmapWindow(obt_display, self->rgripbottom); + } + + if (self->decorations & OB_FRAME_DECOR_HANDLE && + ob_rr_theme->handle_height > 0) + { + XMoveResizeWindow(obt_display, self->handle, + sidebwidth, + FRAME_HANDLE_Y(self) + self->bwidth, + self->width, ob_rr_theme->handle_height); + XMapWindow(obt_display, self->handle); + + if (self->decorations & OB_FRAME_DECOR_GRIPS) { + XMoveResizeWindow(obt_display, self->lgrip, + 0, 0, + ob_rr_theme->grip_width, + ob_rr_theme->handle_height); + XMoveResizeWindow(obt_display, self->rgrip, + self->width - ob_rr_theme->grip_width, + 0, + ob_rr_theme->grip_width, + ob_rr_theme->handle_height); + + XMapWindow(obt_display, self->lgrip); + XMapWindow(obt_display, self->rgrip); + } else { + XUnmapWindow(obt_display, self->lgrip); + XUnmapWindow(obt_display, self->rgrip); + } + } else { + XUnmapWindow(obt_display, self->lgrip); + XUnmapWindow(obt_display, self->rgrip); + + XUnmapWindow(obt_display, self->handle); + } + + if (self->bwidth && !self->max_horz && + (self->client->area.height + self->size.top + + self->size.bottom) > ob_rr_theme->grip_width * 2) + { + XMoveResizeWindow(obt_display, self->left, + 0, + self->bwidth + ob_rr_theme->grip_width, + self->bwidth, + self->client->area.height + + self->size.top + self->size.bottom - + ob_rr_theme->grip_width * 2); + + XMapWindow(obt_display, self->left); + } else + XUnmapWindow(obt_display, self->left); + + if (self->bwidth && !self->max_horz && + (self->client->area.height + self->size.top + + self->size.bottom) > ob_rr_theme->grip_width * 2) + { + XMoveResizeWindow(obt_display, self->right, + self->client->area.width + self->cbwidth_l + + self->cbwidth_r + self->bwidth, + self->bwidth + ob_rr_theme->grip_width, + self->bwidth, + self->client->area.height + + self->size.top + self->size.bottom - + ob_rr_theme->grip_width * 2); + + XMapWindow(obt_display, self->right); + } else + XUnmapWindow(obt_display, self->right); + + XMoveResizeWindow(obt_display, self->backback, + self->size.left, self->size.top, + self->client->area.width, + self->client->area.height); + } + } + + /* shading can change without being moved or resized */ + RECT_SET_SIZE(self->area, + self->client->area.width + + self->size.left + self->size.right, + (self->client->shaded ? + ob_rr_theme->title_height + self->bwidth * 2: + self->client->area.height + + self->size.top + self->size.bottom)); + + if ((moved || resized) && !fake) { + /* find the new coordinates, done after setting the frame.size, for + frame_client_gravity. */ + self->area.x = self->client->area.x; + self->area.y = self->client->area.y; + frame_client_gravity(self, &self->area.x, &self->area.y); + } + + if (!fake) { + if (!frame_iconify_animating(self)) + /* move and resize the top level frame. + shading can change without being moved or resized. + + but don't do this during an iconify animation. it will be + reflected afterwards. + */ + XMoveResizeWindow(obt_display, self->window, + self->area.x, + self->area.y, + self->area.width, + self->area.height); + + /* when the client has StaticGravity, it likes to move around. + also this correctly positions the client when it maps. + this also needs to be run when the frame's decorations sizes change! + */ + XMoveWindow(obt_display, self->client->window, + self->size.left, self->size.top); + + if (resized) { + self->need_render = TRUE; + framerender_frame(self); + frame_adjust_shape(self); + } + + if (!STRUT_EQUAL(self->size, self->oldsize)) { + gulong vals[4]; + vals[0] = self->size.left; + vals[1] = self->size.right; + vals[2] = self->size.top; + vals[3] = self->size.bottom; + OBT_PROP_SETA32(self->client->window, NET_FRAME_EXTENTS, + CARDINAL, vals, 4); + OBT_PROP_SETA32(self->client->window, KDE_NET_WM_FRAME_STRUT, + CARDINAL, vals, 4); + self->oldsize = self->size; + } + + /* if this occurs while we are focus cycling, the indicator needs to + match the changes */ + if (focus_cycle_target == self->client) + focus_cycle_update_indicator(self->client); + } + if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) && + self->label_width) + { + XResizeWindow(obt_display, self->label, self->label_width, + ob_rr_theme->label_height); + } +} + +static void frame_adjust_cursors(ObFrame *self) +{ + if ((self->functions & OB_CLIENT_FUNC_RESIZE) != + (self->client->functions & OB_CLIENT_FUNC_RESIZE) || + self->max_horz != self->client->max_horz || + self->max_vert != self->client->max_vert || + self->shaded != self->client->shaded) + { + gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) && + !(self->client->max_horz && self->client->max_vert); + gboolean topbot = !self->client->max_vert; + gboolean sh = self->client->shaded; + XSetWindowAttributes a; + + /* these ones turn off when max vert, and some when shaded */ + a.cursor = ob_cursor(r && topbot && !sh ? + OB_CURSOR_NORTH : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a); + a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a); + + /* these ones change when shaded */ + a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) : + OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a); + a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) : + OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a); + + /* these ones are pretty static */ + a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->left, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a); + a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->right, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a); + a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a); + a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE); + XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a); + XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a); + } +} + +void frame_adjust_client_area(ObFrame *self) +{ + /* adjust the window which is there to prevent flashing on unmap */ + XMoveResizeWindow(obt_display, self->backfront, 0, 0, + self->client->area.width, + self->client->area.height); +} + +void frame_adjust_state(ObFrame *self) +{ + self->need_render = TRUE; + framerender_frame(self); +} + +void frame_adjust_focus(ObFrame *self, gboolean hilite) +{ + ob_debug_type(OB_DEBUG_FOCUS, + "Frame for 0x%x has focus: %d", + self->client->window, hilite); + self->focused = hilite; + self->need_render = TRUE; + framerender_frame(self); + XFlush(obt_display); +} + +void frame_adjust_title(ObFrame *self) +{ + self->need_render = TRUE; + framerender_frame(self); +} + +void frame_adjust_icon(ObFrame *self) +{ + self->need_render = TRUE; + framerender_frame(self); +} + +void frame_grab_client(ObFrame *self) +{ + /* DO NOT map the client window here. we used to do that, but it is bogus. + we need to set up the client's dimensions and everything before we + send a mapnotify or we create race conditions. + */ + + /* reparent the client to the frame */ + XReparentWindow(obt_display, self->client->window, self->window, 0, 0); + + /* + When reparenting the client window, it is usually not mapped yet, since + this occurs from a MapRequest. However, in the case where Openbox is + starting up, the window is already mapped, so we'll see an unmap event + for it. + */ + if (ob_state() == OB_STATE_STARTING) + ++self->client->ignore_unmaps; + + /* select the event mask on the client's parent (to receive config/map + req's) the ButtonPress is to catch clicks on the client border */ + XSelectInput(obt_display, self->window, FRAME_EVENTMASK); + + /* set all the windows for the frame in the window_map */ + window_add(&self->window, CLIENT_AS_WINDOW(self->client)); + window_add(&self->backback, CLIENT_AS_WINDOW(self->client)); + window_add(&self->backfront, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innertop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client)); + window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client)); + window_add(&self->title, CLIENT_AS_WINDOW(self->client)); + window_add(&self->label, CLIENT_AS_WINDOW(self->client)); + window_add(&self->max, CLIENT_AS_WINDOW(self->client)); + window_add(&self->close, CLIENT_AS_WINDOW(self->client)); + window_add(&self->desk, CLIENT_AS_WINDOW(self->client)); + window_add(&self->shade, CLIENT_AS_WINDOW(self->client)); + window_add(&self->icon, CLIENT_AS_WINDOW(self->client)); + window_add(&self->iconify, CLIENT_AS_WINDOW(self->client)); + window_add(&self->handle, CLIENT_AS_WINDOW(self->client)); + window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client)); + window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client)); + window_add(&self->topresize, CLIENT_AS_WINDOW(self->client)); + window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client)); + window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client)); + window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client)); + window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client)); + window_add(&self->left, CLIENT_AS_WINDOW(self->client)); + window_add(&self->right, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titletop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titleright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->handletop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->handleright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client)); + window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client)); + window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client)); + window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client)); + window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client)); +} + +static gboolean find_reparent(XEvent *e, gpointer data) +{ + const ObFrame *self = data; + + /* Find ReparentNotify events for the window that aren't being reparented into the + frame, thus the client reparenting itself off the frame. */ + return e->type == ReparentNotify && e->xreparent.window == self->client->window && + e->xreparent.parent != self->window; +} + +void frame_release_client(ObFrame *self) +{ + /* if there was any animation going on, kill it */ + if (self->iconify_animation_timer) + g_source_remove(self->iconify_animation_timer); + + /* check if the app has already reparented its window away */ + if (!xqueue_exists_local(find_reparent, self)) { + /* according to the ICCCM - if the client doesn't reparent itself, + then we will reparent the window to root for them */ + XReparentWindow(obt_display, self->client->window, obt_root(ob_screen), + self->client->area.x, self->client->area.y); + } + + /* remove all the windows for the frame from the window_map */ + window_remove(self->window); + window_remove(self->backback); + window_remove(self->backfront); + window_remove(self->innerleft); + window_remove(self->innertop); + window_remove(self->innerright); + window_remove(self->innerbottom); + window_remove(self->innerblb); + window_remove(self->innerbll); + window_remove(self->innerbrb); + window_remove(self->innerbrr); + window_remove(self->title); + window_remove(self->label); + window_remove(self->max); + window_remove(self->close); + window_remove(self->desk); + window_remove(self->shade); + window_remove(self->icon); + window_remove(self->iconify); + window_remove(self->handle); + window_remove(self->lgrip); + window_remove(self->rgrip); + window_remove(self->topresize); + window_remove(self->tltresize); + window_remove(self->tllresize); + window_remove(self->trtresize); + window_remove(self->trrresize); + window_remove(self->left); + window_remove(self->right); + window_remove(self->titleleft); + window_remove(self->titletop); + window_remove(self->titletopleft); + window_remove(self->titletopright); + window_remove(self->titleright); + window_remove(self->titlebottom); + window_remove(self->handleleft); + window_remove(self->handletop); + window_remove(self->handleright); + window_remove(self->handlebottom); + window_remove(self->lgripleft); + window_remove(self->lgriptop); + window_remove(self->lgripbottom); + window_remove(self->rgripright); + window_remove(self->rgriptop); + window_remove(self->rgripbottom); + + if (self->flash_timer) g_source_remove(self->flash_timer); +} + +/* is there anything present between us and the label? */ +static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) { + for (; *lc != '\0' && lc >= config_title_layout; lc += dir) { + if (*lc == ' ') continue; /* it was invalid */ + if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON) + return TRUE; + if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS) + return TRUE; + if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE) + return TRUE; + if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY) + return TRUE; + if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE) + return TRUE; + if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE) + return TRUE; + if (*lc == 'L') return FALSE; + } + return FALSE; +} + +static void place_button(ObFrame *self, const char *lc, gint bwidth, + gint left, gint i, + gint *x, gint *button_on, gint *button_x) +{ + if (!(*button_on = is_button_present(self, lc, i))) + return; + + self->label_width -= bwidth; + if (i > 0) + *button_x = *x; + *x += i * bwidth; + if (i < 0) { + if (self->label_x <= left || *x > self->label_x) { + *button_x = *x; + } else { + /* the button would have been drawn on top of another button */ + *button_on = FALSE; + self->label_width += bwidth; + } + } +} + +static void layout_title(ObFrame *self) +{ + gchar *lc; + gint i; + + const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1; + /* position of the leftmost button */ + const gint left = ob_rr_theme->paddingx + 1; + /* position of the rightmost button */ + const gint right = self->width; + + /* turn them all off */ + self->icon_on = self->desk_on = self->shade_on = self->iconify_on = + self->max_on = self->close_on = self->label_on = FALSE; + self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2; + self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE; + + /* figure out what's being shown, find each element's position, and the + width of the label + + do the ones before the label, then after the label, + i will be +1 the first time through when working to the left, + and -1 the second time through when working to the right */ + for (i = 1; i >= -1; i-=2) { + gint x; + ObFrameContext *firstcon; + + if (i > 0) { + x = left; + lc = config_title_layout; + firstcon = &self->leftmost; + } else { + x = right; + lc = config_title_layout + strlen(config_title_layout)-1; + firstcon = &self->rightmost; + } + + /* stop at the end of the string (or the label, which calls break) */ + for (; *lc != '\0' && lc >= config_title_layout; lc+=i) { + if (*lc == 'L') { + if (i > 0) { + self->label_on = TRUE; + self->label_x = x; + } + break; /* break the for loop, do other side of label */ + } else if (*lc == 'N') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON; + /* icon is bigger than buttons */ + place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x); + } else if (*lc == 'D') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS; + place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x); + } else if (*lc == 'S') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE; + place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x); + } else if (*lc == 'I') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY; + place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x); + } else if (*lc == 'M') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE; + place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x); + } else if (*lc == 'C') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE; + place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x); + } else + continue; /* don't set firstcon */ + firstcon = NULL; + } + } + + /* position and map the elements */ + if (self->icon_on) { + XMapWindow(obt_display, self->icon); + XMoveWindow(obt_display, self->icon, self->icon_x, + ob_rr_theme->paddingy); + } else + XUnmapWindow(obt_display, self->icon); + + if (self->desk_on) { + XMapWindow(obt_display, self->desk); + XMoveWindow(obt_display, self->desk, self->desk_x, + ob_rr_theme->paddingy + 1); + } else + XUnmapWindow(obt_display, self->desk); + + if (self->shade_on) { + XMapWindow(obt_display, self->shade); + XMoveWindow(obt_display, self->shade, self->shade_x, + ob_rr_theme->paddingy + 1); + } else + XUnmapWindow(obt_display, self->shade); + + if (self->iconify_on) { + XMapWindow(obt_display, self->iconify); + XMoveWindow(obt_display, self->iconify, self->iconify_x, + ob_rr_theme->paddingy + 1); + } else + XUnmapWindow(obt_display, self->iconify); + + if (self->max_on) { + XMapWindow(obt_display, self->max); + XMoveWindow(obt_display, self->max, self->max_x, + ob_rr_theme->paddingy + 1); + } else + XUnmapWindow(obt_display, self->max); + + if (self->close_on) { + XMapWindow(obt_display, self->close); + XMoveWindow(obt_display, self->close, self->close_x, + ob_rr_theme->paddingy + 1); + } else + XUnmapWindow(obt_display, self->close); + + if (self->label_on && self->label_width > 0) { + XMapWindow(obt_display, self->label); + XMoveWindow(obt_display, self->label, self->label_x, + ob_rr_theme->paddingy); + } else + XUnmapWindow(obt_display, self->label); +} + +gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx) +{ + gchar *p, *n; + + if (!*names) /* empty string */ + return FALSE; + + /* find the first space */ + for (p = names; *p; p = g_utf8_next_char(p)) { + const gunichar c = g_utf8_get_char(p); + if (g_unichar_isspace(c)) break; + } + + if (p == names) { + /* leading spaces in the string */ + n = g_utf8_next_char(names); + if (!frame_next_context_from_string(n, cx)) + return FALSE; + } else { + n = p; + if (*p) { + /* delete the space with null zero(s) */ + while (n < g_utf8_next_char(p)) + *(n++) = '\0'; + } + + *cx = frame_context_from_string(names); + + /* find the next non-space */ + for (; *n; n = g_utf8_next_char(n)) { + const gunichar c = g_utf8_get_char(n); + if (!g_unichar_isspace(c)) break; + } + } + + /* delete everything we just read (copy everything at n to the start of + the string */ + for (p = names; *n; ++p, ++n) + *p = *n; + *p = *n; + + return TRUE; +} + +ObFrameContext frame_context_from_string(const gchar *name) +{ + if (!g_ascii_strcasecmp("Desktop", name)) + return OB_FRAME_CONTEXT_DESKTOP; + else if (!g_ascii_strcasecmp("Root", name)) + return OB_FRAME_CONTEXT_ROOT; + else if (!g_ascii_strcasecmp("Client", name)) + return OB_FRAME_CONTEXT_CLIENT; + else if (!g_ascii_strcasecmp("Titlebar", name)) + return OB_FRAME_CONTEXT_TITLEBAR; + else if (!g_ascii_strcasecmp("Frame", name)) + return OB_FRAME_CONTEXT_FRAME; + else if (!g_ascii_strcasecmp("TLCorner", name)) + return OB_FRAME_CONTEXT_TLCORNER; + else if (!g_ascii_strcasecmp("TRCorner", name)) + return OB_FRAME_CONTEXT_TRCORNER; + else if (!g_ascii_strcasecmp("BLCorner", name)) + return OB_FRAME_CONTEXT_BLCORNER; + else if (!g_ascii_strcasecmp("BRCorner", name)) + return OB_FRAME_CONTEXT_BRCORNER; + else if (!g_ascii_strcasecmp("Top", name)) + return OB_FRAME_CONTEXT_TOP; + else if (!g_ascii_strcasecmp("Bottom", name)) + return OB_FRAME_CONTEXT_BOTTOM; + else if (!g_ascii_strcasecmp("Left", name)) + return OB_FRAME_CONTEXT_LEFT; + else if (!g_ascii_strcasecmp("Right", name)) + return OB_FRAME_CONTEXT_RIGHT; + else if (!g_ascii_strcasecmp("Maximize", name)) + return OB_FRAME_CONTEXT_MAXIMIZE; + else if (!g_ascii_strcasecmp("AllDesktops", name)) + return OB_FRAME_CONTEXT_ALLDESKTOPS; + else if (!g_ascii_strcasecmp("Shade", name)) + return OB_FRAME_CONTEXT_SHADE; + else if (!g_ascii_strcasecmp("Iconify", name)) + return OB_FRAME_CONTEXT_ICONIFY; + else if (!g_ascii_strcasecmp("Icon", name)) + return OB_FRAME_CONTEXT_ICON; + else if (!g_ascii_strcasecmp("Close", name)) + return OB_FRAME_CONTEXT_CLOSE; + else if (!g_ascii_strcasecmp("MoveResize", name)) + return OB_FRAME_CONTEXT_MOVE_RESIZE; + else if (!g_ascii_strcasecmp("Dock", name)) + return OB_FRAME_CONTEXT_DOCK; + + return OB_FRAME_CONTEXT_NONE; +} + +ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y) +{ + ObFrame *self; + ObWindow *obwin; + + if (moveresize_in_progress) + return OB_FRAME_CONTEXT_MOVE_RESIZE; + + if (win == obt_root(ob_screen)) + return OB_FRAME_CONTEXT_ROOT; + if ((obwin = window_find(win))) { + if (WINDOW_IS_DOCK(obwin)) { + return OB_FRAME_CONTEXT_DOCK; + } + } + if (client == NULL) return OB_FRAME_CONTEXT_NONE; + if (win == client->window) { + /* conceptually, this is the desktop, as far as users are + concerned */ + if (client->type == OB_CLIENT_TYPE_DESKTOP) + return OB_FRAME_CONTEXT_DESKTOP; + return OB_FRAME_CONTEXT_CLIENT; + } + + self = client->frame; + + /* when the user clicks in the corners of the titlebar and the client + is fully maximized, then treat it like they clicked in the + button that is there */ + if (self->max_horz && self->max_vert && + (win == self->title || win == self->titletop || + win == self->titleleft || win == self->titletopleft || + win == self->titleright || win == self->titletopright)) + { + /* get the mouse coords in reference to the whole frame */ + gint fx = x; + gint fy = y; + + /* these windows are down a border width from the top of the frame */ + if (win == self->title || + win == self->titleleft || win == self->titleright) + fy += self->bwidth; + + /* title is a border width in from the edge */ + if (win == self->title) + fx += self->bwidth; + /* titletop is a bit to the right */ + else if (win == self->titletop) + fx += ob_rr_theme->grip_width + self->bwidth; + /* titletopright is way to the right edge */ + else if (win == self->titletopright) + fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth); + /* titleright is even more way to the right edge */ + else if (win == self->titleright) + fx += self->area.width - self->bwidth; + + /* figure out if we're over the area that should be considered a + button */ + if (fy < self->bwidth + ob_rr_theme->paddingy + 1 + + ob_rr_theme->button_size) + { + if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 + + ob_rr_theme->button_size)) + { + if (self->leftmost != OB_FRAME_CONTEXT_NONE) + return self->leftmost; + } + else if (fx >= (self->area.width - + (self->bwidth + ob_rr_theme->paddingx + 1 + + ob_rr_theme->button_size))) + { + if (self->rightmost != OB_FRAME_CONTEXT_NONE) + return self->rightmost; + } + } + + /* there is no resizing maximized windows so make them the titlebar + context */ + return OB_FRAME_CONTEXT_TITLEBAR; + } + else if (self->max_vert && + (win == self->titletop || win == self->topresize)) + /* can't resize vertically when max vert */ + return OB_FRAME_CONTEXT_TITLEBAR; + else if (self->shaded && + (win == self->titletop || win == self->topresize)) + /* can't resize vertically when shaded */ + return OB_FRAME_CONTEXT_TITLEBAR; + + if (win == self->window) return OB_FRAME_CONTEXT_FRAME; + if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR; + if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR; + if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR; + if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->titletop) return OB_FRAME_CONTEXT_TOP; + if (win == self->topresize) return OB_FRAME_CONTEXT_TOP; + if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER; + if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER; + if (win == self->left) return OB_FRAME_CONTEXT_LEFT; + if (win == self->right) return OB_FRAME_CONTEXT_RIGHT; + if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR; + if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT; + if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM; + if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT; + if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER; + if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER; + if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE; + if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY; + if (win == self->close) return OB_FRAME_CONTEXT_CLOSE; + if (win == self->icon) return OB_FRAME_CONTEXT_ICON; + if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS; + if (win == self->shade) return OB_FRAME_CONTEXT_SHADE; + + return OB_FRAME_CONTEXT_NONE; +} + +void frame_client_gravity(ObFrame *self, gint *x, gint *y) +{ + /* horizontal */ + switch (self->client->gravity) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + /* the middle of the client will be the middle of the frame */ + *x -= (self->size.right - self->size.left) / 2; + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + /* the right side of the client will be the right side of the frame */ + *x -= self->size.right + self->size.left - + self->client->border_width * 2; + break; + + case ForgetGravity: + case StaticGravity: + /* the client's position won't move */ + *x -= self->size.left - self->client->border_width; + break; + } + + /* vertical */ + switch (self->client->gravity) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + /* the middle of the client will be the middle of the frame */ + *y -= (self->size.bottom - self->size.top) / 2; + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + /* the bottom of the client will be the bottom of the frame */ + *y -= self->size.bottom + self->size.top - + self->client->border_width * 2; + break; + + case ForgetGravity: + case StaticGravity: + /* the client's position won't move */ + *y -= self->size.top - self->client->border_width; + break; + } +} + +void frame_frame_gravity(ObFrame *self, gint *x, gint *y) +{ + /* horizontal */ + switch (self->client->gravity) { + default: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + /* the middle of the client will be the middle of the frame */ + *x += (self->size.right - self->size.left) / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + /* the right side of the client will be the right side of the frame */ + *x += self->size.right + self->size.left - + self->client->border_width * 2; + break; + case StaticGravity: + case ForgetGravity: + /* the client's position won't move */ + *x += self->size.left - self->client->border_width; + break; + } + + /* vertical */ + switch (self->client->gravity) { + default: + case NorthWestGravity: + case NorthGravity: + case NorthEastGravity: + break; + case WestGravity: + case CenterGravity: + case EastGravity: + /* the middle of the client will be the middle of the frame */ + *y += (self->size.bottom - self->size.top) / 2; + break; + case SouthWestGravity: + case SouthGravity: + case SouthEastGravity: + /* the bottom of the client will be the bottom of the frame */ + *y += self->size.bottom + self->size.top - + self->client->border_width * 2; + break; + case StaticGravity: + case ForgetGravity: + /* the client's position won't move */ + *y += self->size.top - self->client->border_width; + break; + } +} + +void frame_rect_to_frame(ObFrame *self, Rect *r) +{ + r->width += self->size.left + self->size.right; + r->height += self->size.top + self->size.bottom; + frame_client_gravity(self, &r->x, &r->y); +} + +void frame_rect_to_client(ObFrame *self, Rect *r) +{ + r->width -= self->size.left + self->size.right; + r->height -= self->size.top + self->size.bottom; + frame_frame_gravity(self, &r->x, &r->y); +} + +static void flash_done(gpointer data) +{ + ObFrame *self = data; + + if (self->focused != self->flash_on) + frame_adjust_focus(self, self->focused); +} + +static gboolean flash_timeout(gpointer data) +{ + ObFrame *self = data; + GTimeVal now; + + g_get_current_time(&now); + if (now.tv_sec > self->flash_end.tv_sec || + (now.tv_sec == self->flash_end.tv_sec && + now.tv_usec >= self->flash_end.tv_usec)) + self->flashing = FALSE; + + if (!self->flashing) + return FALSE; /* we are done */ + + self->flash_on = !self->flash_on; + if (!self->focused) { + frame_adjust_focus(self, self->flash_on); + self->focused = FALSE; + } + + return TRUE; /* go again */ +} + +void frame_flash_start(ObFrame *self) +{ + self->flash_on = self->focused; + + if (!self->flashing) + self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT, + 600, flash_timeout, self, + flash_done); + g_get_current_time(&self->flash_end); + g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5); + + self->flashing = TRUE; +} + +void frame_flash_stop(ObFrame *self) +{ + self->flashing = FALSE; +} + +static gulong frame_animate_iconify_time_left(ObFrame *self, + const GTimeVal *now) +{ + glong sec, usec; + sec = self->iconify_animation_end.tv_sec - now->tv_sec; + usec = self->iconify_animation_end.tv_usec - now->tv_usec; + if (usec < 0) { + usec += G_USEC_PER_SEC; + sec--; + } + /* no negative values */ + return MAX(sec * G_USEC_PER_SEC + usec, 0); +} + +static gboolean frame_animate_iconify(gpointer p) +{ + ObFrame *self = p; + gint x, y, w, h; + gint iconx, icony, iconw; + GTimeVal now; + gulong time; + gboolean iconifying; + + if (self->client->icon_geometry.width == 0) { + /* there is no icon geometry set so just go straight down */ + const Rect *a; + + a = screen_physical_area_monitor(screen_find_monitor(&self->area)); + iconx = self->area.x + self->area.width / 2 + 32; + icony = a->y + a->width; + iconw = 64; + } else { + iconx = self->client->icon_geometry.x; + icony = self->client->icon_geometry.y; + iconw = self->client->icon_geometry.width; + } + + iconifying = self->iconify_animation_going > 0; + + /* how far do we have left to go ? */ + g_get_current_time(&now); + time = frame_animate_iconify_time_left(self, &now); + + if ((time > 0 && iconifying) || (time == 0 && !iconifying)) { + /* start where the frame is supposed to be */ + x = self->area.x; + y = self->area.y; + w = self->area.width; + h = self->area.height; + } else { + /* start at the icon */ + x = iconx; + y = icony; + w = iconw; + h = self->size.top; /* just the titlebar */ + } + + if (time > 0) { + glong dx, dy, dw; + glong elapsed; + + dx = self->area.x - iconx; + dy = self->area.y - icony; + dw = self->area.width - self->bwidth * 2 - iconw; + /* if restoring, we move in the opposite direction */ + if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; } + + elapsed = FRAME_ANIMATE_ICONIFY_TIME - time; + x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME; + y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME; + w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME; + h = self->size.top; /* just the titlebar */ + } + + XMoveResizeWindow(obt_display, self->window, x, y, w, h); + XFlush(obt_display); + + if (time == 0) + frame_end_iconify_animation(self); + + return time > 0; /* repeat until we're out of time */ +} + +void frame_end_iconify_animation(ObFrame *self) +{ + /* see if there is an animation going */ + if (self->iconify_animation_going == 0) return; + + if (!self->visible) + XUnmapWindow(obt_display, self->window); + else { + /* Send a ConfigureNotify when the animation is done, this fixes + KDE's pager showing the window in the wrong place. since the + window is mapped at a different location and is then moved, we + need to send the synthetic configurenotify, since apps may have + read the position when the client mapped, apparently. */ + client_reconfigure(self->client, TRUE); + } + + /* we're not animating any more ! */ + self->iconify_animation_going = 0; + + XMoveResizeWindow(obt_display, self->window, + self->area.x, self->area.y, + self->area.width, self->area.height); + /* we delay re-rendering until after we're done animating */ + framerender_frame(self); + XFlush(obt_display); +} + +void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying) +{ + gulong time; + gboolean new_anim = FALSE; + gboolean set_end = TRUE; + GTimeVal now; + + /* if there is no titlebar, just don't animate for now + XXX it would be nice tho.. */ + if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR)) + return; + + /* get the current time */ + g_get_current_time(&now); + + /* get how long until the end */ + time = FRAME_ANIMATE_ICONIFY_TIME; + if (self->iconify_animation_going) { + if (!!iconifying != (self->iconify_animation_going > 0)) { + /* animation was already going on in the opposite direction */ + time = time - frame_animate_iconify_time_left(self, &now); + } else + /* animation was already going in the same direction */ + set_end = FALSE; + } else + new_anim = TRUE; + self->iconify_animation_going = iconifying ? 1 : -1; + + /* set the ending time */ + if (set_end) { + self->iconify_animation_end.tv_sec = now.tv_sec; + self->iconify_animation_end.tv_usec = now.tv_usec; + g_time_val_add(&self->iconify_animation_end, time); + } + + if (new_anim) { + if (self->iconify_animation_timer) + g_source_remove(self->iconify_animation_timer); + self->iconify_animation_timer = + g_timeout_add_full(G_PRIORITY_DEFAULT, + FRAME_ANIMATE_ICONIFY_STEP_TIME, + frame_animate_iconify, self, NULL); + + + /* do the first step */ + frame_animate_iconify(self); + + /* show it during the animation even if it is not "visible" */ + if (!self->visible) + XMapWindow(obt_display, self->window); + } +} diff --git a/openbox/frame.h b/openbox/frame.h new file mode 100644 index 0000000..915c08d --- /dev/null +++ b/openbox/frame.h @@ -0,0 +1,272 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + frame.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __frame_h +#define __frame_h + +#include "geom.h" +#include "obrender/render.h" + +typedef struct _ObFrame ObFrame; + +struct _ObClient; + +typedef void (*ObFrameIconifyAnimateFunc)(gpointer data); + +typedef enum { + OB_FRAME_CONTEXT_NONE, + OB_FRAME_CONTEXT_DESKTOP, + OB_FRAME_CONTEXT_ROOT, + OB_FRAME_CONTEXT_CLIENT, + OB_FRAME_CONTEXT_TITLEBAR, + OB_FRAME_CONTEXT_FRAME, + OB_FRAME_CONTEXT_BLCORNER, + OB_FRAME_CONTEXT_BRCORNER, + OB_FRAME_CONTEXT_TLCORNER, + OB_FRAME_CONTEXT_TRCORNER, + OB_FRAME_CONTEXT_TOP, + OB_FRAME_CONTEXT_BOTTOM, + OB_FRAME_CONTEXT_LEFT, + OB_FRAME_CONTEXT_RIGHT, + OB_FRAME_CONTEXT_MAXIMIZE, + OB_FRAME_CONTEXT_ALLDESKTOPS, + OB_FRAME_CONTEXT_SHADE, + OB_FRAME_CONTEXT_ICONIFY, + OB_FRAME_CONTEXT_ICON, + OB_FRAME_CONTEXT_CLOSE, + /*! This is a special context, which occurs while dragging a window in + a move/resize */ + OB_FRAME_CONTEXT_MOVE_RESIZE, + OB_FRAME_CONTEXT_DOCK, + OB_FRAME_NUM_CONTEXTS +} ObFrameContext; + +#define FRAME_CONTEXT(co, cl) ((cl && cl->type != OB_CLIENT_TYPE_DESKTOP) ? \ + co == OB_FRAME_CONTEXT_FRAME : FALSE) +#define CLIENT_CONTEXT(co, cl) ((cl && cl->type == OB_CLIENT_TYPE_DESKTOP) ? \ + co == OB_FRAME_CONTEXT_DESKTOP : \ + co == OB_FRAME_CONTEXT_CLIENT) + +/*! The decorations the client window wants to be displayed on it */ +typedef enum { + OB_FRAME_DECOR_TITLEBAR = 1 << 0, /*!< Display a titlebar */ + OB_FRAME_DECOR_HANDLE = 1 << 1, /*!< Display a handle (bottom) */ + OB_FRAME_DECOR_GRIPS = 1 << 2, /*!< Display grips in the handle */ + OB_FRAME_DECOR_BORDER = 1 << 3, /*!< Display a border */ + OB_FRAME_DECOR_ICON = 1 << 4, /*!< Display the window's icon */ + OB_FRAME_DECOR_ICONIFY = 1 << 5, /*!< Display an iconify button */ + OB_FRAME_DECOR_MAXIMIZE = 1 << 6, /*!< Display a maximize button */ + /*! Display a button to toggle the window's placement on + all desktops */ + OB_FRAME_DECOR_ALLDESKTOPS = 1 << 7, + OB_FRAME_DECOR_SHADE = 1 << 8, /*!< Display a shade button */ + OB_FRAME_DECOR_CLOSE = 1 << 9 /*!< Display a close button */ +} ObFrameDecorations; + +struct _ObFrame +{ + struct _ObClient *client; + + Window window; + + Strut size; /* the size of the frame */ + Strut oldsize; /* the size of the frame last told to the client */ + Rect area; + gboolean visible; + + guint functions; + guint decorations; + + Window title; + Window label; + Window max; + Window close; + Window desk; + Window shade; + Window icon; + Window iconify; + Window handle; + Window lgrip; + Window rgrip; + + /* These are borders of the frame and its elements */ + Window titleleft; + Window titletop; + Window titletopleft; + Window titletopright; + Window titleright; + Window titlebottom; + Window left; + Window right; + Window handleleft; + Window handletop; + Window handleright; + Window handlebottom; + Window lgriptop; + Window lgripleft; + Window lgripbottom; + Window rgriptop; + Window rgripright; + Window rgripbottom; + Window innerleft; /*!< For drawing the inner client border */ + Window innertop; /*!< For drawing the inner client border */ + Window innerright; /*!< For drawing the inner client border */ + Window innerbottom; /*!< For drawing the inner client border */ + Window innerblb; + Window innerbll; + Window innerbrb; + Window innerbrr; + Window backback; /*!< A colored window shown while resizing */ + Window backfront; /*!< An undrawn-in window, to prevent flashing on + unmap */ + + /* These are resize handles inside the titlebar */ + Window topresize; + Window tltresize; + Window tllresize; + Window trtresize; + Window trrresize; + + Colormap colormap; + + gint icon_on; /* if the window icon button is on */ + gint label_on; /* if the window title is on */ + gint iconify_on; /* if the window iconify button is on */ + gint desk_on; /* if the window all-desktops button is on */ + gint shade_on; /* if the window shade button is on */ + gint max_on; /* if the window maximize button is on */ + gint close_on; /* if the window close button is on */ + + gint width; /* width of the titlebar and handle */ + gint label_width; /* width of the label in the titlebar */ + gint icon_x; /* x-position of the window icon button */ + gint label_x; /* x-position of the window title */ + gint iconify_x; /* x-position of the window iconify button */ + gint desk_x; /* x-position of the window all-desktops button */ + gint shade_x; /* x-position of the window shade button */ + gint max_x; /* x-position of the window maximize button */ + gint close_x; /* x-position of the window close button */ + gint bwidth; /* border width */ + gint cbwidth_l; /* client border width */ + gint cbwidth_t; /* client border width */ + gint cbwidth_r; /* client border width */ + gint cbwidth_b; /* client border width */ + gboolean max_horz; /* when maxed some decorations are hidden */ + gboolean max_vert; /* when maxed some decorations are hidden */ + gboolean shaded; /* decorations adjust when shaded */ + + /* the leftmost and rightmost elements in the titlebar */ + ObFrameContext leftmost; + ObFrameContext rightmost; + + gboolean max_press; + gboolean close_press; + gboolean desk_press; + gboolean shade_press; + gboolean iconify_press; + gboolean max_hover; + gboolean close_hover; + gboolean desk_hover; + gboolean shade_hover; + gboolean iconify_hover; + + gboolean focused; + gboolean need_render; + + gboolean flashing; + gboolean flash_on; + GTimeVal flash_end; + guint flash_timer; + + /*! Is the frame currently in an animation for iconify or restore. + 0 means that it is not animating. > 0 means it is animating an iconify. + < 0 means it is animating a restore. + */ + gint iconify_animation_going; + guint iconify_animation_timer; + GTimeVal iconify_animation_end; +}; + +ObFrame *frame_new(struct _ObClient *c); +void frame_free(ObFrame *self); + +void frame_show(ObFrame *self); +void frame_hide(ObFrame *self); +void frame_adjust_theme(ObFrame *self); +#ifdef SHAPE +void frame_adjust_shape_kind(ObFrame *self, int kind); +#endif +void frame_adjust_shape(ObFrame *self); +void frame_adjust_area(ObFrame *self, gboolean moved, + gboolean resized, gboolean fake); +void frame_adjust_client_area(ObFrame *self); +void frame_adjust_state(ObFrame *self); +void frame_adjust_focus(ObFrame *self, gboolean hilite); +void frame_adjust_title(ObFrame *self); +void frame_adjust_icon(ObFrame *self); +void frame_grab_client(ObFrame *self); +void frame_release_client(ObFrame *self); + +ObFrameContext frame_context_from_string(const gchar *name); + +/*! Parses a ObFrameContext from a string of space-separated context names. + @names The list of context names, the first of which is removed from the + string. + @cx The ObFrameContext is returned here. If an invalid name is found, this + is set to OB_FRAME_CONTEXT_NONE. + @return TRUE if there was something to read in @names, FALSE if it was an + empty input. +*/ +gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx); + +ObFrameContext frame_context(struct _ObClient *self, Window win, + gint x, gint y); + +/*! Applies gravity to the client's position to find where the frame should + be positioned. + @return The proper coordinates for the frame, based on the client. +*/ +void frame_client_gravity(ObFrame *self, gint *x, gint *y); + +/*! Reversly applies gravity to the frame's position to find where the client + should be positioned. + @return The proper coordinates for the client, based on the frame. +*/ +void frame_frame_gravity(ObFrame *self, gint *x, gint *y); + +/*! Convert a rectangle in client coordinates/sizes to what it would be + for the frame, given its current decorations sizes */ +void frame_rect_to_frame(ObFrame *self, Rect *r); + +/*! Convert a rectangle in frame coordinates/sizes to what it would be for the + client, given its current decorations sizes */ +void frame_rect_to_client(ObFrame *self, Rect *r); + +void frame_flash_start(ObFrame *self); +void frame_flash_stop(ObFrame *self); + +/*! Start an animation for iconifying or restoring a frame. The callback + will be called when the animation finishes. But if another animation is + started in the meantime, the callback will never get called. */ +void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying); +void frame_end_iconify_animation(ObFrame *self); + +#define frame_iconify_animating(f) (f->iconify_animation_going != 0) + +#endif diff --git a/openbox/framerender.c b/openbox/framerender.c new file mode 100644 index 0000000..041e6d1 --- /dev/null +++ b/openbox/framerender.c @@ -0,0 +1,412 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + framerender.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "frame.h" +#include "openbox.h" +#include "screen.h" +#include "client.h" +#include "framerender.h" +#include "obrender/theme.h" + +static void framerender_label(ObFrame *self, RrAppearance *a); +static void framerender_icon(ObFrame *self, RrAppearance *a); +static void framerender_max(ObFrame *self, RrAppearance *a); +static void framerender_iconify(ObFrame *self, RrAppearance *a); +static void framerender_desk(ObFrame *self, RrAppearance *a); +static void framerender_shade(ObFrame *self, RrAppearance *a); +static void framerender_close(ObFrame *self, RrAppearance *a); + +void framerender_frame(ObFrame *self) +{ + if (frame_iconify_animating(self)) + return; /* delay redrawing until the animation is done */ + if (!self->need_render) + return; + if (!self->visible) + return; + self->need_render = FALSE; + + { + gulong px; + + px = (self->focused ? + RrColorPixel(ob_rr_theme->cb_focused_color) : + RrColorPixel(ob_rr_theme->cb_unfocused_color)); + + XSetWindowBackground(obt_display, self->backback, px); + XClearWindow(obt_display, self->backback); + XSetWindowBackground(obt_display, self->innerleft, px); + XClearWindow(obt_display, self->innerleft); + XSetWindowBackground(obt_display, self->innertop, px); + XClearWindow(obt_display, self->innertop); + XSetWindowBackground(obt_display, self->innerright, px); + XClearWindow(obt_display, self->innerright); + XSetWindowBackground(obt_display, self->innerbottom, px); + XClearWindow(obt_display, self->innerbottom); + XSetWindowBackground(obt_display, self->innerbll, px); + XClearWindow(obt_display, self->innerbll); + XSetWindowBackground(obt_display, self->innerbrr, px); + XClearWindow(obt_display, self->innerbrr); + XSetWindowBackground(obt_display, self->innerblb, px); + XClearWindow(obt_display, self->innerblb); + XSetWindowBackground(obt_display, self->innerbrb, px); + XClearWindow(obt_display, self->innerbrb); + + px = RrColorPixel(self->focused ? + (self->client->undecorated ? + ob_rr_theme->frame_undecorated_focused_border_color : + ob_rr_theme->frame_focused_border_color) : + (self->client->undecorated ? + ob_rr_theme->frame_undecorated_unfocused_border_color : + ob_rr_theme->frame_unfocused_border_color)); + + XSetWindowBackground(obt_display, self->left, px); + XClearWindow(obt_display, self->left); + XSetWindowBackground(obt_display, self->right, px); + XClearWindow(obt_display, self->right); + + XSetWindowBackground(obt_display, self->titleleft, px); + XClearWindow(obt_display, self->titleleft); + XSetWindowBackground(obt_display, self->titletop, px); + XClearWindow(obt_display, self->titletop); + XSetWindowBackground(obt_display, self->titletopleft, px); + XClearWindow(obt_display, self->titletopleft); + XSetWindowBackground(obt_display, self->titletopright, px); + XClearWindow(obt_display, self->titletopright); + XSetWindowBackground(obt_display, self->titleright, px); + XClearWindow(obt_display, self->titleright); + + XSetWindowBackground(obt_display, self->handleleft, px); + XClearWindow(obt_display, self->handleleft); + XSetWindowBackground(obt_display, self->handletop, px); + XClearWindow(obt_display, self->handletop); + XSetWindowBackground(obt_display, self->handleright, px); + XClearWindow(obt_display, self->handleright); + XSetWindowBackground(obt_display, self->handlebottom, px); + XClearWindow(obt_display, self->handlebottom); + + XSetWindowBackground(obt_display, self->lgripleft, px); + XClearWindow(obt_display, self->lgripleft); + XSetWindowBackground(obt_display, self->lgriptop, px); + XClearWindow(obt_display, self->lgriptop); + XSetWindowBackground(obt_display, self->lgripbottom, px); + XClearWindow(obt_display, self->lgripbottom); + + XSetWindowBackground(obt_display, self->rgripright, px); + XClearWindow(obt_display, self->rgripright); + XSetWindowBackground(obt_display, self->rgriptop, px); + XClearWindow(obt_display, self->rgriptop); + XSetWindowBackground(obt_display, self->rgripbottom, px); + XClearWindow(obt_display, self->rgripbottom); + + /* don't use the separator color for shaded windows */ + if (!self->client->shaded) + px = (self->focused ? + RrColorPixel(ob_rr_theme->title_separator_focused_color) : + RrColorPixel(ob_rr_theme->title_separator_unfocused_color)); + + XSetWindowBackground(obt_display, self->titlebottom, px); + XClearWindow(obt_display, self->titlebottom); + } + + if (self->decorations & OB_FRAME_DECOR_TITLEBAR) { + RrAppearance *t, *l, *m, *n, *i, *d, *s, *c, *clear; + if (self->focused) { + t = ob_rr_theme->a_focused_title; + l = ob_rr_theme->a_focused_label; + m = (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE) ? + ob_rr_theme->btn_max->a_disabled_focused : + (self->client->max_vert || self->client->max_horz ? + (self->max_press ? + ob_rr_theme->btn_max->a_toggled_focused_pressed : + (self->max_hover ? + ob_rr_theme->btn_max->a_toggled_hover_focused : + ob_rr_theme->btn_max->a_toggled_focused_unpressed)) : + (self->max_press ? + ob_rr_theme->btn_max->a_focused_pressed : + (self->max_hover ? + ob_rr_theme->btn_max->a_hover_focused : + ob_rr_theme->btn_max->a_focused_unpressed)))); + n = ob_rr_theme->a_icon; + i = (!(self->decorations & OB_FRAME_DECOR_ICONIFY) ? + ob_rr_theme->btn_iconify->a_disabled_focused : + (self->iconify_press ? + ob_rr_theme->btn_iconify->a_focused_pressed : + (self->iconify_hover ? + ob_rr_theme->btn_iconify->a_hover_focused : + ob_rr_theme->btn_iconify->a_focused_unpressed))); + d = (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS) ? + ob_rr_theme->btn_desk->a_disabled_focused : + (self->client->desktop == DESKTOP_ALL ? + (self->desk_press ? + ob_rr_theme->btn_desk->a_toggled_focused_pressed : + (self->desk_hover ? + ob_rr_theme->btn_desk->a_toggled_hover_focused : + ob_rr_theme->btn_desk->a_toggled_focused_unpressed)) : + (self->desk_press ? + ob_rr_theme->btn_desk->a_focused_pressed : + (self->desk_hover ? + ob_rr_theme->btn_desk->a_hover_focused : + ob_rr_theme->btn_desk->a_focused_unpressed)))); + s = (!(self->decorations & OB_FRAME_DECOR_SHADE) ? + ob_rr_theme->btn_shade->a_disabled_focused : + (self->client->shaded ? + (self->shade_press ? + ob_rr_theme->btn_shade->a_toggled_focused_pressed : + (self->shade_hover ? + ob_rr_theme->btn_shade->a_toggled_hover_focused : + ob_rr_theme->btn_shade->a_toggled_focused_unpressed)) : + (self->shade_press ? + ob_rr_theme->btn_shade->a_focused_pressed : + (self->shade_hover ? + ob_rr_theme->btn_shade->a_hover_focused : + ob_rr_theme->btn_shade->a_focused_unpressed)))); + c = (!(self->decorations & OB_FRAME_DECOR_CLOSE) ? + ob_rr_theme->btn_close->a_disabled_focused : + (self->close_press ? + ob_rr_theme->btn_close->a_focused_pressed : + (self->close_hover ? + ob_rr_theme->btn_close->a_hover_focused : + ob_rr_theme->btn_close->a_focused_unpressed))); + } else { + t = ob_rr_theme->a_unfocused_title; + l = ob_rr_theme->a_unfocused_label; + m = (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE) ? + ob_rr_theme->btn_max->a_disabled_unfocused : + (self->client->max_vert || self->client->max_horz ? + (self->max_press ? + ob_rr_theme->btn_max->a_toggled_unfocused_pressed : + (self->max_hover ? + ob_rr_theme->btn_max->a_toggled_hover_unfocused : + ob_rr_theme->btn_max->a_toggled_unfocused_unpressed)) : + (self->max_press ? + ob_rr_theme->btn_max->a_unfocused_pressed : + (self->max_hover ? + ob_rr_theme->btn_max->a_hover_unfocused : + ob_rr_theme->btn_max->a_unfocused_unpressed)))); + n = ob_rr_theme->a_icon; + i = (!(self->decorations & OB_FRAME_DECOR_ICONIFY) ? + ob_rr_theme->btn_iconify->a_disabled_unfocused : + (self->iconify_press ? + ob_rr_theme->btn_iconify->a_unfocused_pressed : + (self->iconify_hover ? + ob_rr_theme->btn_iconify->a_hover_unfocused : + ob_rr_theme->btn_iconify->a_unfocused_unpressed))); + d = (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS) ? + ob_rr_theme->btn_desk->a_disabled_unfocused : + (self->client->desktop == DESKTOP_ALL ? + (self->desk_press ? + ob_rr_theme->btn_desk->a_toggled_unfocused_pressed : + (self->desk_hover ? + ob_rr_theme->btn_desk->a_toggled_hover_unfocused : + ob_rr_theme->btn_desk->a_toggled_unfocused_unpressed)) : + (self->desk_press ? + ob_rr_theme->btn_desk->a_unfocused_pressed : + (self->desk_hover ? + ob_rr_theme->btn_desk->a_hover_unfocused : + ob_rr_theme->btn_desk->a_unfocused_unpressed)))); + s = (!(self->decorations & OB_FRAME_DECOR_SHADE) ? + ob_rr_theme->btn_shade->a_disabled_unfocused : + (self->client->shaded ? + (self->shade_press ? + ob_rr_theme->btn_shade->a_toggled_unfocused_pressed : + (self->shade_hover ? + ob_rr_theme->btn_shade->a_toggled_hover_unfocused : + ob_rr_theme->btn_shade->a_toggled_unfocused_unpressed)) : + (self->shade_press ? + ob_rr_theme->btn_shade->a_unfocused_pressed : + (self->shade_hover ? + ob_rr_theme->btn_shade->a_hover_unfocused : + ob_rr_theme->btn_shade->a_unfocused_unpressed)))); + c = (!(self->decorations & OB_FRAME_DECOR_CLOSE) ? + ob_rr_theme->btn_close->a_disabled_unfocused : + (self->close_press ? + ob_rr_theme->btn_close->a_unfocused_pressed : + (self->close_hover ? + ob_rr_theme->btn_close->a_hover_unfocused : + ob_rr_theme->btn_close->a_unfocused_unpressed))); + } + clear = ob_rr_theme->a_clear; + + RrPaint(t, self->title, self->width, ob_rr_theme->title_height); + + clear->surface.parent = t; + clear->surface.parenty = 0; + + clear->surface.parentx = ob_rr_theme->grip_width; + + RrPaint(clear, self->topresize, + self->width - ob_rr_theme->grip_width * 2, + ob_rr_theme->paddingy + 1); + + clear->surface.parentx = 0; + + if (ob_rr_theme->grip_width > 0) + RrPaint(clear, self->tltresize, + ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + if (ob_rr_theme->title_height > 0) + RrPaint(clear, self->tllresize, + ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); + + clear->surface.parentx = self->width - ob_rr_theme->grip_width; + + if (ob_rr_theme->grip_width > 0) + RrPaint(clear, self->trtresize, + ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1); + + clear->surface.parentx = self->width - (ob_rr_theme->paddingx + 1); + + if (ob_rr_theme->title_height > 0) + RrPaint(clear, self->trrresize, + ob_rr_theme->paddingx + 1, ob_rr_theme->title_height); + + /* set parents for any parent relative guys */ + l->surface.parent = t; + l->surface.parentx = self->label_x; + l->surface.parenty = ob_rr_theme->paddingy; + + m->surface.parent = t; + m->surface.parentx = self->max_x; + m->surface.parenty = ob_rr_theme->paddingy + 1; + + n->surface.parent = t; + n->surface.parentx = self->icon_x; + n->surface.parenty = ob_rr_theme->paddingy; + + i->surface.parent = t; + i->surface.parentx = self->iconify_x; + i->surface.parenty = ob_rr_theme->paddingy + 1; + + d->surface.parent = t; + d->surface.parentx = self->desk_x; + d->surface.parenty = ob_rr_theme->paddingy + 1; + + s->surface.parent = t; + s->surface.parentx = self->shade_x; + s->surface.parenty = ob_rr_theme->paddingy + 1; + + c->surface.parent = t; + c->surface.parentx = self->close_x; + c->surface.parenty = ob_rr_theme->paddingy + 1; + + framerender_label(self, l); + framerender_max(self, m); + framerender_icon(self, n); + framerender_iconify(self, i); + framerender_desk(self, d); + framerender_shade(self, s); + framerender_close(self, c); + } + + if (self->decorations & OB_FRAME_DECOR_HANDLE && + ob_rr_theme->handle_height > 0) + { + RrAppearance *h, *g; + + h = (self->focused ? + ob_rr_theme->a_focused_handle : ob_rr_theme->a_unfocused_handle); + + RrPaint(h, self->handle, self->width, ob_rr_theme->handle_height); + + if (self->decorations & OB_FRAME_DECOR_GRIPS) { + g = (self->focused ? + ob_rr_theme->a_focused_grip : ob_rr_theme->a_unfocused_grip); + + if (g->surface.grad == RR_SURFACE_PARENTREL) + g->surface.parent = h; + + g->surface.parentx = 0; + g->surface.parenty = 0; + + RrPaint(g, self->lgrip, + ob_rr_theme->grip_width, ob_rr_theme->handle_height); + + g->surface.parentx = self->width - ob_rr_theme->grip_width; + g->surface.parenty = 0; + + RrPaint(g, self->rgrip, + ob_rr_theme->grip_width, ob_rr_theme->handle_height); + } + } + + XFlush(obt_display); +} + +static void framerender_label(ObFrame *self, RrAppearance *a) +{ + if (!self->label_on) return; + /* set the texture's text! */ + a->texture[0].data.text.string = self->client->title; + RrPaint(a, self->label, self->label_width, ob_rr_theme->label_height); +} + +static void framerender_icon(ObFrame *self, RrAppearance *a) +{ + RrImage *icon; + + if (!self->icon_on) return; + + icon = client_icon(self->client); + + if (icon) { + RrAppearanceClearTextures(a); + a->texture[0].type = RR_TEXTURE_IMAGE; + a->texture[0].data.image.alpha = 0xff; + a->texture[0].data.image.image = icon; + } else { + RrAppearanceClearTextures(a); + a->texture[0].type = RR_TEXTURE_NONE; + } + + RrPaint(a, self->icon, + ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2); +} + +static void framerender_max(ObFrame *self, RrAppearance *a) +{ + if (!self->max_on) return; + RrPaint(a, self->max, ob_rr_theme->button_size, ob_rr_theme->button_size); +} + +static void framerender_iconify(ObFrame *self, RrAppearance *a) +{ + if (!self->iconify_on) return; + RrPaint(a, self->iconify, + ob_rr_theme->button_size, ob_rr_theme->button_size); +} + +static void framerender_desk(ObFrame *self, RrAppearance *a) +{ + if (!self->desk_on) return; + RrPaint(a, self->desk, ob_rr_theme->button_size, ob_rr_theme->button_size); +} + +static void framerender_shade(ObFrame *self, RrAppearance *a) +{ + if (!self->shade_on) return; + RrPaint(a, self->shade, + ob_rr_theme->button_size, ob_rr_theme->button_size); +} + +static void framerender_close(ObFrame *self, RrAppearance *a) +{ + if (!self->close_on) return; + RrPaint(a, self->close, + ob_rr_theme->button_size, ob_rr_theme->button_size); +} diff --git a/openbox/framerender.h b/openbox/framerender.h new file mode 100644 index 0000000..162fa57 --- /dev/null +++ b/openbox/framerender.h @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + framerender.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __framerender_h +#define __framerender_h + +struct _ObFrame; + +void framerender_frame(struct _ObFrame *self); + +#endif diff --git a/openbox/geom.h b/openbox/geom.h new file mode 100644 index 0000000..003b008 --- /dev/null +++ b/openbox/geom.h @@ -0,0 +1,166 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + geom.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __geom_h +#define __geom_h + +#include <glib.h> + +typedef struct _GravityCoord { + gint pos; + gint denom; + gboolean center; + gboolean opposite; +} GravityCoord; + +typedef struct _GravityPoint { + GravityCoord x; + GravityCoord y; +} GravityPoint; + +#define GRAVITY_COORD_SET(c, p, cen, opp) \ + (c).pos = (p), (c).center = (cen), (c).opposite = (opp) + + +typedef struct _Point { + int x; + int y; +} Point; + +#define POINT_SET(pt, nx, ny) (pt).x = (nx), (pt).y = (ny) +#define POINT_EQUAL(p1, p2) ((p1).x == (p2).x && (p1).y == (p2).y) + +typedef struct _Size { + int width; + int height; +} Size; + +#define SIZE_SET(sz, w, h) (sz).width = (w), (sz).height = (h) + +typedef struct _Rect { + int x; + int y; + int width; + int height; +} Rect; + +#define RECT_LEFT(r) ((r).x) +#define RECT_TOP(r) ((r).y) +#define RECT_RIGHT(r) ((r).x + (r).width - 1) +#define RECT_BOTTOM(r) ((r).y + (r).height - 1) + +#define RECT_SET_POINT(r, nx, ny) \ + (r).x = (nx), (r).y = (ny) +#define RECT_SET_SIZE(r, w, h) \ + (r).width = (w), (r).height = (h) +#define RECT_SET(r, nx, ny, w, h) \ + (r).x = (nx), (r).y = (ny), (r).width = (w), (r).height = (h) + +#define RECT_EQUAL(r1, r2) ((r1).x == (r2).x && (r1).y == (r2).y && \ + (r1).width == (r2).width && \ + (r1).height == (r2).height) +#define RECT_EQUAL_DIMS(r, x, y, w, h) \ + ((r).x == (x) && (r).y == (y) && (r).width == (w) && (r).height == (h)) + +#define RECT_TO_DIMS(r, x, y, w, h) \ + (x) = (r).x, (y) = (r).y, (w) = (r).width, (h) = (r).height + +#define RECT_CONTAINS(r, px, py) \ + ((px) >= (r).x && (px) < (r).x + (r).width && \ + (py) >= (r).y && (py) < (r).y + (r).height) +#define RECT_CONTAINS_RECT(r, o) \ + ((o).x >= (r).x && (o).x + (o).width <= (r).x + (r).width && \ + (o).y >= (r).y && (o).y + (o).height <= (r).y + (r).height) + +/* Returns true if Rect r and o intersect */ +#define RECT_INTERSECTS_RECT(r, o) \ + ((o).x < (r).x + (r).width && (o).x + (o).width > (r).x && \ + (o).y < (r).y + (r).height && (o).y + (o).height > (r).y) + +/* Sets Rect r to be the intersection of Rect a and b. */ +#define RECT_SET_INTERSECTION(r, a, b) \ + ((r).x = MAX((a).x, (b).x), \ + (r).y = MAX((a).y, (b).y), \ + (r).width = MIN((a).x + (a).width - 1, \ + (b).x + (b).width - 1) - (r).x + 1, \ + (r).height = MIN((a).y + (a).height - 1, \ + (b).y + (b).height - 1) - (r).y + 1) + +typedef struct _Strut { + int left; + int top; + int right; + int bottom; +} Strut; + +typedef struct _StrutPartial { + int left; + int top; + int right; + int bottom; + + int left_start, left_end; + int top_start, top_end; + int right_start, right_end; + int bottom_start, bottom_end; +} StrutPartial; + +#define STRUT_SET(s, l, t, r, b) \ + (s).left = (l), (s).top = (t), (s).right = (r), (s).bottom = (b) + +#define STRUT_PARTIAL_SET(s, l, t, r, b, ls, le, ts, te, rs, re, bs, be) \ + (s).left = (l), (s).top = (t), (s).right = (r), (s).bottom = (b), \ + (s).left_start = (ls), (s).left_end = (le), \ + (s).top_start = (ts), (s).top_end = (te), \ + (s).right_start = (rs), (s).right_end = (re), \ + (s).bottom_start = (bs), (s).bottom_end = (be) + +#define STRUT_ADD(s1, s2) \ + (s1).left = MAX((s1).left, (s2).left), \ + (s1).right = MAX((s1).right, (s2).right), \ + (s1).top = MAX((s1).top, (s2).top), \ + (s1).bottom = MAX((s1).bottom, (s2).bottom) + +#define STRUT_EXISTS(s1) \ + ((s1).left || (s1).top || (s1).right || (s1).bottom) + +#define STRUT_EQUAL(s1, s2) \ + ((s1).left == (s2).left && \ + (s1).top == (s2).top && \ + (s1).right == (s2).right && \ + (s1).bottom == (s2).bottom) + +#define PARTIAL_STRUT_EQUAL(s1, s2) \ + ((s1).left == (s2).left && \ + (s1).top == (s2).top && \ + (s1).right == (s2).right && \ + (s1).bottom == (s2).bottom && \ + (s1).left_start == (s2).left_start && \ + (s1).left_end == (s2).left_end && \ + (s1).top_start == (s2).top_start && \ + (s1).top_end == (s2).top_end && \ + (s1).right_start == (s2).right_start && \ + (s1).right_end == (s2).right_end && \ + (s1).bottom_start == (s2).bottom_start && \ + (s1).bottom_end == (s2).bottom_end) + +#define RANGES_INTERSECT(r1x, r1w, r2x, r2w) \ + (r1w && r2w && r1x < r2x + r2w && r1x + r1w > r2x) + +#endif diff --git a/openbox/grab.c b/openbox/grab.c new file mode 100644 index 0000000..4518c43 --- /dev/null +++ b/openbox/grab.c @@ -0,0 +1,244 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grab.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "grab.h" +#include "openbox.h" +#include "event.h" +#include "screen.h" +#include "debug.h" +#include "obt/display.h" +#include "obt/keyboard.h" + +#include <glib.h> +#include <X11/Xlib.h> + +#define GRAB_PTR_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask) +#define GRAB_KEY_MASK (KeyPressMask | KeyReleaseMask) + +#define MASK_LIST_SIZE 8 + +/*! A list of all possible combinations of keyboard lock masks */ +static guint mask_list[MASK_LIST_SIZE]; +static guint kgrabs = 0; +static guint pgrabs = 0; +/*! The time at which the last grab was made */ +static Time grab_time = CurrentTime; +static gint passive_count = 0; +static ObtIC *ic = NULL; + +static Time ungrab_time(void) +{ + Time t = event_time(); + if (grab_time == CurrentTime || + !(t == CurrentTime || event_time_after(t, grab_time))) + /* When the time moves backward on the server, then we can't use + the grab time because that will be in the future. So instead we + have to use CurrentTime. + + "XUngrabPointer does not release the pointer if the specified time + is earlier than the last-pointer-grab time or is later than the + current X server time." + */ + t = CurrentTime; /*grab_time;*/ + return t; +} + +static Window grab_window(void) +{ + return screen_support_win; +} + +gboolean grab_on_keyboard(void) +{ + return kgrabs > 0; +} + +gboolean grab_on_pointer(void) +{ + return pgrabs > 0; +} + +ObtIC *grab_input_context(void) +{ + return ic; +} + +gboolean grab_keyboard_full(gboolean grab) +{ + gboolean ret = FALSE; + + if (grab) { + if (kgrabs++ == 0) { + ret = XGrabKeyboard(obt_display, grab_window(), + False, GrabModeAsync, GrabModeAsync, + event_time()) == Success; + if (!ret) + --kgrabs; + else { + passive_count = 0; + grab_time = event_time(); + } + } else + ret = TRUE; + } else if (kgrabs > 0) { + if (--kgrabs == 0) { + XUngrabKeyboard(obt_display, ungrab_time()); + } + ret = TRUE; + } + + return ret; +} + +gboolean grab_pointer_full(gboolean grab, gboolean owner_events, + gboolean confine, ObCursor cur) +{ + gboolean ret = FALSE; + + if (grab) { + if (pgrabs++ == 0) { + ret = XGrabPointer(obt_display, grab_window(), owner_events, + GRAB_PTR_MASK, + GrabModeAsync, GrabModeAsync, + (confine ? obt_root(ob_screen) : None), + ob_cursor(cur), event_time()) == Success; + if (!ret) + --pgrabs; + else + grab_time = event_time(); + } else + ret = TRUE; + } else if (pgrabs > 0) { + if (--pgrabs == 0) { + XUngrabPointer(obt_display, ungrab_time()); + } + ret = TRUE; + } + return ret; +} + +gint grab_server(gboolean grab) +{ + static guint sgrabs = 0; + if (grab) { + if (sgrabs++ == 0) { + XGrabServer(obt_display); + XSync(obt_display, FALSE); + } + } else if (sgrabs > 0) { + if (--sgrabs == 0) { + XUngrabServer(obt_display); + XFlush(obt_display); + } + } + return sgrabs; +} + +void grab_startup(gboolean reconfig) +{ + guint i = 0; + guint num, caps, scroll; + + num = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_NUMLOCK); + caps = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CAPSLOCK); + scroll = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SCROLLLOCK); + + mask_list[i++] = 0; + mask_list[i++] = num; + mask_list[i++] = caps; + mask_list[i++] = scroll; + mask_list[i++] = num | caps; + mask_list[i++] = num | scroll; + mask_list[i++] = caps | scroll; + mask_list[i++] = num | caps | scroll; + g_assert(i == MASK_LIST_SIZE); + + ic = obt_keyboard_context_new(obt_root(ob_screen), grab_window()); +} + +void grab_shutdown(gboolean reconfig) +{ + obt_keyboard_context_unref(ic); + ic = NULL; + + if (reconfig) return; + + while (ungrab_keyboard()); + while (ungrab_pointer()); + while (grab_server(FALSE)); +} + +void grab_button_full(guint button, guint state, Window win, guint mask, + gint pointer_mode, ObCursor cur) +{ + guint i; + + /* can get BadAccess from these */ + obt_display_ignore_errors(TRUE); + for (i = 0; i < MASK_LIST_SIZE; ++i) + XGrabButton(obt_display, button, state | mask_list[i], win, False, + mask, pointer_mode, GrabModeAsync, None, ob_cursor(cur)); + obt_display_ignore_errors(FALSE); + if (obt_display_error_occured) + ob_debug("Failed to grab button %d modifiers %d", button, state); +} + +void ungrab_button(guint button, guint state, Window win) +{ + guint i; + + for (i = 0; i < MASK_LIST_SIZE; ++i) + XUngrabButton(obt_display, button, state | mask_list[i], win); +} + +void grab_key(guint keycode, guint state, Window win, gint keyboard_mode) +{ + guint i; + + /* can get BadAccess' from these */ + obt_display_ignore_errors(TRUE); + for (i = 0; i < MASK_LIST_SIZE; ++i) + XGrabKey(obt_display, keycode, state | mask_list[i], win, FALSE, + GrabModeAsync, keyboard_mode); + obt_display_ignore_errors(FALSE); + if (obt_display_error_occured) + ob_debug("Failed to grab keycode %d modifiers %d", keycode, state); +} + +void ungrab_all_keys(Window win) +{ + XUngrabKey(obt_display, AnyKey, AnyModifier, win); +} + +void grab_key_passive_count(int change) +{ + if (grab_on_keyboard()) return; + passive_count += change; + if (passive_count < 0) passive_count = 0; +} + +void ungrab_passive_key(void) +{ + /*ob_debug("ungrabbing %d passive grabs\n", passive_count);*/ + if (passive_count) { + /* kill our passive grab */ + XUngrabKeyboard(obt_display, event_time()); + passive_count = 0; + } +} diff --git a/openbox/grab.h b/openbox/grab.h new file mode 100644 index 0000000..c4fe17c --- /dev/null +++ b/openbox/grab.h @@ -0,0 +1,58 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grab.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __grab_h +#define __grab_h + +#include "misc.h" +#include "obt/keyboard.h" + +#include <glib.h> +#include <X11/Xlib.h> + +void grab_startup(gboolean reconfig); +void grab_shutdown(gboolean reconfig); + +ObtIC *grab_input_context(void); + +gboolean grab_keyboard_full(gboolean grab); +/*! @param confine If true the pointer is confined to the screen */ +gboolean grab_pointer_full(gboolean grab, gboolean owner_events, + gboolean confine, ObCursor cur); +gint grab_server(gboolean grab); + +#define grab_keyboard() grab_keyboard_full(TRUE) +#define ungrab_keyboard() grab_keyboard_full(FALSE) +#define grab_pointer(o,c,u) grab_pointer_full(TRUE, (o), (c), (u)) +#define ungrab_pointer() grab_pointer_full(FALSE, FALSE, FALSE, OB_CURSOR_NONE) + +gboolean grab_on_keyboard(void); +gboolean grab_on_pointer(void); + +void grab_button_full(guint button, guint state, Window win, guint mask, + gint pointer_mode, ObCursor cursor); +void ungrab_button(guint button, guint state, Window win); + +void grab_key(guint keycode, guint state, Window win, gint keyboard_mode); + +void ungrab_all_keys(Window win); + +void grab_key_passive_count(int change); +void ungrab_passive_key(void); + +#endif diff --git a/openbox/group.c b/openbox/group.c new file mode 100644 index 0000000..0712bf4 --- /dev/null +++ b/openbox/group.c @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + group.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "group.h" +#include "client.h" + +static GHashTable *group_map; + +static guint window_hash(Window *w) { return *w; } +static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; } + +void group_startup(gboolean reconfig) +{ + if (reconfig) return; + + group_map = g_hash_table_new((GHashFunc)window_hash, + (GEqualFunc)window_comp); +} + +void group_shutdown(gboolean reconfig) +{ + if (reconfig) return; + + g_hash_table_destroy(group_map); +} + +ObGroup *group_add(Window leader, ObClient *client) +{ + ObGroup *self; + + self = g_hash_table_lookup(group_map, &leader); + if (self == NULL) { + self = g_slice_new(ObGroup); + self->leader = leader; + self->members = NULL; + g_hash_table_insert(group_map, &self->leader, self); + } + + self->members = g_slist_append(self->members, client); + + return self; +} + +void group_remove(ObGroup *self, ObClient *client) +{ + self->members = g_slist_remove(self->members, client); + if (self->members == NULL) { + g_hash_table_remove(group_map, &self->leader); + g_slice_free(ObGroup, self); + } +} diff --git a/openbox/group.h b/openbox/group.h new file mode 100644 index 0000000..86b36b4 --- /dev/null +++ b/openbox/group.h @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + group.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __group_h +#define __group_h + +#include <X11/Xlib.h> +#include <glib.h> + +typedef struct _ObGroup ObGroup; + +struct _ObClient; + +struct _ObGroup +{ + Window leader; + + /* list of clients */ + GSList *members; +}; + +void group_startup(gboolean reconfig); +void group_shutdown(gboolean reconfig); + +ObGroup *group_add(Window leader, struct _ObClient *client); + +void group_remove(ObGroup *self, struct _ObClient *client); + +#endif diff --git a/openbox/keyboard.c b/openbox/keyboard.c new file mode 100644 index 0000000..8f4424e --- /dev/null +++ b/openbox/keyboard.c @@ -0,0 +1,340 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + keyboard.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "focus.h" +#include "screen.h" +#include "frame.h" +#include "openbox.h" +#include "event.h" +#include "grab.h" +#include "client.h" +#include "actions.h" +#include "menuframe.h" +#include "config.h" +#include "keytree.h" +#include "keyboard.h" +#include "translate.h" +#include "moveresize.h" +#include "popup.h" +#include "gettext.h" +#include "obt/keyboard.h" + +#include <glib.h> + +KeyBindingTree *keyboard_firstnode = NULL; +static ObPopup *popup = NULL; +static KeyBindingTree *curpos; +static guint chain_timer = 0; + +static void grab_keys(gboolean grab) +{ + KeyBindingTree *p; + + ungrab_all_keys(obt_root(ob_screen)); + + if (grab) { + p = curpos ? curpos->first_child : keyboard_firstnode; + while (p) { + if (p->key) + grab_key(p->key, p->state, obt_root(ob_screen), + GrabModeAsync); + p = p->next_sibling; + } + if (curpos) + grab_key(config_keyboard_reset_keycode, + config_keyboard_reset_state, + obt_root(ob_screen), GrabModeAsync); + } +} + +static gboolean chain_timeout(gpointer data) +{ + keyboard_reset_chains(0); + return FALSE; /* don't repeat */ +} + +static void chain_done(gpointer data) +{ + chain_timer = 0; +} + +static void set_curpos(KeyBindingTree *newpos) +{ + if (curpos == newpos) return; + + grab_keys(FALSE); + curpos = newpos; + grab_keys(TRUE); + + if (curpos != NULL) { + gchar *text = NULL; + GList *it; + const Rect *a; + + for (it = curpos->keylist; it; it = g_list_next(it)) { + gchar *oldtext = text; + if (text == NULL) + text = g_strdup(it->data); + else + text = g_strconcat(text, " - ", it->data, NULL); + g_free(oldtext); + } + + a = screen_physical_area_primary(FALSE); + popup_position(popup, NorthWestGravity, a->x + 10, a->y + 10); + /* 1 second delay for the popup to show */ + popup_delay_show(popup, 1000, text); + g_free(text); + } else { + popup_hide(popup); + } +} + +void keyboard_reset_chains(gint break_chroots) +{ + KeyBindingTree *p; + + for (p = curpos; p; p = p->parent) { + if (p->chroot) { + if (break_chroots == 0) break; /* stop here */ + if (break_chroots > 0) + --break_chroots; + } + } + set_curpos(p); +} + +void keyboard_unbind_all(void) +{ + tree_destroy(keyboard_firstnode); + keyboard_firstnode = NULL; +} + +void keyboard_chroot(GList *keylist) +{ + /* try do it in the existing tree. if we can't that means it is an empty + chroot binding. so add it to the tree then. */ + if (!tree_chroot(keyboard_firstnode, keylist)) { + KeyBindingTree *tree; + if (!(tree = tree_build(keylist))) + return; + tree_chroot(tree, keylist); + tree_assimilate(tree); + } +} + +gboolean keyboard_bind(GList *keylist, ObActionsAct *action) +{ + KeyBindingTree *tree, *t; + gboolean conflict; + + g_assert(keylist != NULL); + g_assert(action != NULL); + + if (!(tree = tree_build(keylist))) + return FALSE; + + if ((t = tree_find(tree, &conflict)) != NULL) { + /* already bound to something, use the existing tree */ + tree_destroy(tree); + tree = NULL; + } else + t = tree; + + if (conflict) { + g_message(_("Conflict with key binding in config file")); + tree_destroy(tree); + return FALSE; + } + + /* find the bottom node */ + for (; t->first_child; t = t->first_child); + + /* set the action */ + t->actions = g_slist_append(t->actions, action); + /* assimilate this built tree into the main tree. assimilation + destroys/uses the tree */ + if (tree) tree_assimilate(tree); + + return TRUE; +} + +#if 0 +gboolean keyboard_process_interactive_grab(const XEvent *e, ObClient **client) +{ + gboolean handled = FALSE; + gboolean done = FALSE; + gboolean cancel = FALSE; + guint mods; + + mods = obt_keyboard_only_modmasks(ev->xkey.state); + + if (istate.active) { + if ((e->type == KeyRelease && !(istate.state & mods))) { + done = TRUE; + handled = TRUE; + } else if (e->type == KeyPress) { + /*if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) + done = TRUE; + else */if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) { + cancel = done = TRUE; + handled = TRUE; + } + } else if (e->type == ButtonPress) { + cancel = TRUE; + done = TRUE; + handled = FALSE; + } + + if (done) + keyboard_interactive_end(e->xkey.state, cancel, e->xkey.time,TRUE); + + if (handled) + *client = istate.client; + } + + return handled; +} +#endif + +gboolean keyboard_event(ObClient *client, const XEvent *e) +{ + KeyBindingTree *p; + gboolean used; + guint mods; + + if (e->type == KeyRelease) { + grab_key_passive_count(-1); + return FALSE; + } + + g_assert(e->type == KeyPress); + grab_key_passive_count(1); + + mods = obt_keyboard_only_modmasks(e->xkey.state); + + if (e->xkey.keycode == config_keyboard_reset_keycode && + mods == config_keyboard_reset_state) + { + if (chain_timer) g_source_remove(chain_timer); + keyboard_reset_chains(-1); + return TRUE; + } + + used = FALSE; + if (curpos == NULL) + p = keyboard_firstnode; + else + p = curpos->first_child; + while (p) { + if (p->key == e->xkey.keycode && p->state == mods) { + /* if we hit a key binding, then close any open menus and run it */ + if (menu_frame_visible) + menu_frame_hide_all(); + + if (p->first_child != NULL) { /* part of a chain */ + if (chain_timer) g_source_remove(chain_timer); + /* 3 second timeout for chains */ + chain_timer = + g_timeout_add_full(G_PRIORITY_DEFAULT, + 3000, chain_timeout, NULL, + chain_done); + set_curpos(p); + } else if (p->chroot) /* an empty chroot */ + set_curpos(p); + else { + GSList *it; + + for (it = p->actions; it; it = g_slist_next(it)) + if (actions_act_is_interactive(it->data)) break; + if (it == NULL) /* reset if the actions are not interactive */ + keyboard_reset_chains(0); + + actions_run_acts(p->actions, OB_USER_ACTION_KEYBOARD_KEY, + e->xkey.state, e->xkey.x_root, e->xkey.y_root, + 0, OB_FRAME_CONTEXT_NONE, client); + } + break; + used = TRUE; + } + p = p->next_sibling; + } + return used; +} + +static void node_rebind(KeyBindingTree *node) +{ + if (node->first_child) { + /* find leaf nodes */ + node_rebind(node->first_child); + + /* for internal nodes, add them to the tree if they + are a chroot, but do this after adding their + children */ + if (node->chroot) + keyboard_chroot(node->keylist); + } + else { + /* for leaf nodes, rebind each action assigned to it */ + while (node->actions) { + /* add each action, and remove them from the original tree so + they don't get free'd on us */ + keyboard_bind(node->keylist, node->actions->data); + node->actions = g_slist_delete_link(node->actions, node->actions); + } + + if (node->chroot) + keyboard_chroot(node->keylist); + } + + /* go through each sibling */ + if (node->next_sibling) node_rebind(node->next_sibling); +} + +void keyboard_rebind(void) +{ + KeyBindingTree *old; + + old = keyboard_firstnode; + keyboard_firstnode = NULL; + if (old) + node_rebind(old); + + tree_destroy(old); + set_curpos(NULL); + grab_keys(TRUE); +} + +void keyboard_startup(gboolean reconfig) +{ + grab_keys(TRUE); + popup = popup_new(); + popup_set_text_align(popup, RR_JUSTIFY_CENTER); +} + +void keyboard_shutdown(gboolean reconfig) +{ + if (chain_timer) g_source_remove(chain_timer); + + keyboard_unbind_all(); + set_curpos(NULL); + + popup_free(popup); + popup = NULL; +} diff --git a/openbox/keyboard.h b/openbox/keyboard.h new file mode 100644 index 0000000..c89f67e --- /dev/null +++ b/openbox/keyboard.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + keyboard.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__keybaord_h +#define ob__keybaord_h + +#include "keytree.h" +#include "frame.h" + +#include <glib.h> +#include <X11/Xlib.h> + +struct _ObClient; +struct _ObActionsAct; + +extern KeyBindingTree *keyboard_firstnode; + +void keyboard_startup(gboolean reconfig); +void keyboard_shutdown(gboolean reconfig); + +void keyboard_rebind(void); + +void keyboard_chroot(GList *keylist); +gboolean keyboard_bind(GList *keylist, struct _ObActionsAct *action); +void keyboard_unbind_all(void); + +gboolean keyboard_event(struct _ObClient *client, const XEvent *e); +/*! @param break_chroots how many chroots to break. -1 means to break them ALL! + */ +void keyboard_reset_chains(gint break_chroots); + +#endif diff --git a/openbox/keytree.c b/openbox/keytree.c new file mode 100644 index 0000000..93a0c7a --- /dev/null +++ b/openbox/keytree.c @@ -0,0 +1,152 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + keytree.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "keyboard.h" +#include "translate.h" +#include "actions.h" +#include <glib.h> + +void tree_destroy(KeyBindingTree *tree) +{ + KeyBindingTree *c; + + while (tree) { + tree_destroy(tree->next_sibling); + c = tree->first_child; + if (c == NULL) { + GList *it; + GSList *sit; + for (it = tree->keylist; it != NULL; it = it->next) + g_free(it->data); + g_list_free(tree->keylist); + for (sit = tree->actions; sit != NULL; sit = sit->next) + actions_act_unref(sit->data); + g_slist_free(tree->actions); + } + g_slice_free(KeyBindingTree, tree); + tree = c; + } +} + +KeyBindingTree *tree_build(GList *keylist) +{ + GList *it; + KeyBindingTree *ret = NULL, *p; + + if (g_list_length(keylist) <= 0) + return NULL; /* nothing in the list.. */ + + for (it = g_list_last(keylist); it; it = g_list_previous(it)) { + GList *kit; + + p = ret; + ret = g_slice_new0(KeyBindingTree); + + for (kit = it; kit != NULL; kit = g_list_previous(kit)) + ret->keylist = g_list_prepend(ret->keylist, + g_strdup(kit->data)); /* deep copy */ + ret->first_child = p; + if (p != NULL) p->parent = ret; + translate_key(it->data, &ret->state, &ret->key); + } + return ret; +} + +void tree_assimilate(KeyBindingTree *node) +{ + KeyBindingTree *a, *b, *tmp, *last; + + if (keyboard_firstnode == NULL) { + /* there are no nodes at this level yet */ + keyboard_firstnode = node; + } else { + a = keyboard_firstnode; + last = a; + b = node; + while (a) { + last = a; + /* check b->key != 0 for key bindings that didn't get translated */ + if (!(a->state == b->state && a->key == b->key && b->key != 0)) { + a = a->next_sibling; + } else { + tmp = b; + b = b->first_child; + g_slice_free(KeyBindingTree, tmp); + a = a->first_child; + } + } + /* check b->key != 0, and save key bindings that didn't get translated + as siblings here */ + if (!(last->state == b->state && last->key == b->key && b->key != 0)) { + last->next_sibling = b; + b->parent = last->parent; + } else { + last->first_child = b->first_child; + last->first_child->parent = last; + g_slice_free(KeyBindingTree, b); + } + } +} + +KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict) +{ + KeyBindingTree *a, *b; + + *conflict = FALSE; + + a = keyboard_firstnode; + b = search; + while (a && b) { + /* check b->key != 0 for key bindings that didn't get translated, and + don't make them conflict with anything else so that they can all + live together in peace and harmony */ + if (!(a->state == b->state && a->key == b->key && b->key != 0)) { + a = a->next_sibling; + } else { + if ((a->first_child == NULL) == (b->first_child == NULL)) { + if (a->first_child == NULL) { + /* found it! (return the actual node, not the search's) */ + return a; + } + } else { + *conflict = TRUE; + return NULL; /* the chain status' don't match (conflict!) */ + } + b = b->first_child; + a = a->first_child; + } + } + return NULL; /* it just isn't in here */ +} + +gboolean tree_chroot(KeyBindingTree *tree, GList *keylist) +{ + guint key, state; + translate_key(keylist->data, &state, &key); + while (tree != NULL && !(tree->state == state && tree->key == key)) + tree = tree->next_sibling; + if (tree != NULL) { + if (keylist->next == NULL) { + tree->chroot = TRUE; + return TRUE; + } else + return tree_chroot(tree->first_child, keylist->next); + } + return FALSE; +} diff --git a/openbox/keytree.h b/openbox/keytree.h new file mode 100644 index 0000000..66edc3c --- /dev/null +++ b/openbox/keytree.h @@ -0,0 +1,45 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + keytree.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __plugin_keyboard_tree_h +#define __plugin_keyboard_tree_h + +#include <glib.h> + +typedef struct KeyBindingTree { + guint state; + guint key; + GList *keylist; + GSList *actions; /* list of Action pointers */ + gboolean chroot; + + /* the level up in the tree */ + struct KeyBindingTree *parent; + /* the next binding in the tree at the same level */ + struct KeyBindingTree *next_sibling; + /* the first child of this binding (next binding in a chained sequence).*/ + struct KeyBindingTree *first_child; +} KeyBindingTree; + +void tree_destroy(KeyBindingTree *tree); +KeyBindingTree *tree_build(GList *keylist); +void tree_assimilate(KeyBindingTree *node); +KeyBindingTree *tree_find(KeyBindingTree *search, gboolean *conflict); +gboolean tree_chroot(KeyBindingTree *tree, GList *keylist); + +#endif diff --git a/openbox/menu.c b/openbox/menu.c new file mode 100644 index 0000000..1294c4c --- /dev/null +++ b/openbox/menu.c @@ -0,0 +1,729 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + menu.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "menu.h" +#include "openbox.h" +#include "stacking.h" +#include "grab.h" +#include "client.h" +#include "config.h" +#include "actions.h" +#include "screen.h" +#include "menuframe.h" +#include "keyboard.h" +#include "geom.h" +#include "misc.h" +#include "client_menu.h" +#include "client_list_menu.h" +#include "client_list_combined_menu.h" +#include "gettext.h" +#include "obt/xml.h" +#include "obt/paths.h" + +typedef struct _ObMenuParseState ObMenuParseState; + +struct _ObMenuParseState +{ + ObMenu *parent; + ObMenu *pipe_creator; +}; + +static GHashTable *menu_hash = NULL; +static ObtXmlInst *menu_parse_inst; +static ObMenuParseState menu_parse_state; +static gboolean menu_can_hide = FALSE; +static guint menu_timeout_id = 0; + +static void menu_destroy_hash_value(ObMenu *self); +static void parse_menu_item(xmlNodePtr node, gpointer data); +static void parse_menu_separator(xmlNodePtr node, gpointer data); +static void parse_menu(xmlNodePtr node, gpointer data); +static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut, + gchar **strippedlabel, guint *position, + gboolean *always_show); + +void menu_startup(gboolean reconfig) +{ + gboolean loaded = FALSE; + GSList *it; + + menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)menu_destroy_hash_value); + + client_list_menu_startup(reconfig); + client_list_combined_menu_startup(reconfig); + client_menu_startup(); + + menu_parse_inst = obt_xml_instance_new(); + + menu_parse_state.parent = NULL; + menu_parse_state.pipe_creator = NULL; + obt_xml_register(menu_parse_inst, "menu", parse_menu, &menu_parse_state); + obt_xml_register(menu_parse_inst, "item", parse_menu_item, + &menu_parse_state); + obt_xml_register(menu_parse_inst, "separator", + parse_menu_separator, &menu_parse_state); + + for (it = config_menu_files; it; it = g_slist_next(it)) { + if (obt_xml_load_config_file(menu_parse_inst, + "openbox", + it->data, + "openbox_menu")) + { + loaded = TRUE; + obt_xml_tree_from_root(menu_parse_inst); + obt_xml_close(menu_parse_inst); + } else + g_message(_("Unable to find a valid menu file \"%s\""), + (const gchar*)it->data); + } + if (!loaded) { + if (obt_xml_load_config_file(menu_parse_inst, + "openbox", + "menu.xml", + "openbox_menu")) + { + obt_xml_tree_from_root(menu_parse_inst); + obt_xml_close(menu_parse_inst); + } else + g_message(_("Unable to find a valid menu file \"%s\""), + "menu.xml"); + } + + g_assert(menu_parse_state.parent == NULL); +} + +void menu_shutdown(gboolean reconfig) +{ + obt_xml_instance_unref(menu_parse_inst); + menu_parse_inst = NULL; + + client_list_menu_shutdown(reconfig); + client_list_combined_menu_shutdown(reconfig); + + menu_frame_hide_all(); + g_hash_table_destroy(menu_hash); + menu_hash = NULL; +} + +static gboolean menu_pipe_submenu(gpointer key, gpointer val, gpointer data) +{ + ObMenu *menu = val; + return menu->pipe_creator != NULL; +} + +static void clear_cache(gpointer key, gpointer val, gpointer data) +{ + ObMenu *menu = val; + if (menu->execute) + menu_clear_entries(menu); +} + +void menu_clear_pipe_caches(void) +{ + /* delete any pipe menus' submenus */ + g_hash_table_foreach_remove(menu_hash, menu_pipe_submenu, NULL); + /* empty the top level pipe menus */ + g_hash_table_foreach(menu_hash, clear_cache, NULL); +} + +void menu_pipe_execute(ObMenu *self) +{ + gchar *output; + GError *err = NULL; + + if (!self->execute) + return; + if (self->entries) /* the entries are already created and cached */ + return; + + if (!g_spawn_command_line_sync(self->execute, &output, NULL, NULL, &err)) { + g_message(_("Failed to execute command for pipe-menu \"%s\": %s"), + self->execute, err->message); + g_error_free(err); + return; + } + + if (obt_xml_load_mem(menu_parse_inst, output, strlen(output), + "openbox_pipe_menu")) + { + menu_parse_state.pipe_creator = self; + menu_parse_state.parent = self; + obt_xml_tree_from_root(menu_parse_inst); + obt_xml_close(menu_parse_inst); + } else { + g_message(_("Invalid output from pipe-menu \"%s\""), self->execute); + } + + g_free(output); +} + +static ObMenu* menu_from_name(gchar *name) +{ + ObMenu *self = NULL; + + g_assert(name != NULL); + + if (!(self = g_hash_table_lookup(menu_hash, name))) + g_message(_("Attempted to access menu \"%s\" but it does not exist"), + name); + return self; +} + +#define VALID_SHORTCUT(c) (((c) >= '0' && (c) <= '9') || \ + ((c) >= 'A' && (c) <= 'Z') || \ + ((c) >= 'a' && (c) <= 'z')) + +static gunichar parse_shortcut(const gchar *label, gboolean allow_shortcut, + gchar **strippedlabel, guint *position, + gboolean *always_show) +{ + gunichar shortcut = 0; + + *position = 0; + *always_show = FALSE; + + g_assert(strippedlabel != NULL); + + if (label == NULL) { + *strippedlabel = NULL; + } else { + gchar *i; + gboolean escape; + + *strippedlabel = g_strdup(label); + + /* if allow_shortcut is false, then you can't use the '_', instead you + have to just use the first valid character + */ + + /* allow __ to escape an underscore */ + i = *strippedlabel; + do { + escape = FALSE; + i = strchr(i, '_'); + if (i && *(i+1) == '_') { + gchar *j; + + /* remove the escape '_' from the string */ + for (j = i; *j != '\0'; ++j) + *j = *(j+1); + + ++i; + escape = TRUE; + } + } while (escape); + + if (allow_shortcut && i != NULL) { + /* there is an underscore in the string */ + + /* you have to use a printable ascii character for shortcuts + don't allow space either, so you can have like "a _ b" + */ + if (VALID_SHORTCUT(*(i+1))) { + shortcut = g_unichar_tolower(g_utf8_get_char(i+1)); + *position = i - *strippedlabel; + *always_show = TRUE; + + /* remove the '_' from the string */ + for (; *i != '\0'; ++i) + *i = *(i+1); + } else if (*(i+1) == '\0') { + /* no default shortcut if the '_' is the last character + (eg. "Exit_") for menu entries that you don't want + to be executed by mistake + */ + *i = '\0'; + } + } else { + /* there is no underscore, so find the first valid character to use + instead */ + + for (i = *strippedlabel; *i != '\0'; ++i) + if (VALID_SHORTCUT(*i)) { + *position = i - *strippedlabel; + shortcut = g_unichar_tolower(g_utf8_get_char(i)); + break; + } + } + } + return shortcut; +} + +static void parse_menu_item(xmlNodePtr node, gpointer data) +{ + ObMenuParseState *state = data; + gchar *label; + gchar *icon; + ObMenuEntry *e; + + if (state->parent) { + /* Don't try to extract "icon" attribute if icons in user-defined + menus are not enabled. */ + + if (obt_xml_attr_string(node, "label", &label)) { + xmlNodePtr c; + GSList *acts = NULL; + + c = obt_xml_find_node(node->children, "action"); + while (c) { + ObActionsAct *action = actions_parse(c); + if (action) + acts = g_slist_append(acts, action); + c = obt_xml_find_node(c->next, "action"); + } + e = menu_add_normal(state->parent, -1, label, acts, TRUE); + + if (config_menu_show_icons && + obt_xml_attr_string(node, "icon", &icon)) + { + e->data.normal.icon = RrImageNewFromName(ob_rr_icons, icon); + + if (e->data.normal.icon) + e->data.normal.icon_alpha = 0xff; + + g_free(icon); + } + g_free(label); + } + } +} + +static void parse_menu_separator(xmlNodePtr node, gpointer data) +{ + ObMenuParseState *state = data; + + if (state->parent) { + gchar *label; + + if (!obt_xml_attr_string(node, "label", &label)) + label = NULL; + + menu_add_separator(state->parent, -1, label); + g_free(label); + } +} + +static void parse_menu(xmlNodePtr node, gpointer data) +{ + ObMenuParseState *state = data; + gchar *name = NULL, *title = NULL, *script = NULL; + ObMenu *menu; + ObMenuEntry *e; + gchar *icon; + + if (!obt_xml_attr_string(node, "id", &name)) + goto parse_menu_fail; + + if (!g_hash_table_lookup(menu_hash, name)) { + if (!obt_xml_attr_string(node, "label", &title)) + goto parse_menu_fail; + + if ((menu = menu_new(name, title, TRUE, NULL))) { + menu->pipe_creator = state->pipe_creator; + if (obt_xml_attr_string(node, "execute", &script)) { + menu->execute = obt_paths_expand_tilde(script); + } else { + ObMenu *old; + + old = state->parent; + state->parent = menu; + obt_xml_tree(menu_parse_inst, node->children); + state->parent = old; + } + } + } + + if (state->parent) { + e = menu_add_submenu(state->parent, -1, name); + + if (config_menu_show_icons && + obt_xml_attr_string(node, "icon", &icon)) + { + e->data.submenu.icon = RrImageNewFromName(ob_rr_icons, icon); + + if (e->data.submenu.icon) + e->data.submenu.icon_alpha = 0xff; + + g_free(icon); + } + } + +parse_menu_fail: + g_free(name); + g_free(title); + g_free(script); +} + +ObMenu* menu_new(const gchar *name, const gchar *title, + gboolean allow_shortcut_selection, gpointer data) +{ + ObMenu *self; + + self = g_slice_new0(ObMenu); + self->name = g_strdup(name); + self->data = data; + + self->shortcut = parse_shortcut(title, allow_shortcut_selection, + &self->title, &self->shortcut_position, + &self->shortcut_always_show); + + g_hash_table_replace(menu_hash, self->name, self); + + /* Each menu has a single more_menu. When the menu spills past what + can fit on the screen, a new menu frame entry is created from this + more_menu, and a new menu frame for the submenu is created for this + menu, also pointing to the more_menu. + + This can be done multiple times using the same more_menu. + + more_menu->more_menu will always be NULL, since there is only 1 for + each menu. */ + self->more_menu = g_slice_new0(ObMenu); + self->more_menu->name = _("More..."); + self->more_menu->title = _("More..."); + self->more_menu->data = data; + self->more_menu->shortcut = g_unichar_tolower(g_utf8_get_char("M")); + + return self; +} + +static void menu_destroy_hash_value(ObMenu *self) +{ + /* make sure its not visible */ + { + GList *it; + ObMenuFrame *f; + + for (it = menu_frame_visible; it; it = g_list_next(it)) { + f = it->data; + if (f->menu == self) + menu_frame_hide_all(); + } + } + + if (self->destroy_func) + self->destroy_func(self, self->data); + + menu_clear_entries(self); + g_free(self->name); + g_free(self->title); + g_free(self->execute); + g_slice_free(ObMenu, self->more_menu); + + g_slice_free(ObMenu, self); +} + +void menu_free(ObMenu *menu) +{ + if (menu) + g_hash_table_remove(menu_hash, menu->name); +} + +static gboolean menu_hide_delay_func(gpointer data) +{ + menu_can_hide = TRUE; + menu_timeout_id = 0; + return FALSE; /* no repeat */ +} + +void menu_show(gchar *name, gint x, gint y, gboolean mouse, ObClient *client) +{ + ObMenu *self; + ObMenuFrame *frame; + + if (!(self = menu_from_name(name)) || + grab_on_keyboard() || grab_on_pointer()) return; + + /* if the requested menu is already the top visible menu, then don't + bother */ + if (menu_frame_visible) { + frame = menu_frame_visible->data; + if (frame->menu == self) + return; + } + + menu_frame_hide_all(); + + /* clear the pipe menus when showing a new menu */ + menu_clear_pipe_caches(); + + frame = menu_frame_new(self, 0, client); + if (!menu_frame_show_topmenu(frame, x, y, mouse)) + menu_frame_free(frame); + else { + if (!mouse) { + /* select the first entry if it's not a submenu and we opened + * the menu with the keyboard, and skip all headers */ + GList *it = frame->entries; + while (it) { + ObMenuEntryFrame *e = it->data; + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) { + menu_frame_select(frame, e, FALSE); + break; + } else if (e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR) + it = g_list_next(it); + else + break; + } + } + + /* reset the hide timer */ + if (!mouse) + menu_can_hide = TRUE; + else { + menu_can_hide = FALSE; + if (menu_timeout_id) g_source_remove(menu_timeout_id); + menu_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, + config_menu_hide_delay, + menu_hide_delay_func, + NULL, NULL); + } + } +} + +gboolean menu_hide_delay_reached(void) +{ + return menu_can_hide; +} + +static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type, gint id) +{ + ObMenuEntry *self; + + g_assert(menu); + + self = g_slice_new0(ObMenuEntry); + self->ref = 1; + self->type = type; + self->menu = menu; + self->id = id; + + switch (type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + self->data.normal.enabled = TRUE; + break; + case OB_MENU_ENTRY_TYPE_SUBMENU: + case OB_MENU_ENTRY_TYPE_SEPARATOR: + break; + } + + return self; +} + +void menu_entry_ref(ObMenuEntry *self) +{ + ++self->ref; +} + +void menu_entry_unref(ObMenuEntry *self) +{ + if (self && --self->ref == 0) { + switch (self->type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + RrImageUnref(self->data.normal.icon); + g_free(self->data.normal.label); + while (self->data.normal.actions) { + actions_act_unref(self->data.normal.actions->data); + self->data.normal.actions = + g_slist_delete_link(self->data.normal.actions, + self->data.normal.actions); + } + break; + case OB_MENU_ENTRY_TYPE_SUBMENU: + RrImageUnref(self->data.submenu.icon); + g_free(self->data.submenu.name); + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + g_free(self->data.separator.label); + break; + } + + g_slice_free(ObMenuEntry, self); + } +} + +void menu_clear_entries(ObMenu *self) +{ +#ifdef DEBUG + /* assert that the menu isn't visible */ + { + GList *it; + ObMenuFrame *f; + + for (it = menu_frame_visible; it; it = g_list_next(it)) { + f = it->data; + g_assert(f->menu != self); + } + } +#endif + + while (self->entries) { + menu_entry_unref(self->entries->data); + self->entries = g_list_delete_link(self->entries, self->entries); + } + self->more_menu->entries = self->entries; /* keep it in sync */ +} + +void menu_entry_remove(ObMenuEntry *self) +{ + self->menu->entries = g_list_remove(self->menu->entries, self); + menu_entry_unref(self); +} + +ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label, + GSList *actions, gboolean allow_shortcut) +{ + ObMenuEntry *e; + + e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id); + e->data.normal.actions = actions; + + menu_entry_set_label(e, label, allow_shortcut); + + self->entries = g_list_append(self->entries, e); + self->more_menu->entries = self->entries; /* keep it in sync */ + return e; +} + +ObMenuEntry* menu_get_more(ObMenu *self, guint show_from) +{ + ObMenuEntry *e; + e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, -1); + /* points to itself */ + e->data.submenu.name = g_strdup(self->name); + e->data.submenu.submenu = self; + e->data.submenu.show_from = show_from; + return e; +} + +ObMenuEntry* menu_add_submenu(ObMenu *self, gint id, const gchar *submenu) +{ + ObMenuEntry *e; + + e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU, id); + e->data.submenu.name = g_strdup(submenu); + + self->entries = g_list_append(self->entries, e); + self->more_menu->entries = self->entries; /* keep it in sync */ + return e; +} + +ObMenuEntry* menu_add_separator(ObMenu *self, gint id, const gchar *label) +{ + ObMenuEntry *e; + + e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id); + + menu_entry_set_label(e, label, FALSE); + + self->entries = g_list_append(self->entries, e); + self->more_menu->entries = self->entries; /* keep it in sync */ + return e; +} + +void menu_set_show_func(ObMenu *self, ObMenuShowFunc func) +{ + self->show_func = func; +} + +void menu_set_hide_func(ObMenu *self, ObMenuHideFunc func) +{ + self->hide_func = func; +} + +void menu_set_update_func(ObMenu *self, ObMenuUpdateFunc func) +{ + self->update_func = func; +} + +void menu_set_execute_func(ObMenu *self, ObMenuExecuteFunc func) +{ + self->execute_func = func; + self->more_menu->execute_func = func; /* keep it in sync */ +} + +void menu_set_cleanup_func(ObMenu *self, ObMenuCleanupFunc func) +{ + self->cleanup_func = func; +} + +void menu_set_destroy_func(ObMenu *self, ObMenuDestroyFunc func) +{ + self->destroy_func = func; +} + +void menu_set_place_func(ObMenu *self, ObMenuPlaceFunc func) +{ + self->place_func = func; +} + +ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id) +{ + ObMenuEntry *ret = NULL; + GList *it; + + for (it = self->entries; it; it = g_list_next(it)) { + ObMenuEntry *e = it->data; + + if (e->id == id) { + ret = e; + break; + } + } + return ret; +} + +void menu_find_submenus(ObMenu *self) +{ + GList *it; + + for (it = self->entries; it; it = g_list_next(it)) { + ObMenuEntry *e = it->data; + + if (e->type == OB_MENU_ENTRY_TYPE_SUBMENU) + e->data.submenu.submenu = menu_from_name(e->data.submenu.name); + } +} + +void menu_entry_set_label(ObMenuEntry *self, const gchar *label, + gboolean allow_shortcut) +{ + switch (self->type) { + case OB_MENU_ENTRY_TYPE_SEPARATOR: + g_free(self->data.separator.label); + self->data.separator.label = g_strdup(label); + break; + case OB_MENU_ENTRY_TYPE_NORMAL: + g_free(self->data.normal.label); + self->data.normal.shortcut = + parse_shortcut(label, allow_shortcut, &self->data.normal.label, + &self->data.normal.shortcut_position, + &self->data.normal.shortcut_always_show); + break; + default: + g_assert_not_reached(); + } +} + +void menu_show_all_shortcuts(ObMenu *self, gboolean show) +{ + self->show_all_shortcuts = show; +} diff --git a/openbox/menu.h b/openbox/menu.h new file mode 100644 index 0000000..76cc238 --- /dev/null +++ b/openbox/menu.h @@ -0,0 +1,227 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + menu.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __menu_h +#define __menu_h + +#include "window.h" +#include "geom.h" +#include "obrender/render.h" + +#include <glib.h> + +struct _ObClient; +struct _ObMenuFrame; +struct _ObMenuEntryFrame; + +typedef struct _ObMenu ObMenu; +typedef struct _ObMenuEntry ObMenuEntry; +typedef struct _ObNormalMenuEntry ObNormalMenuEntry; +typedef struct _ObSubmenuMenuEntry ObSubmenuMenuEntry; +typedef struct _ObSeparatorMenuEntry ObSeparatorMenuEntry; + +typedef void (*ObMenuShowFunc)(struct _ObMenuFrame *frame, gpointer data); +typedef void (*ObMenuHideFunc)(struct _ObMenuFrame *frame, gpointer data); +typedef gboolean (*ObMenuUpdateFunc)(struct _ObMenuFrame *frame, + gpointer data); +typedef void (*ObMenuExecuteFunc)(struct _ObMenuEntry *entry, + struct _ObMenuFrame *frame, + struct _ObClient *client, + guint state, gpointer data); +typedef void (*ObMenuCleanupFunc)(struct _ObMenu *menu, gpointer data); +typedef void (*ObMenuDestroyFunc)(struct _ObMenu *menu, gpointer data); +/*! @param x is the mouse x coordinate. on return it should be the x coordinate + for the menu + @param y is the mouse y coordinate. on return it should be the y coordinate + for the menu +*/ +typedef void (*ObMenuPlaceFunc)(struct _ObMenuFrame *frame, gint *x, gint *y, + gboolean mouse, gpointer data); + +struct _ObMenu +{ + /* Name of the menu. Used in the showmenu action. */ + gchar *name; + /* Displayed title */ + gchar *title; + /*! The shortcut key that would be used to activate this menu if it was + displayed as a submenu */ + gunichar shortcut; + /*! The shortcut's position in the string */ + guint shortcut_position; + /*! If the shortcut was specified by & and should always be drawn */ + gboolean shortcut_always_show; + + /*! If the shortcut key should be shown in menu entries even when it + is the first character in the string */ + gboolean show_all_shortcuts; + + /* Command to execute to rebuild the menu */ + gchar *execute; + + /* ObMenuEntry list */ + GList *entries; + + /* plugin data */ + gpointer data; + + ObMenuShowFunc show_func; + ObMenuHideFunc hide_func; + ObMenuUpdateFunc update_func; + ObMenuExecuteFunc execute_func; + ObMenuCleanupFunc cleanup_func; + ObMenuDestroyFunc destroy_func; + ObMenuPlaceFunc place_func; + + /* Pipe-menu parent, we get destroyed when it is destroyed */ + ObMenu *pipe_creator; + + /* The menu used as the destination for the "More..." entry for this menu*/ + ObMenu *more_menu; +}; + +typedef enum +{ + OB_MENU_ENTRY_TYPE_NORMAL, + OB_MENU_ENTRY_TYPE_SUBMENU, + OB_MENU_ENTRY_TYPE_SEPARATOR +} ObMenuEntryType; + +struct _ObNormalMenuEntry { + /* Icon stuff. If you set this, make sure you RrImageRef() it too. */ + RrImage *icon; + gint icon_alpha; + + gchar *label; + /*! The shortcut key that would be used to activate this menu entry */ + gunichar shortcut; + /*! The shortcut's position in the string */ + guint shortcut_position; + /*! If the shortcut was specified by & and should always be drawn */ + gboolean shortcut_always_show; + + /* state */ + gboolean enabled; + + /* List of ObActions */ + GSList *actions; + + /* Mask icon */ + RrPixmapMask *mask; + RrColor *mask_normal_color; + RrColor *mask_selected_color; + RrColor *mask_disabled_color; + RrColor *mask_disabled_selected_color; + + gpointer data; +}; + +struct _ObSubmenuMenuEntry { + /* Icon stuff. If you set this, make sure you RrImageRef() it too. */ + RrImage *icon; + gint icon_alpha; + + gchar *name; + ObMenu *submenu; + + guint show_from; +}; + +struct _ObSeparatorMenuEntry { + gchar *label; +}; + +struct _ObMenuEntry +{ + guint ref; + + ObMenuEntryType type; + ObMenu *menu; + + gint id; + + union u { + ObNormalMenuEntry normal; + ObSubmenuMenuEntry submenu; + ObSeparatorMenuEntry separator; + } data; +}; + +void menu_startup(gboolean reconfig); +void menu_shutdown(gboolean reconfig); + +void menu_entry_ref(ObMenuEntry *self); +void menu_entry_unref(ObMenuEntry *self); + +ObMenu* menu_new(const gchar *name, const gchar *title, + gboolean allow_shortcut_selection, gpointer data); +void menu_free(ObMenu *menu); + +/*! Repopulate a pipe-menu by running its command */ +void menu_pipe_execute(ObMenu *self); +/*! Clear a pipe-menu's entries */ +void menu_clear_pipe_caches(void); + +void menu_show_all_shortcuts(ObMenu *self, gboolean show); + +void menu_show(gchar *name, gint x, gint y, gboolean mouse, + struct _ObClient *client); +gboolean menu_hide_delay_reached(void); + +/*! The show function is called right after a menu is shown */ +void menu_set_show_func(ObMenu *menu, ObMenuShowFunc func); +/*! The hide function is called right before a menu is hidden */ +void menu_set_hide_func(ObMenu *menu, ObMenuHideFunc func); +/*! The update function is called when the menu should refresh its + contents, generally right before it is shown */ +void menu_set_update_func(ObMenu *menu, ObMenuUpdateFunc func); +/*! The execute function is called when a user chooses to execute an + entry in the menu */ +void menu_set_execute_func(ObMenu *menu, ObMenuExecuteFunc func); +/*! The cleanup function is called after a menu is hidden, allowing it + to be cleaned up between uses */ +void menu_set_cleanup_func(ObMenu *menu, ObMenuCleanupFunc func); +/*! The destroy function is called when the menu is being destroyed + permanently */ +void menu_set_destroy_func(ObMenu *menu, ObMenuDestroyFunc func); +/*! The place function is called when the menu is being shown and allows + the menu to choose its initial position */ +void menu_set_place_func(ObMenu *menu, ObMenuPlaceFunc func); + +/* functions for building menus */ +/*! @param allow_shortcut this should be false when the label is coming from + outside data like window or desktop titles */ +ObMenuEntry* menu_add_normal(ObMenu *menu, gint id, const gchar *label, + GSList *actions, gboolean allow_shortcut); +ObMenuEntry* menu_add_submenu(ObMenu *menu, gint id, const gchar *submenu); +ObMenuEntry* menu_add_separator(ObMenu *menu, gint id, const gchar *label); + +void menu_clear_entries(ObMenu *menu); +void menu_entry_remove(ObMenuEntry *self); + +void menu_entry_set_label(ObMenuEntry *self, const gchar *label, + gboolean allow_shortcut); + +ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id); + +/* fills in the submenus, for use when a menu is being shown */ +void menu_find_submenus(ObMenu *self); + +ObMenuEntry* menu_get_more(ObMenu *menu, guint show_from); + +#endif diff --git a/openbox/menuframe.c b/openbox/menuframe.c new file mode 100644 index 0000000..6110045 --- /dev/null +++ b/openbox/menuframe.c @@ -0,0 +1,1387 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + menuframe.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "menuframe.h" +#include "client.h" +#include "menu.h" +#include "screen.h" +#include "actions.h" +#include "event.h" +#include "grab.h" +#include "openbox.h" +#include "config.h" +#include "obt/prop.h" +#include "obt/keyboard.h" +#include "obrender/theme.h" + +#define PADDING 2 +#define MAX_MENU_WIDTH 400 + +#define ITEM_HEIGHT (ob_rr_theme->menu_font_height + 2*PADDING) + +#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\ + LeaveWindowMask) +#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \ + ButtonPressMask | ButtonReleaseMask) + +GList *menu_frame_visible; +GHashTable *menu_frame_map; + +static RrAppearance *a_sep; +static guint submenu_show_timer = 0; +static guint submenu_hide_timer = 0; + +static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry, + ObMenuFrame *frame); +static void menu_entry_frame_free(ObMenuEntryFrame *self); +static void menu_frame_update(ObMenuFrame *self); +static gboolean submenu_show_timeout(gpointer data); +static void menu_frame_hide(ObMenuFrame *self); + +static gboolean submenu_hide_timeout(gpointer data); + +static Window createWindow(Window parent, gulong mask, + XSetWindowAttributes *attrib) +{ + return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), mask, attrib); +} + +static void client_dest(ObClient *client, gpointer data) +{ + GList *it; + + /* menus can be associated with a client, so null those refs since + we are disappearing now */ + for (it = menu_frame_visible; it; it = g_list_next(it)) { + ObMenuFrame *f = it->data; + if (f->client == client) + f->client = NULL; + } +} + +void menu_frame_startup(gboolean reconfig) +{ + gint i; + + a_sep = RrAppearanceCopy(ob_rr_theme->a_clear); + RrAppearanceAddTextures(a_sep, ob_rr_theme->menu_sep_width); + for (i = 0; i < ob_rr_theme->menu_sep_width; ++i) { + a_sep->texture[i].type = RR_TEXTURE_LINE_ART; + a_sep->texture[i].data.lineart.color = + ob_rr_theme->menu_sep_color; + } + + if (reconfig) return; + + client_add_destroy_notify(client_dest, NULL); + menu_frame_map = g_hash_table_new(g_int_hash, g_int_equal); +} + +void menu_frame_shutdown(gboolean reconfig) +{ + RrAppearanceFree(a_sep); + + if (reconfig) return; + + client_remove_destroy_notify(client_dest); + g_hash_table_destroy(menu_frame_map); +} + +ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client) +{ + ObMenuFrame *self; + XSetWindowAttributes attr; + + self = g_slice_new0(ObMenuFrame); + self->obwin.type = OB_WINDOW_CLASS_MENUFRAME; + self->menu = menu; + self->selected = NULL; + self->client = client; + self->direction_right = TRUE; + self->show_from = show_from; + + attr.event_mask = FRAME_EVENTMASK; + self->window = createWindow(obt_root(ob_screen), + CWEventMask, &attr); + + /* make it a popup menu type window */ + OBT_PROP_SET32(self->window, NET_WM_WINDOW_TYPE, ATOM, + OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_POPUP_MENU)); + + XSetWindowBorderWidth(obt_display, self->window, ob_rr_theme->mbwidth); + XSetWindowBorder(obt_display, self->window, + RrColorPixel(ob_rr_theme->menu_border_color)); + + self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu); + + window_add(&self->window, MENUFRAME_AS_WINDOW(self)); + stacking_add(MENUFRAME_AS_WINDOW(self)); + + return self; +} + +void menu_frame_free(ObMenuFrame *self) +{ + if (self) { + while (self->entries) { + menu_entry_frame_free(self->entries->data); + self->entries = g_list_delete_link(self->entries, self->entries); + } + + stacking_remove(MENUFRAME_AS_WINDOW(self)); + window_remove(self->window); + + RrAppearanceFree(self->a_items); + + XDestroyWindow(obt_display, self->window); + + g_slice_free(ObMenuFrame, self); + } +} + +ObtIC* menu_frame_ic(ObMenuFrame *self) +{ + /* menus are always used through a grab right now, so they can always use + the grab input context */ + return grab_input_context(); +} + +static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry, + ObMenuFrame *frame) +{ + ObMenuEntryFrame *self; + XSetWindowAttributes attr; + + self = g_slice_new0(ObMenuEntryFrame); + self->entry = entry; + self->frame = frame; + + menu_entry_ref(entry); + + attr.event_mask = ENTRY_EVENTMASK; + self->window = createWindow(self->frame->window, CWEventMask, &attr); + self->text = createWindow(self->window, 0, NULL); + g_hash_table_insert(menu_frame_map, &self->window, self); + g_hash_table_insert(menu_frame_map, &self->text, self); + if ((entry->type == OB_MENU_ENTRY_TYPE_NORMAL) || + (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) { + self->icon = createWindow(self->window, 0, NULL); + g_hash_table_insert(menu_frame_map, &self->icon, self); + } + if (entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + self->bullet = createWindow(self->window, 0, NULL); + g_hash_table_insert(menu_frame_map, &self->bullet, self); + } + + XMapWindow(obt_display, self->window); + XMapWindow(obt_display, self->text); + + window_add(&self->window, MENUFRAME_AS_WINDOW(self->frame)); + + return self; +} + +static void menu_entry_frame_free(ObMenuEntryFrame *self) +{ + if (self) { + menu_entry_unref(self->entry); + + window_remove(self->window); + + XDestroyWindow(obt_display, self->text); + XDestroyWindow(obt_display, self->window); + g_hash_table_remove(menu_frame_map, &self->text); + g_hash_table_remove(menu_frame_map, &self->window); + if ((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) || + (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) { + XDestroyWindow(obt_display, self->icon); + g_hash_table_remove(menu_frame_map, &self->icon); + } + if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + XDestroyWindow(obt_display, self->bullet); + g_hash_table_remove(menu_frame_map, &self->bullet); + } + + g_slice_free(ObMenuEntryFrame, self); + } +} + +void menu_frame_move(ObMenuFrame *self, gint x, gint y) +{ + RECT_SET_POINT(self->area, x, y); + self->monitor = screen_find_monitor_point(x, y); + XMoveWindow(obt_display, self->window, self->area.x, self->area.y); +} + +static void menu_frame_place_topmenu(ObMenuFrame *self, gint *x, gint *y) +{ + gint dx, dy; + + if (config_menu_middle) { + gint myx; + + myx = *x; + *y -= self->area.height / 2; + + /* try to the right of the cursor */ + menu_frame_move_on_screen(self, myx, *y, &dx, &dy); + self->direction_right = TRUE; + if (dx != 0) { + /* try to the left of the cursor */ + myx = *x - self->area.width; + menu_frame_move_on_screen(self, myx, *y, &dx, &dy); + self->direction_right = FALSE; + } + if (dx != 0) { + /* if didnt fit on either side so just use what it says */ + myx = *x; + menu_frame_move_on_screen(self, myx, *y, &dx, &dy); + self->direction_right = TRUE; + } + *x = myx + dx; + *y += dy; + } else { + gint myx, myy; + + myx = *x; + myy = *y; + + /* try to the bottom right of the cursor */ + menu_frame_move_on_screen(self, myx, myy, &dx, &dy); + self->direction_right = TRUE; + if (dx != 0 || dy != 0) { + /* try to the bottom left of the cursor */ + myx = *x - self->area.width; + myy = *y; + menu_frame_move_on_screen(self, myx, myy, &dx, &dy); + self->direction_right = FALSE; + } + if (dx != 0 || dy != 0) { + /* try to the top right of the cursor */ + myx = *x; + myy = *y - self->area.height; + menu_frame_move_on_screen(self, myx, myy, &dx, &dy); + self->direction_right = TRUE; + } + if (dx != 0 || dy != 0) { + /* try to the top left of the cursor */ + myx = *x - self->area.width; + myy = *y - self->area.height; + menu_frame_move_on_screen(self, myx, myy, &dx, &dy); + self->direction_right = FALSE; + } + if (dx != 0 || dy != 0) { + /* if didnt fit on either side so just use what it says */ + myx = *x; + myy = *y; + menu_frame_move_on_screen(self, myx, myy, &dx, &dy); + self->direction_right = TRUE; + } + *x = myx + dx; + *y = myy + dy; + } +} + +static void menu_frame_place_submenu(ObMenuFrame *self, gint *x, gint *y) +{ + gint overlapx, overlapy; + gint bwidth; + + overlapx = ob_rr_theme->menu_overlap_x; + overlapy = ob_rr_theme->menu_overlap_y; + bwidth = ob_rr_theme->mbwidth; + + if (self->direction_right) + *x = self->parent->area.x + self->parent->area.width - + overlapx - bwidth; + else + *x = self->parent->area.x - self->area.width + overlapx + bwidth; + + *y = self->parent->area.y + self->parent_entry->area.y; + if (config_menu_middle) + *y -= (self->area.height - (bwidth * 2) - ITEM_HEIGHT) / 2; + else + *y += overlapy; +} + +void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y, + gint *dx, gint *dy) +{ + const Rect *a = NULL; + Rect search = self->area; + gint pos, half, monitor; + + *dx = *dy = 0; + RECT_SET_POINT(search, x, y); + + if (self->parent) + monitor = self->parent->monitor; + else + monitor = screen_find_monitor(&search); + + a = screen_physical_area_monitor(monitor); + + half = g_list_length(self->entries) / 2; + pos = g_list_index(self->entries, self->selected); + + /* if in the bottom half then check this stuff first, will keep the bottom + edge of the menu visible */ + if (pos > half) { + *dx = MAX(*dx, a->x - x); + *dy = MAX(*dy, a->y - y); + } + *dx = MIN(*dx, (a->x + a->width) - (x + self->area.width)); + *dy = MIN(*dy, (a->y + a->height) - (y + self->area.height)); + /* if in the top half then check this stuff last, will keep the top + edge of the menu visible */ + if (pos <= half) { + *dx = MAX(*dx, a->x - x); + *dy = MAX(*dy, a->y - y); + } +} + +static void menu_entry_frame_render(ObMenuEntryFrame *self) +{ + RrAppearance *item_a, *text_a; + gint th; /* temp */ + ObMenu *sub; + ObMenuFrame *frame = self->frame; + + switch (self->entry->type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + case OB_MENU_ENTRY_TYPE_SUBMENU: + item_a = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + !self->entry->data.normal.enabled ? + /* disabled */ + (self == self->frame->selected ? + ob_rr_theme->a_menu_disabled_selected : + ob_rr_theme->a_menu_disabled) : + /* enabled */ + (self == self->frame->selected ? + ob_rr_theme->a_menu_selected : + ob_rr_theme->a_menu_normal)); + th = ITEM_HEIGHT; + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + if (self->entry->data.separator.label) { + item_a = ob_rr_theme->a_menu_title; + th = ob_rr_theme->menu_title_height; + } else { + item_a = ob_rr_theme->a_menu_normal; + th = ob_rr_theme->menu_sep_width + + 2*ob_rr_theme->menu_sep_paddingy; + } + break; + default: + g_assert_not_reached(); + } + + RECT_SET_SIZE(self->area, self->frame->inner_w, th); + XResizeWindow(obt_display, self->window, + self->area.width, self->area.height); + item_a->surface.parent = self->frame->a_items; + item_a->surface.parentx = self->area.x; + item_a->surface.parenty = self->area.y; + RrPaint(item_a, self->window, self->area.width, self->area.height); + + switch (self->entry->type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + text_a = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + !self->entry->data.normal.enabled ? + /* disabled */ + (self == self->frame->selected ? + ob_rr_theme->a_menu_text_disabled_selected : + ob_rr_theme->a_menu_text_disabled) : + /* enabled */ + (self == self->frame->selected ? + ob_rr_theme->a_menu_text_selected : + ob_rr_theme->a_menu_text_normal)); + text_a->texture[0].data.text.string = self->entry->data.normal.label; + if (self->entry->data.normal.shortcut && + (self->frame->menu->show_all_shortcuts || + self->entry->data.normal.shortcut_always_show || + self->entry->data.normal.shortcut_position > 0)) + { + text_a->texture[0].data.text.shortcut = TRUE; + text_a->texture[0].data.text.shortcut_pos = + self->entry->data.normal.shortcut_position; + } else + text_a->texture[0].data.text.shortcut = FALSE; + break; + case OB_MENU_ENTRY_TYPE_SUBMENU: + text_a = (self == self->frame->selected ? + ob_rr_theme->a_menu_text_selected : + ob_rr_theme->a_menu_text_normal); + sub = self->entry->data.submenu.submenu; + text_a->texture[0].data.text.string = sub ? sub->title : ""; + if (sub && sub->shortcut && (self->frame->menu->show_all_shortcuts || + sub->shortcut_always_show || + sub->shortcut_position > 0)) + { + text_a->texture[0].data.text.shortcut = TRUE; + text_a->texture[0].data.text.shortcut_pos = sub->shortcut_position; + } else + text_a->texture[0].data.text.shortcut = FALSE; + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + if (self->entry->data.separator.label != NULL) { + text_a = ob_rr_theme->a_menu_text_title; + text_a->texture[0].data.text.string = + self->entry->data.separator.label; + } + else + text_a = ob_rr_theme->a_menu_text_normal; + break; + default: + g_assert_not_reached(); + } + + switch (self->entry->type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + XMoveResizeWindow(obt_display, self->text, + self->frame->text_x, PADDING, + self->frame->text_w, + ITEM_HEIGHT - 2*PADDING); + text_a->surface.parent = item_a; + text_a->surface.parentx = self->frame->text_x; + text_a->surface.parenty = PADDING; + RrPaint(text_a, self->text, self->frame->text_w, + ITEM_HEIGHT - 2*PADDING); + break; + case OB_MENU_ENTRY_TYPE_SUBMENU: + XMoveResizeWindow(obt_display, self->text, + self->frame->text_x, PADDING, + self->frame->text_w - ITEM_HEIGHT, + ITEM_HEIGHT - 2*PADDING); + text_a->surface.parent = item_a; + text_a->surface.parentx = self->frame->text_x; + text_a->surface.parenty = PADDING; + RrPaint(text_a, self->text, self->frame->text_w - ITEM_HEIGHT, + ITEM_HEIGHT - 2*PADDING); + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + if (self->entry->data.separator.label != NULL) { + /* labeled separator */ + XMoveResizeWindow(obt_display, self->text, + ob_rr_theme->paddingx, ob_rr_theme->paddingy, + self->area.width - 2*ob_rr_theme->paddingx, + ob_rr_theme->menu_title_height - + 2*ob_rr_theme->paddingy); + text_a->surface.parent = item_a; + text_a->surface.parentx = ob_rr_theme->paddingx; + text_a->surface.parenty = ob_rr_theme->paddingy; + RrPaint(text_a, self->text, + self->area.width - 2*ob_rr_theme->paddingx, + ob_rr_theme->menu_title_height - + 2*ob_rr_theme->paddingy); + } else { + gint i; + + /* unlabeled separator */ + XMoveResizeWindow(obt_display, self->text, 0, 0, + self->area.width, + ob_rr_theme->menu_sep_width + + 2*ob_rr_theme->menu_sep_paddingy); + + a_sep->surface.parent = item_a; + a_sep->surface.parentx = 0; + a_sep->surface.parenty = 0; + for (i = 0; i < ob_rr_theme->menu_sep_width; ++i) { + a_sep->texture[i].data.lineart.x1 = + ob_rr_theme->menu_sep_paddingx; + a_sep->texture[i].data.lineart.y1 = + ob_rr_theme->menu_sep_paddingy + i; + a_sep->texture[i].data.lineart.x2 = + self->area.width - ob_rr_theme->menu_sep_paddingx - 1; + a_sep->texture[i].data.lineart.y2 = + ob_rr_theme->menu_sep_paddingy + i; + } + + RrPaint(a_sep, self->text, self->area.width, + ob_rr_theme->menu_sep_width + + 2*ob_rr_theme->menu_sep_paddingy); + } + break; + default: + g_assert_not_reached(); + } + + if (((self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) || + (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)) && + self->entry->data.normal.icon) + { + RrAppearance *clear; + + XMoveResizeWindow(obt_display, self->icon, + PADDING, frame->item_margin.top, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom); + + clear = ob_rr_theme->a_clear_tex; + RrAppearanceClearTextures(clear); + clear->texture[0].type = RR_TEXTURE_IMAGE; + clear->texture[0].data.image.image = + self->entry->data.normal.icon; + clear->texture[0].data.image.alpha = + self->entry->data.normal.icon_alpha; + clear->surface.parent = item_a; + clear->surface.parentx = PADDING; + clear->surface.parenty = frame->item_margin.top; + RrPaint(clear, self->icon, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom); + XMapWindow(obt_display, self->icon); + } else if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + self->entry->data.normal.mask) + { + RrColor *c; + RrAppearance *clear; + + XMoveResizeWindow(obt_display, self->icon, + PADDING, frame->item_margin.top, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom); + + clear = ob_rr_theme->a_clear_tex; + RrAppearanceClearTextures(clear); + clear->texture[0].type = RR_TEXTURE_MASK; + clear->texture[0].data.mask.mask = + self->entry->data.normal.mask; + + c = (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + !self->entry->data.normal.enabled ? + /* disabled */ + (self == self->frame->selected ? + self->entry->data.normal.mask_disabled_selected_color : + self->entry->data.normal.mask_disabled_color) : + /* enabled */ + (self == self->frame->selected ? + self->entry->data.normal.mask_selected_color : + self->entry->data.normal.mask_normal_color)); + clear->texture[0].data.mask.color = c; + + clear->surface.parent = item_a; + clear->surface.parentx = PADDING; + clear->surface.parenty = frame->item_margin.top; + RrPaint(clear, self->icon, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom, + ITEM_HEIGHT - frame->item_margin.top + - frame->item_margin.bottom); + XMapWindow(obt_display, self->icon); + } else + XUnmapWindow(obt_display, self->icon); + + if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + RrAppearance *bullet_a; + XMoveResizeWindow(obt_display, self->bullet, + self->frame->text_x + self->frame->text_w - + ITEM_HEIGHT + PADDING, PADDING, + ITEM_HEIGHT - 2*PADDING, + ITEM_HEIGHT - 2*PADDING); + bullet_a = (self == self->frame->selected ? + ob_rr_theme->a_menu_bullet_selected : + ob_rr_theme->a_menu_bullet_normal); + bullet_a->surface.parent = item_a; + bullet_a->surface.parentx = + self->frame->text_x + self->frame->text_w - ITEM_HEIGHT + PADDING; + bullet_a->surface.parenty = PADDING; + RrPaint(bullet_a, self->bullet, + ITEM_HEIGHT - 2*PADDING, + ITEM_HEIGHT - 2*PADDING); + XMapWindow(obt_display, self->bullet); + } else + XUnmapWindow(obt_display, self->bullet); + + XFlush(obt_display); +} + +/*! this code is taken from the menu_frame_render. if that changes, this won't + work.. */ +static gint menu_entry_frame_get_height(ObMenuEntryFrame *self, + gboolean first_entry, + gboolean last_entry) +{ + ObMenuEntryType t; + gint h = 0; + + h += 2*PADDING; + + if (self) + t = self->entry->type; + else + /* this is the More... entry, it's NORMAL type */ + t = OB_MENU_ENTRY_TYPE_NORMAL; + + switch (t) { + case OB_MENU_ENTRY_TYPE_NORMAL: + case OB_MENU_ENTRY_TYPE_SUBMENU: + h += ob_rr_theme->menu_font_height; + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + if (self->entry->data.separator.label != NULL) { + h += ob_rr_theme->menu_title_height + + (ob_rr_theme->mbwidth - PADDING) * 2; + + /* if the first entry is a labeled separator, then make its border + overlap with the menu's outside border */ + if (first_entry) + h -= ob_rr_theme->mbwidth; + /* if the last entry is a labeled separator, then make its border + overlap with the menu's outside border */ + if (last_entry) + h -= ob_rr_theme->mbwidth; + } else { + h += ob_rr_theme->menu_sep_width + + 2*ob_rr_theme->menu_sep_paddingy - PADDING * 2; + } + break; + } + + return h; +} + +void menu_frame_render(ObMenuFrame *self) +{ + gint w = 0, h = 0; + gint tw, th; /* temps */ + GList *it; + gboolean has_icon = FALSE; + ObMenu *sub; + ObMenuEntryFrame *e; + + /* find text dimensions */ + + STRUT_SET(self->item_margin, 0, 0, 0, 0); + + if (self->entries) { + gint l, t, r, b; + + e = self->entries->data; + ob_rr_theme->a_menu_text_normal->texture[0].data.text.string = ""; + tw = RrMinWidth(ob_rr_theme->a_menu_text_normal); + tw += 2*PADDING; + + th = ITEM_HEIGHT; + + RrMargins(ob_rr_theme->a_menu_normal, &l, &t, &r, &b); + STRUT_SET(self->item_margin, + MAX(self->item_margin.left, l), + MAX(self->item_margin.top, t), + MAX(self->item_margin.right, r), + MAX(self->item_margin.bottom, b)); + RrMargins(ob_rr_theme->a_menu_selected, &l, &t, &r, &b); + STRUT_SET(self->item_margin, + MAX(self->item_margin.left, l), + MAX(self->item_margin.top, t), + MAX(self->item_margin.right, r), + MAX(self->item_margin.bottom, b)); + RrMargins(ob_rr_theme->a_menu_disabled, &l, &t, &r, &b); + STRUT_SET(self->item_margin, + MAX(self->item_margin.left, l), + MAX(self->item_margin.top, t), + MAX(self->item_margin.right, r), + MAX(self->item_margin.bottom, b)); + RrMargins(ob_rr_theme->a_menu_disabled_selected, &l, &t, &r, &b); + STRUT_SET(self->item_margin, + MAX(self->item_margin.left, l), + MAX(self->item_margin.top, t), + MAX(self->item_margin.right, r), + MAX(self->item_margin.bottom, b)); + } + + /* render the entries */ + + for (it = self->entries; it; it = g_list_next(it)) { + RrAppearance *text_a; + e = it->data; + + /* if the first entry is a labeled separator, then make its border + overlap with the menu's outside border */ + if (it == self->entries && + e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR && + e->entry->data.separator.label) + { + h -= ob_rr_theme->mbwidth; + } + + if (e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR && + e->entry->data.separator.label) + { + e->border = ob_rr_theme->mbwidth; + } + + RECT_SET_POINT(e->area, 0, h+e->border); + XMoveWindow(obt_display, e->window, + e->area.x-e->border, e->area.y-e->border); + XSetWindowBorderWidth(obt_display, e->window, e->border); + XSetWindowBorder(obt_display, e->window, + RrColorPixel(ob_rr_theme->menu_border_color)); + + text_a = (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + !e->entry->data.normal.enabled ? + /* disabled */ + (e == self->selected ? + ob_rr_theme->a_menu_text_disabled_selected : + ob_rr_theme->a_menu_text_disabled) : + /* enabled */ + (e == self->selected ? + ob_rr_theme->a_menu_text_selected : + ob_rr_theme->a_menu_text_normal)); + switch (e->entry->type) { + case OB_MENU_ENTRY_TYPE_NORMAL: + text_a->texture[0].data.text.string = e->entry->data.normal.label; + tw = RrMinWidth(text_a); + tw = MIN(tw, MAX_MENU_WIDTH); + th = ob_rr_theme->menu_font_height; + + if (e->entry->data.normal.icon || + e->entry->data.normal.mask) + has_icon = TRUE; + break; + case OB_MENU_ENTRY_TYPE_SUBMENU: + sub = e->entry->data.submenu.submenu; + text_a->texture[0].data.text.string = sub ? sub->title : ""; + tw = RrMinWidth(text_a); + tw = MIN(tw, MAX_MENU_WIDTH); + th = ob_rr_theme->menu_font_height; + + if (e->entry->data.normal.icon || + e->entry->data.normal.mask) + has_icon = TRUE; + + tw += ITEM_HEIGHT - PADDING; + break; + case OB_MENU_ENTRY_TYPE_SEPARATOR: + if (e->entry->data.separator.label != NULL) { + ob_rr_theme->a_menu_text_title->texture[0].data.text.string = + e->entry->data.separator.label; + tw = RrMinWidth(ob_rr_theme->a_menu_text_title) + + 2*ob_rr_theme->paddingx; + tw = MIN(tw, MAX_MENU_WIDTH); + th = ob_rr_theme->menu_title_height + + (ob_rr_theme->mbwidth - PADDING) *2; + } else { + tw = 0; + th = ob_rr_theme->menu_sep_width + + 2*ob_rr_theme->menu_sep_paddingy - 2*PADDING; + } + break; + default: + g_assert_not_reached(); + } + tw += 2*PADDING; + th += 2*PADDING; + w = MAX(w, tw); + h += th; + } + + /* if the last entry is a labeled separator, then make its border + overlap with the menu's outside border */ + it = g_list_last(self->entries); + e = it ? it->data : NULL; + if (e && e->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR && + e->entry->data.separator.label) + { + h -= ob_rr_theme->mbwidth; + } + + self->text_x = PADDING; + self->text_w = w; + + if (self->entries) { + if (has_icon) { + w += ITEM_HEIGHT + PADDING; + self->text_x += ITEM_HEIGHT + PADDING; + } + } + + if (!w) w = 10; + if (!h) h = 3; + + XResizeWindow(obt_display, self->window, w, h); + + self->inner_w = w; + + RrPaint(self->a_items, self->window, w, h); + + for (it = self->entries; it; it = g_list_next(it)) + menu_entry_frame_render(it->data); + + w += ob_rr_theme->mbwidth * 2; + h += ob_rr_theme->mbwidth * 2; + + RECT_SET_SIZE(self->area, w, h); + + XFlush(obt_display); +} + +static void menu_frame_update(ObMenuFrame *self) +{ + GList *mit, *fit; + const Rect *a; + gint h; + + menu_pipe_execute(self->menu); + menu_find_submenus(self->menu); + + self->selected = NULL; + + /* start at show_from */ + mit = g_list_nth(self->menu->entries, self->show_from); + + /* go through the menu's and frame's entries and connect the frame entries + to the menu entries */ + for (fit = self->entries; mit && fit; + mit = g_list_next(mit), fit = g_list_next(fit)) + { + ObMenuEntryFrame *f = fit->data; + f->entry = mit->data; + } + + /* if there are more menu entries than in the frame, add them */ + while (mit) { + ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self); + self->entries = g_list_append(self->entries, e); + mit = g_list_next(mit); + } + + /* if there are more frame entries than menu entries then get rid of + them */ + while (fit) { + GList *n = g_list_next(fit); + menu_entry_frame_free(fit->data); + self->entries = g_list_delete_link(self->entries, fit); + fit = n; + } + + /* * make the menu fit on the screen */ + + /* calculate the height of the menu */ + h = 0; + for (fit = self->entries; fit; fit = g_list_next(fit)) + h += menu_entry_frame_get_height(fit->data, + fit == self->entries, + g_list_next(fit) == NULL); + /* add the border at the top and bottom */ + h += ob_rr_theme->mbwidth * 2; + + a = screen_physical_area_monitor(self->monitor); + + if (h > a->height) { + GList *flast, *tmp; + gboolean last_entry = TRUE; + + /* take the height of our More... entry into account */ + h += menu_entry_frame_get_height(NULL, FALSE, TRUE); + + /* start at the end of the entries */ + flast = g_list_last(self->entries); + + /* pull out all the entries from the frame that don't + fit on the screen, leaving at least 1 though */ + while (h > a->height && g_list_previous(flast) != NULL) { + /* update the height, without this entry */ + h -= menu_entry_frame_get_height(flast->data, FALSE, last_entry); + + /* destroy the entry we're not displaying */ + tmp = flast; + flast = g_list_previous(flast); + menu_entry_frame_free(tmp->data); + self->entries = g_list_delete_link(self->entries, tmp); + + /* only the first one that we see is the last entry in the menu */ + last_entry = FALSE; + }; + + { + ObMenuEntry *more_entry; + ObMenuEntryFrame *more_frame; + /* make the More... menu entry frame which will display in this + frame. + if self->menu->more_menu is NULL that means that this is already + More... menu, so just use ourself. + */ + more_entry = menu_get_more((self->menu->more_menu ? + self->menu->more_menu : + self->menu), + /* continue where we left off */ + self->show_from + + g_list_length(self->entries)); + more_frame = menu_entry_frame_new(more_entry, self); + /* make it get deleted when the menu frame goes away */ + menu_entry_unref(more_entry); + + /* add our More... entry to the frame */ + self->entries = g_list_append(self->entries, more_frame); + } + } + + menu_frame_render(self); +} + +static gboolean menu_frame_is_visible(ObMenuFrame *self) +{ + return !!(g_list_find(menu_frame_visible, self)); +} + +static gboolean menu_frame_show(ObMenuFrame *self) +{ + GList *it; + + /* determine if the underlying menu is already visible */ + for (it = menu_frame_visible; it; it = g_list_next(it)) { + ObMenuFrame *f = it->data; + if (f->menu == self->menu) + break; + } + if (!it) { + if (self->menu->update_func) + if (!self->menu->update_func(self, self->menu->data)) + return FALSE; + } + + if (menu_frame_visible == NULL) { + /* no menus shown yet */ + + /* grab the pointer in such a way as to pass through "owner events" + so that we can get enter/leave notifies in the menu. */ + if (!grab_pointer(TRUE, FALSE, OB_CURSOR_POINTER)) + return FALSE; + if (!grab_keyboard()) { + ungrab_pointer(); + return FALSE; + } + } + + menu_frame_update(self); + + menu_frame_visible = g_list_prepend(menu_frame_visible, self); + + if (self->menu->show_func) + self->menu->show_func(self, self->menu->data); + + return TRUE; +} + +gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y, + gboolean mouse) +{ + gint px, py; + + if (menu_frame_is_visible(self)) + return TRUE; + if (!menu_frame_show(self)) + return FALSE; + + if (self->menu->place_func) + self->menu->place_func(self, &x, &y, mouse, self->menu->data); + else + menu_frame_place_topmenu(self, &x, &y); + + menu_frame_move(self, x, y); + + XMapWindow(obt_display, self->window); + + if (screen_pointer_pos(&px, &py)) { + ObMenuEntryFrame *e = menu_entry_frame_under(px, py); + if (e && e->frame == self) + e->ignore_enters++; + } + + return TRUE; +} + +/*! Stop hiding an open submenu. + @child The OnMenuFrame of the submenu to be hidden +*/ +static void remove_submenu_hide_timeout(ObMenuFrame *child) +{ + if (submenu_hide_timer) g_source_remove(submenu_hide_timer); + submenu_hide_timer = 0; +} + +gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent, + ObMenuEntryFrame *parent_entry) +{ + gint x, y, dx, dy; + gint px, py; + + if (menu_frame_is_visible(self)) + return TRUE; + + self->monitor = parent->monitor; + self->parent = parent; + self->parent_entry = parent_entry; + + /* set up parent's child to be us */ + if ((parent->child) != self) { + if (parent->child) + menu_frame_hide(parent->child); + parent->child = self; + parent->child_entry = parent_entry; + } + + if (!menu_frame_show(self)) + return FALSE; + + menu_frame_place_submenu(self, &x, &y); + menu_frame_move_on_screen(self, x, y, &dx, &dy); + + if (dx != 0) { + /*try the other side */ + self->direction_right = !self->direction_right; + menu_frame_place_submenu(self, &x, &y); + menu_frame_move_on_screen(self, x, y, &dx, &dy); + } + menu_frame_move(self, x + dx, y + dy); + + XMapWindow(obt_display, self->window); + + if (screen_pointer_pos(&px, &py)) { + ObMenuEntryFrame *e = menu_entry_frame_under(px, py); + if (e && e->frame == self) + e->ignore_enters++; + } + + return TRUE; +} + +static void menu_frame_hide(ObMenuFrame *self) +{ + ObMenu *const menu = self->menu; + GList *it = g_list_find(menu_frame_visible, self); + gulong ignore_start; + + if (!it) + return; + + if (menu->hide_func) + menu->hide_func(self, menu->data); + + if (self->child) + menu_frame_hide(self->child); + + if (self->parent) { + remove_submenu_hide_timeout(self); + + self->parent->child = NULL; + self->parent->child_entry = NULL; + } + self->parent = NULL; + self->parent_entry = NULL; + + menu_frame_visible = g_list_delete_link(menu_frame_visible, it); + + if (menu_frame_visible == NULL) { + /* last menu shown */ + ungrab_pointer(); + ungrab_keyboard(); + } + + ignore_start = event_start_ignore_all_enters(); + XUnmapWindow(obt_display, self->window); + event_end_ignore_all_enters(ignore_start); + + menu_frame_free(self); + + if (menu->cleanup_func) + menu->cleanup_func(menu, menu->data); +} + +void menu_frame_hide_all(void) +{ + GList *it; + + if (config_submenu_show_delay) { + /* remove any submenu open requests */ + if (submenu_show_timer) g_source_remove(submenu_show_timer); + submenu_show_timer = 0; + } + if ((it = g_list_last(menu_frame_visible))) + menu_frame_hide(it->data); +} + +ObMenuFrame* menu_frame_under(gint x, gint y) +{ + ObMenuFrame *ret = NULL; + GList *it; + + for (it = menu_frame_visible; it; it = g_list_next(it)) { + ObMenuFrame *f = it->data; + + if (RECT_CONTAINS(f->area, x, y)) { + ret = f; + break; + } + } + return ret; +} + +ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y) +{ + ObMenuFrame *frame; + ObMenuEntryFrame *ret = NULL; + GList *it; + + if ((frame = menu_frame_under(x, y))) { + x -= ob_rr_theme->mbwidth + frame->area.x; + y -= ob_rr_theme->mbwidth + frame->area.y; + + for (it = frame->entries; it; it = g_list_next(it)) { + ObMenuEntryFrame *e = it->data; + + if (RECT_CONTAINS(e->area, x, y)) { + ret = e; + break; + } + } + } + return ret; +} + +static gboolean submenu_show_timeout(gpointer data) +{ + g_assert(menu_frame_visible); + menu_entry_frame_show_submenu((ObMenuEntryFrame*)data); + return FALSE; +} + +static gboolean submenu_hide_timeout(gpointer data) +{ + g_assert(menu_frame_visible); + menu_frame_hide((ObMenuFrame*)data); + return FALSE; +} + +void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, + gboolean immediate) +{ + ObMenuEntryFrame *old = self->selected; + ObMenuFrame *oldchild = self->child; + ObMenuEntryFrame *oldchild_entry = self->child_entry; + + /* if the user selected a separator, ignore it and reselect what we had + selected before */ + if (entry && entry->entry->type == OB_MENU_ENTRY_TYPE_SEPARATOR) + entry = old; + + if (old == entry && + (!old || old->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)) + return; + + /* if the user left this menu but we have a submenu open, move the + selection back to that submenu */ + if (!entry && oldchild_entry) + entry = oldchild_entry; + + if (config_submenu_show_delay) { + /* remove any submenu open requests */ + if (submenu_show_timer) g_source_remove(submenu_show_timer); + submenu_show_timer = 0; + } + + self->selected = entry; + + if (old) + menu_entry_frame_render(old); + + if (oldchild_entry) { + /* There is an open submenu */ + if (oldchild_entry == self->selected) { + /* The open submenu has been reselected, so stop hiding the + submenu */ + remove_submenu_hide_timeout(oldchild); + } + else if (oldchild_entry == old) { + /* The open submenu was selected and is no longer, so hide the + submenu */ + if (immediate || config_submenu_hide_delay == 0) + menu_frame_hide(oldchild); + else if (config_submenu_hide_delay > 0) { + if (submenu_hide_timer) g_source_remove(submenu_hide_timer); + submenu_hide_timer = + g_timeout_add_full(G_PRIORITY_DEFAULT, + config_submenu_hide_delay, + submenu_hide_timeout, oldchild, NULL); + } + } + } + + if (self->selected) { + menu_entry_frame_render(self->selected); + + if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + /* only show if the submenu isn't already showing */ + if (oldchild_entry != self->selected) { + if (immediate || config_submenu_hide_delay == 0) + menu_entry_frame_show_submenu(self->selected); + else if (config_submenu_hide_delay > 0) { + if (submenu_show_timer) + g_source_remove(submenu_show_timer); + submenu_show_timer = + g_timeout_add_full(G_PRIORITY_DEFAULT, + config_submenu_show_delay, + submenu_show_timeout, + self->selected, NULL); + } + } + /* hide the grandchildren of this menu. and move the cursor to + the current menu */ + else if (immediate && self->child && self->child->child) { + menu_frame_hide(self->child->child); + menu_frame_select(self->child, NULL, TRUE); + } + } + } +} + +void menu_entry_frame_show_submenu(ObMenuEntryFrame *self) +{ + ObMenuFrame *f; + + if (!self->entry->data.submenu.submenu) return; + + f = menu_frame_new(self->entry->data.submenu.submenu, + self->entry->data.submenu.show_from, + self->frame->client); + /* pass our direction on to our child */ + f->direction_right = self->frame->direction_right; + + menu_frame_show_submenu(f, self->frame, self); +} + +void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state) +{ + if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL && + self->entry->data.normal.enabled) + { + /* grab all this shizzle, cuz when the menu gets hidden, 'self' + gets freed */ + ObMenuEntry *entry = self->entry; + ObMenuExecuteFunc func = self->frame->menu->execute_func; + gpointer data = self->frame->menu->data; + GSList *acts = self->entry->data.normal.actions; + ObClient *client = self->frame->client; + ObMenuFrame *frame = self->frame; + guint mods = obt_keyboard_only_modmasks(state); + + /* release grabs before executing the shit */ + if (!(mods & ControlMask)) { + event_cancel_all_key_grabs(); + frame = NULL; + } + + if (func) + func(entry, frame, client, state, data); + else + actions_run_acts(acts, OB_USER_ACTION_MENU_SELECTION, + state, -1, -1, 0, OB_FRAME_CONTEXT_NONE, client); + } +} + +void menu_frame_select_previous(ObMenuFrame *self) +{ + GList *it = NULL, *start; + + if (self->entries) { + start = it = g_list_find(self->entries, self->selected); + while (TRUE) { + ObMenuEntryFrame *e; + + it = it ? g_list_previous(it) : g_list_last(self->entries); + if (it == start) + break; + + if (it) { + e = it->data; + if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) + break; + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) + break; + } + } + } + menu_frame_select(self, it ? it->data : NULL, FALSE); +} + +void menu_frame_select_next(ObMenuFrame *self) +{ + GList *it = NULL, *start; + + if (self->entries) { + start = it = g_list_find(self->entries, self->selected); + while (TRUE) { + ObMenuEntryFrame *e; + + it = it ? g_list_next(it) : self->entries; + if (it == start) + break; + + if (it) { + e = it->data; + if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) + break; + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) + break; + } + } + } + menu_frame_select(self, it ? it->data : NULL, FALSE); +} + +void menu_frame_select_first(ObMenuFrame *self) +{ + GList *it = NULL; + + if (self->entries) { + for (it = self->entries; it; it = g_list_next(it)) { + ObMenuEntryFrame *e = it->data; + if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) + break; + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) + break; + } + } + menu_frame_select(self, it ? it->data : NULL, FALSE); +} + +void menu_frame_select_last(ObMenuFrame *self) +{ + GList *it = NULL; + + if (self->entries) { + for (it = g_list_last(self->entries); it; it = g_list_previous(it)) { + ObMenuEntryFrame *e = it->data; + if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) + break; + if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) + break; + } + } + menu_frame_select(self, it ? it->data : NULL, FALSE); +} diff --git a/openbox/menuframe.h b/openbox/menuframe.h new file mode 100644 index 0000000..44c0256 --- /dev/null +++ b/openbox/menuframe.h @@ -0,0 +1,147 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + menuframe.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__menuframe_h +#define ob__menuframe_h + +#include "geom.h" +#include "window.h" +#include "obrender/render.h" +#include "obt/keyboard.h" + +#include <glib.h> + +struct _ObClient; +struct _ObMenu; +struct _ObMenuEntry; + +typedef struct _ObMenuFrame ObMenuFrame; +typedef struct _ObMenuEntryFrame ObMenuEntryFrame; + +extern GList *menu_frame_visible; + +struct _ObMenuFrame +{ + /* stuff to be an ObWindow */ + ObWindow obwin; + Window window; + + struct _ObMenu *menu; + + /* The client that the visual instance of the menu is associated with for + its actions */ + struct _ObClient *client; + + ObMenuFrame *parent; + ObMenuEntryFrame *parent_entry; + ObMenuFrame *child; + ObMenuEntryFrame *child_entry; + + GList *entries; + ObMenuEntryFrame *selected; + + /* show entries from the menu starting at this index */ + guint show_from; + + /* If the submenus are being drawn to the right or the left */ + gboolean direction_right; + + /* On-screen area (including borders!) */ + Rect area; + Strut item_margin; + gint inner_w; /* inside the borders */ + gint item_h; /* height of all normal items */ + gint text_x; /* offset at which the text appears in the items */ + gint text_w; /* width of the text area in the items */ + gint text_h; /* height of the items */ + + gint monitor; /* monitor on which to show the menu in xinerama */ + + /* We make a copy of this for each menu, so that we don't have to re-render + the background of the entire menu each time we render an item inside it. + */ + RrAppearance *a_items; + + gboolean got_press; /* don't allow a KeyRelease event to run things in the + menu until it has seen a KeyPress. this is to + avoid having the keybinding used to show the menu + end up running something inside the menu */ + guint press_keycode; /* the KeyCode that was used in the last KeyPress */ + gboolean press_doexec; /* if the upcoming KeyRelease should be used to + execute the menu item that was selected by the + KeyPress */ +}; + +struct _ObMenuEntryFrame +{ + struct _ObMenuEntry *entry; + ObMenuFrame *frame; + + guint ignore_enters; + + Rect area; + gint border; + + Window window; + Window icon; + Window text; + Window bullet; +}; + +extern GHashTable *menu_frame_map; + +void menu_frame_startup(gboolean reconfig); +void menu_frame_shutdown(gboolean reconfig); + +ObMenuFrame* menu_frame_new(struct _ObMenu *menu, + guint show_from, + struct _ObClient *client); +void menu_frame_free(ObMenuFrame *self); + +ObtIC* menu_frame_ic(ObMenuFrame *self); + +void menu_frame_move(ObMenuFrame *self, gint x, gint y); +void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y, + gint *dx, gint *dy); + +gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y, + gint button); +gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent, + ObMenuEntryFrame *parent_entry); + +void menu_frame_hide_all(void); +void menu_frame_hide_all_client(struct _ObClient *client); + +void menu_frame_render(ObMenuFrame *self); + +void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, + gboolean immediate); +void menu_frame_select_previous(ObMenuFrame *self); +void menu_frame_select_next(ObMenuFrame *self); +void menu_frame_select_first(ObMenuFrame *self); +void menu_frame_select_last(ObMenuFrame *self); + +ObMenuFrame* menu_frame_under(gint x, gint y); +ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y); + +void menu_entry_frame_show_submenu(ObMenuEntryFrame *self); + +void menu_entry_frame_execute(ObMenuEntryFrame *self, guint state); + +#endif diff --git a/openbox/misc.h b/openbox/misc.h new file mode 100644 index 0000000..750dddd --- /dev/null +++ b/openbox/misc.h @@ -0,0 +1,101 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + misc.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ob__misc_h +#define __ob__misc_h + +/*! The alpha value to use for icons of iconified windows in various places + like the focus cycle popup and client list menus. + Give iconic windows 7/16 alpha. A little under 50%. + */ +#define OB_ICONIC_ALPHA 0x70 + +typedef enum +{ + OB_CURSOR_NONE, + OB_CURSOR_POINTER, + OB_CURSOR_BUSYPOINTER, + OB_CURSOR_BUSY, + OB_CURSOR_MOVE, + OB_CURSOR_NORTH, + OB_CURSOR_NORTHEAST, + OB_CURSOR_EAST, + OB_CURSOR_SOUTHEAST, + OB_CURSOR_SOUTH, + OB_CURSOR_SOUTHWEST, + OB_CURSOR_WEST, + OB_CURSOR_NORTHWEST, + OB_NUM_CURSORS +} ObCursor; + +typedef enum +{ + OB_STATE_STARTING, + OB_STATE_RUNNING, + OB_STATE_EXITING, + OB_STATE_RECONFIGURING +} ObState; + +typedef enum +{ + OB_DIRECTION_NORTH, + OB_DIRECTION_NORTHEAST, + OB_DIRECTION_EAST, + OB_DIRECTION_SOUTHEAST, + OB_DIRECTION_SOUTH, + OB_DIRECTION_SOUTHWEST, + OB_DIRECTION_WEST, + OB_DIRECTION_NORTHWEST +} ObDirection; + +typedef enum +{ + OB_ORIENTATION_HORZ, + OB_ORIENTATION_VERT +} ObOrientation; + +typedef enum +{ + OB_CORNER_TOPLEFT, + OB_CORNER_TOPRIGHT, + OB_CORNER_BOTTOMLEFT, + OB_CORNER_BOTTOMRIGHT +} ObCorner; + +typedef enum { + OB_MOUSE_ACTION_PRESS, + OB_MOUSE_ACTION_RELEASE, + OB_MOUSE_ACTION_CLICK, + OB_MOUSE_ACTION_DOUBLE_CLICK, + OB_MOUSE_ACTION_MOTION, + OB_NUM_MOUSE_ACTIONS +} ObMouseAction; + +typedef enum { + OB_USER_ACTION_NONE, /* being fired from inside another action and such */ + OB_USER_ACTION_KEYBOARD_KEY, + OB_USER_ACTION_MOUSE_PRESS, + OB_USER_ACTION_MOUSE_RELEASE, + OB_USER_ACTION_MOUSE_CLICK, + OB_USER_ACTION_MOUSE_DOUBLE_CLICK, + OB_USER_ACTION_MOUSE_MOTION, + OB_USER_ACTION_MENU_SELECTION, + OB_NUM_USER_ACTIONS +} ObUserAction; + +#endif diff --git a/openbox/mouse.c b/openbox/mouse.c new file mode 100644 index 0000000..2f0c8f5 --- /dev/null +++ b/openbox/mouse.c @@ -0,0 +1,413 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + mouse.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "openbox.h" +#include "config.h" +#include "actions.h" +#include "event.h" +#include "client.h" +#include "grab.h" +#include "frame.h" +#include "translate.h" +#include "mouse.h" +#include "gettext.h" +#include "obt/display.h" + +#include <glib.h> + +typedef struct { + guint state; + guint button; + GSList *actions[OB_NUM_MOUSE_ACTIONS]; /* lists of Action pointers */ +} ObMouseBinding; + +/* Array of GSList*s of ObMouseBinding*s. */ +static GSList *bound_contexts[OB_FRAME_NUM_CONTEXTS]; +/* TRUE when we have a grab on the pointer and need to replay the pointer event + to send it to other applications */ +static gboolean replay_pointer_needed; + +ObFrameContext mouse_button_frame_context(ObFrameContext context, + guint button, + guint state) +{ + GSList *it; + ObFrameContext x = context; + + for (it = bound_contexts[context]; it; it = g_slist_next(it)) { + ObMouseBinding *b = it->data; + + if (b->button == button && b->state == state) + return context; + } + + switch (context) { + case OB_FRAME_CONTEXT_NONE: + case OB_FRAME_CONTEXT_DESKTOP: + case OB_FRAME_CONTEXT_CLIENT: + case OB_FRAME_CONTEXT_TITLEBAR: + case OB_FRAME_CONTEXT_FRAME: + case OB_FRAME_CONTEXT_MOVE_RESIZE: + case OB_FRAME_CONTEXT_LEFT: + case OB_FRAME_CONTEXT_RIGHT: + case OB_FRAME_CONTEXT_DOCK: + break; + case OB_FRAME_CONTEXT_ROOT: + x = OB_FRAME_CONTEXT_DESKTOP; + break; + case OB_FRAME_CONTEXT_BOTTOM: + case OB_FRAME_CONTEXT_BLCORNER: + case OB_FRAME_CONTEXT_BRCORNER: + x = OB_FRAME_CONTEXT_BOTTOM; + break; + case OB_FRAME_CONTEXT_TLCORNER: + case OB_FRAME_CONTEXT_TRCORNER: + case OB_FRAME_CONTEXT_TOP: + case OB_FRAME_CONTEXT_MAXIMIZE: + case OB_FRAME_CONTEXT_ALLDESKTOPS: + case OB_FRAME_CONTEXT_SHADE: + case OB_FRAME_CONTEXT_ICONIFY: + case OB_FRAME_CONTEXT_ICON: + case OB_FRAME_CONTEXT_CLOSE: + x = OB_FRAME_CONTEXT_TITLEBAR; + break; + case OB_FRAME_NUM_CONTEXTS: + g_assert_not_reached(); + } + + /* allow for multiple levels of fall-through */ + if (x != context) + return mouse_button_frame_context(x, button, state); + else + return x; +} + +void mouse_grab_for_client(ObClient *client, gboolean grab) +{ + gint i; + GSList *it; + + for (i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) + for (it = bound_contexts[i]; it; it = g_slist_next(it)) { + /* grab/ungrab the button */ + ObMouseBinding *b = it->data; + Window win; + gint mode; + guint mask; + + if (FRAME_CONTEXT(i, client)) { + win = client->frame->window; + mode = GrabModeAsync; + mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask; + } else if (CLIENT_CONTEXT(i, client)) { + win = client->window; + mode = GrabModeSync; /* this is handled in event */ + mask = ButtonPressMask; /* can't catch more than this with Sync + mode the release event is + manufactured in event() */ + } else continue; + + if (grab) + grab_button_full(b->button, b->state, win, mask, mode, + OB_CURSOR_NONE); + else + ungrab_button(b->button, b->state, win); + } +} + +static void grab_all_clients(gboolean grab) +{ + GList *it; + + for (it = client_list; it; it = g_list_next(it)) + mouse_grab_for_client(it->data, grab); +} + +void mouse_unbind_all(void) +{ + gint i; + GSList *it; + + for(i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) { + for (it = bound_contexts[i]; it; it = g_slist_next(it)) { + ObMouseBinding *b = it->data; + gint j; + + for (j = 0; j < OB_NUM_MOUSE_ACTIONS; ++j) { + GSList *jt; + + for (jt = b->actions[j]; jt; jt = g_slist_next(jt)) + actions_act_unref(jt->data); + g_slist_free(b->actions[j]); + } + g_slice_free(ObMouseBinding, b); + } + g_slist_free(bound_contexts[i]); + bound_contexts[i] = NULL; + } +} + +static ObUserAction mouse_action_to_user_action(ObMouseAction a) +{ + switch (a) { + case OB_MOUSE_ACTION_PRESS: return OB_USER_ACTION_MOUSE_PRESS; + case OB_MOUSE_ACTION_RELEASE: return OB_USER_ACTION_MOUSE_RELEASE; + case OB_MOUSE_ACTION_CLICK: return OB_USER_ACTION_MOUSE_CLICK; + case OB_MOUSE_ACTION_DOUBLE_CLICK: + return OB_USER_ACTION_MOUSE_DOUBLE_CLICK; + case OB_MOUSE_ACTION_MOTION: return OB_USER_ACTION_MOUSE_MOTION; + default: + g_assert_not_reached(); + } +} + +static gboolean fire_binding(ObMouseAction a, ObFrameContext context, + ObClient *c, guint state, + guint button, gint x, gint y) +{ + GSList *it; + ObMouseBinding *b; + + for (it = bound_contexts[context]; it; it = g_slist_next(it)) { + b = it->data; + if (b->state == state && b->button == button) + break; + } + /* if not bound, then nothing to do! */ + if (it == NULL) return FALSE; + + actions_run_acts(b->actions[a], mouse_action_to_user_action(a), + state, x, y, button, context, c); + return TRUE; +} + +void mouse_replay_pointer(void) +{ + if (replay_pointer_needed) { + /* replay the pointer event before any windows move */ + XAllowEvents(obt_display, ReplayPointer, event_time()); + replay_pointer_needed = FALSE; + } +} + +gboolean mouse_event(ObClient *client, XEvent *e) +{ + static Time ltime; + static guint button = 0, state = 0, lbutton = 0; + static Window lwindow = None; + static gint px, py, pwx = -1, pwy = -1, lx = -10, ly = -10; + gboolean used = FALSE; + + ObFrameContext context; + gboolean click = FALSE; + gboolean dclick = FALSE; + + switch (e->type) { + case ButtonPress: + context = frame_context(client, e->xbutton.window, + e->xbutton.x, e->xbutton.y); + context = mouse_button_frame_context(context, e->xbutton.button, + e->xbutton.state); + + px = e->xbutton.x_root; + py = e->xbutton.y_root; + if (!button) pwx = e->xbutton.x; + if (!button) pwy = e->xbutton.y; + button = e->xbutton.button; + state = e->xbutton.state; + + /* if the binding was in a client context, then we need to call + XAllowEvents with ReplayPointer at some point, to send the event + through to the client. when this happens though depends. if + windows are going to be moved on screen, then the click will end + up going somewhere wrong, set that we need it, and if nothing + else causes the replay pointer to be run, then we will do it + after all the actions are finished. + + (We do it after all the actions because FocusIn interrupts + dragging for kdesktop, so if we send the button event now, and + then they get a focus event after, it breaks. Instead, wait to send + the button press until after the actions when possible.) + */ + if (CLIENT_CONTEXT(context, client)) + replay_pointer_needed = TRUE; + + used = fire_binding(OB_MOUSE_ACTION_PRESS, context, + client, e->xbutton.state, + e->xbutton.button, + e->xbutton.x_root, e->xbutton.y_root) || used; + + /* if the bindings grab the pointer, there won't be a ButtonRelease + event for us */ + if (grab_on_pointer()) + button = 0; + + /* replay the pointer event if it hasn't been replayed yet (i.e. no + windows were moved) */ + mouse_replay_pointer(); + + /* in the client context, we won't get a button release because of the + way it is grabbed, so just fake one */ + if (!CLIENT_CONTEXT(context, client)) + break; + + case ButtonRelease: + /* use where the press occured in the window */ + context = frame_context(client, e->xbutton.window, pwx, pwy); + context = mouse_button_frame_context(context, e->xbutton.button, + e->xbutton.state); + + if (e->xbutton.button == button) + pwx = pwy = -1; + + if (e->xbutton.button == button) { + /* clicks are only valid if its released over the window */ + gint junk1, junk2; + Window wjunk; + guint ujunk, b, w, h; + /* this can cause errors to occur when the window closes */ + obt_display_ignore_errors(TRUE); + junk1 = XGetGeometry(obt_display, e->xbutton.window, + &wjunk, &junk1, &junk2, &w, &h, &b, &ujunk); + obt_display_ignore_errors(FALSE); + if (junk1) { + if (e->xbutton.x >= (signed)-b && + e->xbutton.y >= (signed)-b && + e->xbutton.x < (signed)(w+b) && + e->xbutton.y < (signed)(h+b)) + { + click = TRUE; + /* double clicks happen if there were 2 in a row! */ + if (lbutton == button && + lwindow == e->xbutton.window && + e->xbutton.time - config_mouse_dclicktime <= + ltime && + ABS(e->xbutton.x - lx) < 8 && + ABS(e->xbutton.y - ly) < 8) + { + dclick = TRUE; + lbutton = 0; + } else { + lbutton = button; + lwindow = e->xbutton.window; + lx = e->xbutton.x; + ly = e->xbutton.y; + } + } else { + lbutton = 0; + lwindow = None; + } + } + + button = 0; + state = 0; + ltime = e->xbutton.time; + } + used = fire_binding(OB_MOUSE_ACTION_RELEASE, context, + client, e->xbutton.state, + e->xbutton.button, + e->xbutton.x_root, + e->xbutton.y_root) || used; + if (click) + used = fire_binding(OB_MOUSE_ACTION_CLICK, context, + client, e->xbutton.state, + e->xbutton.button, + e->xbutton.x_root, + e->xbutton.y_root) || used; + if (dclick) + used = fire_binding(OB_MOUSE_ACTION_DOUBLE_CLICK, context, + client, e->xbutton.state, + e->xbutton.button, + e->xbutton.x_root, + e->xbutton.y_root) || used; + break; + + case MotionNotify: + if (button) { + context = frame_context(client, e->xmotion.window, pwx, pwy); + context = mouse_button_frame_context(context, button, state); + + if (ABS(e->xmotion.x_root - px) >= config_mouse_threshold || + ABS(e->xmotion.y_root - py) >= config_mouse_threshold) { + + /* You can't drag on buttons */ + if (context == OB_FRAME_CONTEXT_MAXIMIZE || + context == OB_FRAME_CONTEXT_ALLDESKTOPS || + context == OB_FRAME_CONTEXT_SHADE || + context == OB_FRAME_CONTEXT_ICONIFY || + context == OB_FRAME_CONTEXT_ICON || + context == OB_FRAME_CONTEXT_CLOSE) + break; + + used = fire_binding(OB_MOUSE_ACTION_MOTION, context, + client, state, button, px, py); + button = 0; + state = 0; + } + } + break; + + default: + g_assert_not_reached(); + } + return used; +} + +gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context, + ObMouseAction mact, ObActionsAct *action) +{ + guint state, button; + ObMouseBinding *b; + GSList *it; + + g_assert(context != OB_FRAME_CONTEXT_NONE); + + if (!translate_button(buttonstr, &state, &button)) { + g_message(_("Invalid button \"%s\" in mouse binding"), buttonstr); + return FALSE; + } + + for (it = bound_contexts[context]; it; it = g_slist_next(it)) { + b = it->data; + if (b->state == state && b->button == button) { + b->actions[mact] = g_slist_append(b->actions[mact], action); + return TRUE; + } + } + + /* add the binding */ + b = g_slice_new0(ObMouseBinding); + b->state = state; + b->button = button; + b->actions[mact] = g_slist_append(NULL, action); + bound_contexts[context] = g_slist_append(bound_contexts[context], b); + + return TRUE; +} + +void mouse_startup(gboolean reconfig) +{ + grab_all_clients(TRUE); +} + +void mouse_shutdown(gboolean reconfig) +{ + grab_all_clients(FALSE); + mouse_unbind_all(); +} diff --git a/openbox/mouse.h b/openbox/mouse.h new file mode 100644 index 0000000..de4c0ec --- /dev/null +++ b/openbox/mouse.h @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + mouse.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__mouse_h +#define ob__mouse_h + +#include "frame.h" +#include "misc.h" + +#include <X11/Xlib.h> + +struct _ObActionsAct; + +void mouse_startup(gboolean reconfig); +void mouse_shutdown(gboolean reconfig); + +gboolean mouse_bind(const gchar *buttonstr, ObFrameContext context, + ObMouseAction mact, struct _ObActionsAct *action); +void mouse_unbind_all(void); + +gboolean mouse_event(struct _ObClient *client, XEvent *e); + +void mouse_grab_for_client(struct _ObClient *client, gboolean grab); + +ObFrameContext mouse_button_frame_context(ObFrameContext context, + guint button, guint state); + +/*! If a replay pointer is needed, then do it. Call this when windows are + going to be moving/appearing/disappearing, so that you know the mouse click + will go to the right window */ +void mouse_replay_pointer(void); + +#endif diff --git a/openbox/moveresize.c b/openbox/moveresize.c new file mode 100644 index 0000000..3a98db3 --- /dev/null +++ b/openbox/moveresize.c @@ -0,0 +1,1092 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + moveresize.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "grab.h" +#include "framerender.h" +#include "screen.h" +#include "client.h" +#include "focus.h" +#include "frame.h" +#include "openbox.h" +#include "resist.h" +#include "popup.h" +#include "moveresize.h" +#include "config.h" +#include "event.h" +#include "debug.h" +#include "obrender/render.h" +#include "obrender/theme.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/prop.h" +#include "obt/keyboard.h" + +#include <X11/Xlib.h> +#include <glib.h> + +/* how far windows move and resize with the keyboard arrows */ +#define KEY_DIST 8 +#define SYNC_TIMEOUTS 4 + +gboolean moveresize_in_progress = FALSE; +ObClient *moveresize_client = NULL; +#ifdef SYNC +XSyncAlarm moveresize_alarm = None; +#endif + +static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */ + +/* starting geometry for the window being moved/resized, so it can be + restored */ +static gint start_x, start_y, start_cx, start_cy, start_cw, start_ch; +static gboolean was_max_horz, was_max_vert; +static Rect pre_max_area; +static gint cur_x, cur_y, cur_w, cur_h; +static guint button; +static guint32 corner; +static ObDirection edge_warp_dir = -1; +static gboolean edge_warp_odd = FALSE; +static guint edge_warp_timer = 0; +static ObDirection key_resize_edge = -1; +static guint waiting_for_sync; +#ifdef SYNC +static guint sync_timer = 0; +#endif + +static ObPopup *popup = NULL; + +static void do_move(gboolean keyboard, gint keydist); +static void do_resize(void); +static void do_edge_warp(gint x, gint y); +static void cancel_edge_warp(); +#ifdef SYNC +static gboolean sync_timeout_func(gpointer data); +#endif + +static void client_dest(ObClient *client, gpointer data) +{ + if (moveresize_client == client) + moveresize_end(TRUE); +} + +void moveresize_startup(gboolean reconfig) +{ + popup = popup_new(); + popup_set_text_align(popup, RR_JUSTIFY_CENTER); + + if (!reconfig) + client_add_destroy_notify(client_dest, NULL); +} + +void moveresize_shutdown(gboolean reconfig) +{ + if (!reconfig) { + if (moveresize_in_progress) + moveresize_end(FALSE); + client_remove_destroy_notify(client_dest); + } + + popup_free(popup); + popup = NULL; +} + +static void popup_coords(ObClient *c, const gchar *format, gint a, gint b) +{ + gchar *text; + + text = g_strdup_printf(format, a, b); + if (config_resize_popup_pos == OB_RESIZE_POS_TOP) + popup_position(popup, SouthGravity, + c->frame->area.x + + c->frame->area.width/2, + c->frame->area.y - ob_rr_theme->fbwidth); + else if (config_resize_popup_pos == OB_RESIZE_POS_CENTER) + popup_position(popup, CenterGravity, + c->frame->area.x + c->frame->area.width / 2, + c->frame->area.y + c->frame->area.height / 2); + else /* Fixed */ { + const Rect *area = screen_physical_area_active(); + gint gravity, x, y; + + x = config_resize_popup_fixed.x.pos; + if (config_resize_popup_fixed.x.center) + x = area->x + area->width/2; + else if (config_resize_popup_fixed.x.opposite) + x = RECT_RIGHT(*area) - x; + else + x = area->x + x; + + y = config_resize_popup_fixed.y.pos; + if (config_resize_popup_fixed.y.center) + y = area->y + area->height/2; + else if (config_resize_popup_fixed.y.opposite) + y = RECT_RIGHT(*area) - y; + else + y = area->y + y; + + if (config_resize_popup_fixed.x.center) { + if (config_resize_popup_fixed.y.center) + gravity = CenterGravity; + else if (config_resize_popup_fixed.y.opposite) + gravity = SouthGravity; + else + gravity = NorthGravity; + } + else if (config_resize_popup_fixed.x.opposite) { + if (config_resize_popup_fixed.y.center) + gravity = EastGravity; + else if (config_resize_popup_fixed.y.opposite) + gravity = SouthEastGravity; + else + gravity = NorthEastGravity; + } + else { + if (config_resize_popup_fixed.y.center) + gravity = WestGravity; + else if (config_resize_popup_fixed.y.opposite) + gravity = SouthWestGravity; + else + gravity = NorthWestGravity; + } + + popup_position(popup, gravity, x, y); + } + popup_show(popup, text); + g_free(text); +} + +void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr) +{ + ObCursor cur; + gboolean mv = (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) || + cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD)); + gint up = 1; + gint left = 1; + + if (moveresize_in_progress || !c->frame->visible || + !(mv ? + (c->functions & OB_CLIENT_FUNC_MOVE) : + (c->functions & OB_CLIENT_FUNC_RESIZE))) + return; + + if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) { + cur = OB_CURSOR_NORTHWEST; + up = left = -1; + } + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) { + cur = OB_CURSOR_NORTH; + up = -1; + } + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) { + cur = OB_CURSOR_NORTHEAST; + up = -1; + } + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) + cur = OB_CURSOR_EAST; + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) + cur = OB_CURSOR_SOUTHEAST; + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM)) + cur = OB_CURSOR_SOUTH; + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) { + cur = OB_CURSOR_SOUTHWEST; + left = -1; + } + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) { + cur = OB_CURSOR_WEST; + left = -1; + } + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) + cur = OB_CURSOR_SOUTHEAST; + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE)) + cur = OB_CURSOR_MOVE; + else if (cnr == OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD)) + cur = OB_CURSOR_MOVE; + else + g_assert_not_reached(); + + /* keep the pointer bounded to the screen for move/resize */ + if (!grab_pointer(FALSE, TRUE, cur)) + return; + if (!grab_keyboard()) { + ungrab_pointer(); + return; + } + + frame_end_iconify_animation(c->frame); + + moving = mv; + moveresize_client = c; + start_cx = c->area.x; + start_cy = c->area.y; + start_cw = c->area.width; + start_ch = c->area.height; + /* these adjustments for the size_inc make resizing a terminal more + friendly. you essentially start the resize in the middle of the + increment instead of at 0, so you have to move half an increment + either way instead of a full increment one and 1 px the other. */ + start_x = x - (mv ? 0 : left * c->size_inc.width / 2); + start_y = y - (mv ? 0 : up * c->size_inc.height / 2); + corner = cnr; + button = b; + key_resize_edge = -1; + + /* default to not putting max back on cancel */ + was_max_horz = was_max_vert = FALSE; + + /* + have to change start_cx and start_cy if going to do this.. + if (corner == prop_atoms.net_wm_moveresize_move_keyboard || + corner == prop_atoms.net_wm_moveresize_size_keyboard) + XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0, + c->area.width / 2, c->area.height / 2); + */ + + cur_x = start_cx; + cur_y = start_cy; + cur_w = start_cw; + cur_h = start_ch; + + moveresize_in_progress = TRUE; + waiting_for_sync = 0; + +#ifdef SYNC + if (config_resize_redraw && !moving && obt_display_extension_sync && + moveresize_client->sync_request && moveresize_client->sync_counter && + !moveresize_client->not_responding) + { + /* Initialize values for the resize syncing, and create an alarm for + the client's xsync counter */ + + XSyncValue val; + XSyncAlarmAttributes aa; + + /* set the counter to an initial value */ + XSyncIntToValue(&val, 0); + XSyncSetCounter(obt_display, moveresize_client->sync_counter, val); + + /* this will be incremented when we tell the client what we're + looking for */ + moveresize_client->sync_counter_value = 0; + + /* the next sequence we're waiting for with the alarm */ + XSyncIntToValue(&val, 1); + + /* set an alarm on the counter */ + aa.trigger.counter = moveresize_client->sync_counter; + aa.trigger.wait_value = val; + aa.trigger.value_type = XSyncAbsolute; + aa.trigger.test_type = XSyncPositiveTransition; + aa.events = True; + XSyncIntToValue(&aa.delta, 1); + moveresize_alarm = XSyncCreateAlarm(obt_display, + XSyncCACounter | + XSyncCAValue | + XSyncCAValueType | + XSyncCATestType | + XSyncCADelta | + XSyncCAEvents, + &aa); + } +#endif +} + +void moveresize_end(gboolean cancel) +{ + ungrab_keyboard(); + ungrab_pointer(); + + popup_hide(popup); + + if (!moving) { +#ifdef SYNC + /* turn off the alarm */ + if (moveresize_alarm != None) { + XSyncDestroyAlarm(obt_display, moveresize_alarm); + moveresize_alarm = None; + } + + if (sync_timer) g_source_remove(sync_timer); + sync_timer = 0; +#endif + } + + /* don't use client_move() here, use the same width/height as + we've been using during the move, otherwise we get different results + when moving maximized windows between monitors of different sizes ! + */ + client_configure(moveresize_client, + (cancel ? start_cx : cur_x), + (cancel ? start_cy : cur_y), + (cancel ? start_cw : cur_w), + (cancel ? start_ch : cur_h), + TRUE, TRUE, FALSE); + + /* restore the client's maximized state. do this after putting the window + back in its original spot to minimize visible flicker */ + if (cancel && (was_max_horz || was_max_vert)) { + const gboolean h = moveresize_client->max_horz; + const gboolean v = moveresize_client->max_vert; + + client_maximize(moveresize_client, TRUE, + was_max_horz && was_max_vert ? 0 : + (was_max_horz ? 1 : 2)); + + /* replace the premax values with the ones we had saved if + the client doesn't have any already set */ + if (was_max_horz && !h) { + moveresize_client->pre_max_area.x = pre_max_area.x; + moveresize_client->pre_max_area.width = pre_max_area.width; + } + if (was_max_vert && !v) { + moveresize_client->pre_max_area.y = pre_max_area.y; + moveresize_client->pre_max_area.height = pre_max_area.height; + } + } + + /* dont edge warp after its ended */ + cancel_edge_warp(); + + moveresize_in_progress = FALSE; + moveresize_client = NULL; +} + +static void do_move(gboolean keyboard, gint keydist) +{ + gint resist; + + if (keyboard) resist = keydist - 1; /* resist for one key press */ + else resist = config_resist_win; + resist_move_windows(moveresize_client, resist, &cur_x, &cur_y); + if (!keyboard) resist = config_resist_edge; + resist_move_monitors(moveresize_client, resist, &cur_x, &cur_y); + + client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h, + TRUE, FALSE, FALSE); + if (config_resize_popup_show == 2) /* == "Always" */ + popup_coords(moveresize_client, "%d x %d", + moveresize_client->frame->area.x, + moveresize_client->frame->area.y); +} + +static void do_resize(void) +{ + gint x, y, w, h, lw, lh; + + /* see if it is actually going to resize + USE cur_x AND cur_y HERE ! Otherwise the try_configure won't know + what struts to use !! + */ + x = cur_x; + y = cur_y; + w = cur_w; + h = cur_h; + client_try_configure(moveresize_client, &x, &y, &w, &h, + &lw, &lh, TRUE); + if (!(w == moveresize_client->area.width && + h == moveresize_client->area.height) && + /* if waiting_for_sync == 0, then we aren't waiting. + if it is > SYNC_TIMEOUTS, then we have timed out + that many times already, so forget about waiting more */ + (waiting_for_sync == 0 || waiting_for_sync > SYNC_TIMEOUTS)) + { +#ifdef SYNC + if (config_resize_redraw && obt_display_extension_sync && + /* don't send another sync when one is pending */ + waiting_for_sync == 0 && + moveresize_client->sync_request && + moveresize_client->sync_counter && + !moveresize_client->not_responding) + { + XEvent ce; + XSyncValue val; + + /* increment the value we're waiting for */ + ++moveresize_client->sync_counter_value; + XSyncIntToValue(&val, moveresize_client->sync_counter_value); + + /* tell the client what we're waiting for */ + ce.xclient.type = ClientMessage; + ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS); + ce.xclient.display = obt_display; + ce.xclient.window = moveresize_client->window; + ce.xclient.format = 32; + ce.xclient.data.l[0] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST); + ce.xclient.data.l[1] = event_time(); + ce.xclient.data.l[2] = XSyncValueLow32(val); + ce.xclient.data.l[3] = XSyncValueHigh32(val); + ce.xclient.data.l[4] = 0l; + XSendEvent(obt_display, moveresize_client->window, FALSE, + NoEventMask, &ce); + + waiting_for_sync = 1; + + if (sync_timer) g_source_remove(sync_timer); + sync_timer = g_timeout_add(2000, sync_timeout_func, NULL); + } +#endif + + /* force a ConfigureNotify, it is part of the spec for SYNC resizing + and MUST follow the sync counter notification */ + client_configure(moveresize_client, cur_x, cur_y, cur_w, cur_h, + TRUE, FALSE, TRUE); + } + + /* this would be better with a fixed width font ... XXX can do it better + if there are 2 text boxes */ + if (config_resize_popup_show == 2 || /* == "Always" */ + (config_resize_popup_show == 1 && /* == "Nonpixel" */ + moveresize_client->size_inc.width > 1 && + moveresize_client->size_inc.height > 1)) + popup_coords(moveresize_client, "%d x %d", lw, lh); +} + +#ifdef SYNC +static gboolean sync_timeout_func(gpointer data) +{ + ++waiting_for_sync; /* we timed out waiting for our sync... */ + do_resize(); /* ...so let any pending resizes through */ + + if (waiting_for_sync > SYNC_TIMEOUTS) { + sync_timer = 0; + return FALSE; /* don't repeat */ + } + else + return TRUE; /* keep waiting */ +} +#endif + +static void calc_resize(gboolean keyboard, gint keydist, gint *dw, gint *dh, + ObDirection dir) +{ + gint resist, x = 0, y = 0, lw, lh, ow, oh, nw, nh; + gint trydw, trydh; + + ow = cur_w; + oh = cur_h; + nw = ow + *dw; + nh = oh + *dh; + + if (!keyboard && + (moveresize_client->max_ratio || moveresize_client->min_ratio)) + { + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + /* resize the width based on the height */ + if (moveresize_client->min_ratio) { + if (nh * moveresize_client->min_ratio > nw) + nw = (gint)(nh * moveresize_client->min_ratio); + } + if (moveresize_client->max_ratio) { + if (nh * moveresize_client->max_ratio < nw) + nw = (gint)(nh * moveresize_client->max_ratio); + } + break; + default: + /* resize the height based on the width */ + if (moveresize_client->min_ratio) { + if (nh * moveresize_client->min_ratio > nw) + nh = (gint)(nw / moveresize_client->min_ratio); + } + if (moveresize_client->max_ratio) { + if (nh * moveresize_client->max_ratio < nw) + nh = (gint)(nw / moveresize_client->max_ratio); + } + break; + } + + /* see its actual size (apply aspect ratios) */ + client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, + TRUE); + trydw = nw - ow; + trydh = nh - oh; + } + + /* resist_size_* needs the frame size */ + nw += moveresize_client->frame->size.left + + moveresize_client->frame->size.right; + nh += moveresize_client->frame->size.top + + moveresize_client->frame->size.bottom; + + if (keyboard) resist = keydist - 1; /* resist for one key press */ + else resist = config_resist_win; + resist_size_windows(moveresize_client, resist, &nw, &nh, dir); + if (!keyboard) resist = config_resist_edge; + resist_size_monitors(moveresize_client, resist, &nw, &nh, dir); + + nw -= moveresize_client->frame->size.left + + moveresize_client->frame->size.right; + nh -= moveresize_client->frame->size.top + + moveresize_client->frame->size.bottom; + + *dw = nw - ow; + *dh = nh - oh; + + /* take aspect ratios into account for resistance */ + if (!keyboard && + (moveresize_client->max_ratio || moveresize_client->min_ratio)) + { + if (*dh != trydh) { /* got resisted */ + /* resize the width based on the height */ + if (moveresize_client->min_ratio) { + if (nh * moveresize_client->min_ratio > nw) + nw = (gint)(nh * moveresize_client->min_ratio); + } + if (moveresize_client->max_ratio) { + if (nh * moveresize_client->max_ratio < nw) + nw = (gint)(nh * moveresize_client->max_ratio); + } + } + if (*dw != trydw) { /* got resisted */ + /* resize the height based on the width */ + if (moveresize_client->min_ratio) { + if (nh * moveresize_client->min_ratio > nw) + nh = (gint)(nw / moveresize_client->min_ratio); + } + if (moveresize_client->max_ratio) { + if (nh * moveresize_client->max_ratio < nw) + nh = (gint)(nw / moveresize_client->max_ratio); + } + } + } + + /* make sure it's all valid */ + client_try_configure(moveresize_client, &x, &y, &nw, &nh, &lw, &lh, TRUE); + + *dw = nw - ow; + *dh = nh - oh; +} + +static void edge_warp_move_ptr(void) +{ + gint x, y; + const Rect* a; + + screen_pointer_pos(&x, &y); + a = screen_physical_area_all_monitors(); + + switch (edge_warp_dir) { + case OB_DIRECTION_NORTH: + y = a->height - 1; + break; + case OB_DIRECTION_EAST: + x = a->x; + break; + case OB_DIRECTION_SOUTH: + y = a->y; + break; + case OB_DIRECTION_WEST: + x = a->width - 1; + break; + default: + g_assert_not_reached(); + } + + XWarpPointer(obt_display, 0, obt_root(ob_screen), 0, 0, 0, 0, x, y); +} + +static gboolean edge_warp_delay_func(gpointer data) +{ + guint d; + + /* only fire every second time. so it's fast the first time, but slower + after that */ + if (edge_warp_odd) { + d = screen_find_desktop(screen_desktop, edge_warp_dir, TRUE, FALSE); + if (d != screen_desktop) { + if (config_mouse_screenedgewarp) edge_warp_move_ptr(); + screen_set_desktop(d, TRUE); + } + } + edge_warp_odd = !edge_warp_odd; + + return TRUE; /* do repeat ! */ +} + +static void do_edge_warp(gint x, gint y) +{ + guint i; + ObDirection dir; + + if (!config_mouse_screenedgetime) return; + + dir = -1; + + for (i = 0; i < screen_num_monitors; ++i) { + const Rect *a = screen_physical_area_monitor(i); + if (x == RECT_LEFT(*a)) dir = OB_DIRECTION_WEST; + if (x == RECT_RIGHT(*a)) dir = OB_DIRECTION_EAST; + if (y == RECT_TOP(*a)) dir = OB_DIRECTION_NORTH; + if (y == RECT_BOTTOM(*a)) dir = OB_DIRECTION_SOUTH; + + /* try check for xinerama boundaries */ + if ((x + 1 == RECT_LEFT(*a) || x - 1 == RECT_RIGHT(*a)) && + (dir == OB_DIRECTION_WEST || dir == OB_DIRECTION_EAST)) + { + dir = -1; + } + if ((y + 1 == RECT_TOP(*a) || y - 1 == RECT_BOTTOM(*a)) && + (dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH)) + { + dir = -1; + } + } + + if (dir != edge_warp_dir) { + cancel_edge_warp(); + if (dir != (ObDirection)-1) { + edge_warp_odd = TRUE; /* switch on the first timeout */ + edge_warp_timer = g_timeout_add(config_mouse_screenedgetime, + edge_warp_delay_func, NULL); + } + edge_warp_dir = dir; + } +} + +static void cancel_edge_warp(void) +{ + if (edge_warp_timer) g_source_remove(edge_warp_timer); + edge_warp_timer = 0; +} + +static void move_with_keys(KeySym sym, guint state) +{ + gint dx = 0, dy = 0, ox = cur_x, oy = cur_y; + gint opx, px, opy, py; + gint dist = 0; + + /* shift means jump to edge */ + if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) + { + gint x, y; + ObDirection dir; + + if (sym == XK_Right) + dir = OB_DIRECTION_EAST; + else if (sym == XK_Left) + dir = OB_DIRECTION_WEST; + else if (sym == XK_Down) + dir = OB_DIRECTION_SOUTH; + else /* sym == XK_Up */ + dir = OB_DIRECTION_NORTH; + + client_find_move_directional(moveresize_client, dir, &x, &y); + dx = x - moveresize_client->area.x; + dy = y - moveresize_client->area.y; + } else { + /* control means fine grained */ + if (state & + obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL)) + { + dist = 1; + } + else + dist = KEY_DIST; + + if (sym == XK_Right) + dx = dist; + else if (sym == XK_Left) + dx = -dist; + else if (sym == XK_Down) + dy = dist; + else /* if (sym == XK_Up) */ + dy = -dist; + } + + screen_pointer_pos(&opx, &opy); + XWarpPointer(obt_display, None, None, 0, 0, 0, 0, dx, dy); + /* steal the motion events this causes */ + XSync(obt_display, FALSE); + { + XEvent ce; + while (xqueue_remove_local(&ce, xqueue_match_type, + GINT_TO_POINTER(MotionNotify))); + } + screen_pointer_pos(&px, &py); + + cur_x += dx; + cur_y += dy; + do_move(TRUE, dist); + + /* because the cursor moves even though the window does + not nessesarily (resistance), this adjusts where the curor + thinks it started so that it keeps up with where the window + actually is */ + start_x += (px - opx) - (cur_x - ox); + start_y += (py - opy) - (cur_y - oy); +} + +static void resize_with_keys(KeySym sym, guint state) +{ + gint dw = 0, dh = 0, pdx = 0, pdy = 0, opx, opy, px, py; + gint resist = 0; + ObDirection dir; + + /* pick the edge if it needs to move */ + if (sym == XK_Right) { + dir = OB_DIRECTION_EAST; + if (key_resize_edge != OB_DIRECTION_WEST && + key_resize_edge != OB_DIRECTION_EAST) + { + key_resize_edge = OB_DIRECTION_EAST; + return; + } + } else if (sym == XK_Left) { + dir = OB_DIRECTION_WEST; + if (key_resize_edge != OB_DIRECTION_WEST && + key_resize_edge != OB_DIRECTION_EAST) + { + key_resize_edge = OB_DIRECTION_WEST; + return; + } + } else if (sym == XK_Up) { + dir = OB_DIRECTION_NORTH; + if (key_resize_edge != OB_DIRECTION_NORTH && + key_resize_edge != OB_DIRECTION_SOUTH) + { + key_resize_edge = OB_DIRECTION_NORTH; + return; + } + } else /* if (sym == XK_Down) */ { + dir = OB_DIRECTION_SOUTH; + if (key_resize_edge != OB_DIRECTION_NORTH && + key_resize_edge != OB_DIRECTION_SOUTH) + { + key_resize_edge = OB_DIRECTION_SOUTH; + return; + } + } + + /* shift means jump to edge */ + if (state & obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT)) + { + gint x, y, w, h; + + if (sym == XK_Right) + dir = OB_DIRECTION_EAST; + else if (sym == XK_Left) + dir = OB_DIRECTION_WEST; + else if (sym == XK_Down) + dir = OB_DIRECTION_SOUTH; + else /* if (sym == XK_Up)) */ + dir = OB_DIRECTION_NORTH; + + client_find_resize_directional(moveresize_client, key_resize_edge, + key_resize_edge == dir, + &x, &y, &w, &h); + dw = w - moveresize_client->area.width; + dh = h - moveresize_client->area.height; + } else { + gint distw, disth; + + /* control means fine grained */ + if (moveresize_client->size_inc.width > 1) { + distw = moveresize_client->size_inc.width; + resist = 1; + } + else if (state & + obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL)) + { + distw = 1; + resist = 1; + } + else { + distw = KEY_DIST; + resist = KEY_DIST; + } + if (moveresize_client->size_inc.height > 1) { + disth = moveresize_client->size_inc.height; + resist = 1; + } + else if (state & + obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL)) + { + disth = 1; + resist = 1; + } + else { + disth = KEY_DIST; + resist = KEY_DIST; + } + + if (key_resize_edge == OB_DIRECTION_WEST) { + if (dir == OB_DIRECTION_WEST) + dw = distw; + else + dw = -distw; + } + else if (key_resize_edge == OB_DIRECTION_EAST) { + if (dir == OB_DIRECTION_EAST) + dw = distw; + else + dw = -distw; + } + else if (key_resize_edge == OB_DIRECTION_NORTH) { + if (dir == OB_DIRECTION_NORTH) + dh = disth; + else + dh = -disth; + } + else /*if (key_resize_edge == OB_DIRECTION_SOUTH)*/ { + if (dir == OB_DIRECTION_SOUTH) + dh = disth; + else + dh = -disth; + } + } + + if (moveresize_client->max_horz && + (key_resize_edge == OB_DIRECTION_WEST || + key_resize_edge == OB_DIRECTION_EAST)) + { + /* unmax horz */ + was_max_horz = TRUE; + pre_max_area.x = moveresize_client->pre_max_area.x; + pre_max_area.width = moveresize_client->pre_max_area.width; + + moveresize_client->pre_max_area.x = cur_x; + moveresize_client->pre_max_area.width = cur_w; + client_maximize(moveresize_client, FALSE, 1); + } + else if (moveresize_client->max_vert && + (key_resize_edge == OB_DIRECTION_NORTH || + key_resize_edge == OB_DIRECTION_SOUTH)) + { + /* unmax vert */ + was_max_vert = TRUE; + pre_max_area.y = moveresize_client->pre_max_area.y; + pre_max_area.height = moveresize_client->pre_max_area.height; + + moveresize_client->pre_max_area.y = cur_y; + moveresize_client->pre_max_area.height = cur_h; + client_maximize(moveresize_client, FALSE, 2); + } + + calc_resize(TRUE, resist, &dw, &dh, dir); + if (key_resize_edge == OB_DIRECTION_WEST) + cur_x -= dw; + else if (key_resize_edge == OB_DIRECTION_NORTH) + cur_y -= dh; + cur_w += dw; + cur_h += dh; + + /* how to move the pointer to keep up with the change */ + if (key_resize_edge == OB_DIRECTION_WEST) + pdx = -dw; + else if (key_resize_edge == OB_DIRECTION_EAST) + pdx = dw; + else if (key_resize_edge == OB_DIRECTION_NORTH) + pdy = -dh; + else if (key_resize_edge == OB_DIRECTION_SOUTH) + pdy = dh; + + screen_pointer_pos(&opx, &opy); + XWarpPointer(obt_display, None, None, 0, 0, 0, 0, pdx, pdy); + /* steal the motion events this causes */ + XSync(obt_display, FALSE); + { + XEvent ce; + while (xqueue_remove_local(&ce, xqueue_match_type, + GINT_TO_POINTER(MotionNotify))); + } + screen_pointer_pos(&px, &py); + + do_resize(); + + /* because the cursor moves even though the window does + not nessesarily (resistance), this adjusts where the cursor + thinks it started so that it keeps up with where the window + actually is */ + start_x += (px - opx) - dw; + start_y += (py - opy) - dh; + +} + +gboolean moveresize_event(XEvent *e) +{ + gboolean used = FALSE; + + if (!moveresize_in_progress) return FALSE; + + if (e->type == ButtonPress) { + if (!button) { + start_x = e->xbutton.x_root; + start_y = e->xbutton.y_root; + button = e->xbutton.button; /* this will end it now */ + } + used = e->xbutton.button == button; + } else if (e->type == ButtonRelease) { + if (!button || e->xbutton.button == button) { + moveresize_end(FALSE); + used = TRUE; + } + } else if (e->type == MotionNotify) { + if (moving) { + cur_x = start_cx + e->xmotion.x_root - start_x; + cur_y = start_cy + e->xmotion.y_root - start_y; + do_move(FALSE, 0); + do_edge_warp(e->xmotion.x_root, e->xmotion.y_root); + } else { + gint dw, dh; + ObDirection dir; + + if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT)) { + dw = -(e->xmotion.x_root - start_x); + dh = -(e->xmotion.y_root - start_y); + dir = OB_DIRECTION_NORTHWEST; + } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP)) { + dw = 0; + dh = -(e->xmotion.y_root - start_y); + dir = OB_DIRECTION_NORTH; + } else if (corner == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) { + dw = (e->xmotion.x_root - start_x); + dh = -(e->xmotion.y_root - start_y); + dir = OB_DIRECTION_NORTHEAST; + } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT)) { + dw = (e->xmotion.x_root - start_x); + dh = 0; + dir = OB_DIRECTION_EAST; + } else if (corner == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT)) { + dw = (e->xmotion.x_root - start_x); + dh = (e->xmotion.y_root - start_y); + dir = OB_DIRECTION_SOUTHEAST; + } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM)) + { + dw = 0; + dh = (e->xmotion.y_root - start_y); + dir = OB_DIRECTION_SOUTH; + } else if (corner == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) { + dw = -(e->xmotion.x_root - start_x); + dh = (e->xmotion.y_root - start_y); + dir = OB_DIRECTION_SOUTHWEST; + } else if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT)) { + dw = -(e->xmotion.x_root - start_x); + dh = 0; + dir = OB_DIRECTION_WEST; + } else if (corner == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) { + dw = (e->xmotion.x_root - start_x); + dh = (e->xmotion.y_root - start_y); + dir = OB_DIRECTION_SOUTHEAST; + } else + g_assert_not_reached(); + + /* override the client's max state if desired */ + if (ABS(dw) >= config_resist_edge) { + if (moveresize_client->max_horz) { + /* unmax horz */ + was_max_horz = TRUE; + pre_max_area.x = moveresize_client->pre_max_area.x; + pre_max_area.width = moveresize_client->pre_max_area.width; + + moveresize_client->pre_max_area.x = cur_x; + moveresize_client->pre_max_area.width = cur_w; + client_maximize(moveresize_client, FALSE, 1); + } + } + else if (was_max_horz && !moveresize_client->max_horz) { + /* remax horz and put the premax back */ + client_maximize(moveresize_client, TRUE, 1); + moveresize_client->pre_max_area.x = pre_max_area.x; + moveresize_client->pre_max_area.width = pre_max_area.width; + } + + if (ABS(dh) >= config_resist_edge) { + if (moveresize_client->max_vert) { + /* unmax vert */ + was_max_vert = TRUE; + pre_max_area.y = moveresize_client->pre_max_area.y; + pre_max_area.height = + moveresize_client->pre_max_area.height; + + moveresize_client->pre_max_area.y = cur_y; + moveresize_client->pre_max_area.height = cur_h; + client_maximize(moveresize_client, FALSE, 2); + } + } + else if (was_max_vert && !moveresize_client->max_vert) { + /* remax vert and put the premax back */ + client_maximize(moveresize_client, TRUE, 2); + moveresize_client->pre_max_area.y = pre_max_area.y; + moveresize_client->pre_max_area.height = pre_max_area.height; + } + + dw -= cur_w - start_cw; + dh -= cur_h - start_ch; + + calc_resize(FALSE, 0, &dw, &dh, dir); + cur_w += dw; + cur_h += dh; + + if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) || + corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) || + corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT)) + { + cur_x -= dw; + } + if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) || + corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) || + corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT)) + { + cur_y -= dh; + } + + do_resize(); + } + used = TRUE; + } else if (e->type == KeyPress) { + KeySym sym = obt_keyboard_keypress_to_keysym(e); + + if (sym == XK_Escape) { + moveresize_end(TRUE); + used = TRUE; + } else if (sym == XK_Return || sym == XK_KP_Enter) { + moveresize_end(FALSE); + used = TRUE; + } else if (sym == XK_Right || sym == XK_Left || + sym == XK_Up || sym == XK_Down) + { + if (corner == OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD)) { + resize_with_keys(sym, e->xkey.state); + used = TRUE; + } else if (corner == + OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD)) + { + move_with_keys(sym, e->xkey.state); + used = TRUE; + } + } + } +#ifdef SYNC + else if (e->type == obt_display_extension_sync_basep + XSyncAlarmNotify) + { + waiting_for_sync = 0; /* we got our sync... */ + do_resize(); /* ...so try resize if there is more change pending */ + used = TRUE; + } +#endif + + if (used && moveresize_client == focus_client) + event_update_user_time(); + + return used; +} diff --git a/openbox/moveresize.h b/openbox/moveresize.h new file mode 100644 index 0000000..2d0f7dc --- /dev/null +++ b/openbox/moveresize.h @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + moveresize.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __moveresize_h +#define __moveresize_h + +#include <glib.h> + +#ifdef SYNC +#include <X11/extensions/sync.h> +#endif + +struct _ObClient; + +typedef enum { + OB_RESIZE_POS_CENTER, + OB_RESIZE_POS_TOP, + OB_RESIZE_POS_FIXED +} ObResizePopupPos; + +extern gboolean moveresize_in_progress; +extern struct _ObClient *moveresize_client; +#ifdef SYNC +extern XSyncAlarm moveresize_alarm; +#endif + +void moveresize_startup(gboolean reconfig); +void moveresize_shutdown(gboolean reconfig); + +/*! @param corner This is one of the prop_atoms.net_wm_moveresize_ atoms */ +void moveresize_start(struct _ObClient *c, + gint x, gint y, guint button, guint32 corner); +void moveresize_end(gboolean cancel); + +gboolean moveresize_event(XEvent *e); + +#endif diff --git a/openbox/mwm.h b/openbox/mwm.h new file mode 100644 index 0000000..e7a9d39 --- /dev/null +++ b/openbox/mwm.h @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + mwm.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ob__mwm_h +#define __ob__mwm_h + +#include <glib.h> + +typedef struct _ObMwmHints ObMwmHints; + +/*! The MWM Hints as retrieved from the window property + This structure only contains 3 elements, even though the Motif 2.0 + structure contains 5. We only use the first 3, so that is all gets + defined. +*/ +struct _ObMwmHints +{ + /*! A bitmask of ObMwmFlags values */ + guint flags; + /*! A bitmask of ObMwmFunctions values */ + guint functions; + /*! A bitmask of ObMwmDecorations values */ + guint decorations; +}; + +/*! The number of elements in the ObMwmHints struct */ +#define OB_MWM_ELEMENTS 3 + +/*! Possible flags for MWM Hints (defined by Motif 2.0) */ +typedef enum +{ + OB_MWM_FLAG_FUNCTIONS = 1 << 0, /*!< The MMW Hints define funcs */ + OB_MWM_FLAG_DECORATIONS = 1 << 1 /*!< The MWM Hints define decor */ +} ObMwmFlags; + +/*! Possible functions for MWM Hints (defined by Motif 2.0) */ +typedef enum +{ + OB_MWM_FUNC_ALL = 1 << 0, /*!< All functions */ + OB_MWM_FUNC_RESIZE = 1 << 1, /*!< Allow resizing */ + OB_MWM_FUNC_MOVE = 1 << 2, /*!< Allow moving */ + OB_MWM_FUNC_ICONIFY = 1 << 3, /*!< Allow to be iconfied */ + OB_MWM_FUNC_MAXIMIZE = 1 << 4 /*!< Allow to be maximized */ +#if 0 + OM_MWM_FUNC_CLOSE = 1 << 5 /*!< Allow to be closed */ +#endif +} ObMwmFunctions; + +/*! Possible decorations for MWM Hints (defined by Motif 2.0) */ +typedef enum +{ + OB_MWM_DECOR_ALL = 1 << 0, /*!< All decorations */ + OB_MWM_DECOR_BORDER = 1 << 1, /*!< Show a border */ + OB_MWM_DECOR_HANDLE = 1 << 2, /*!< Show a handle (bottom) */ + OB_MWM_DECOR_TITLE = 1 << 3, /*!< Show a titlebar */ +#if 0 + OB_MWM_DECOR_MENU = 1 << 4, /*!< Show a menu */ +#endif + OB_MWM_DECOR_ICONIFY = 1 << 5, /*!< Show an iconify button */ + OB_MWM_DECOR_MAXIMIZE = 1 << 6 /*!< Show a maximize button */ +} ObMwmDecorations; + +#endif diff --git a/openbox/openbox.c b/openbox/openbox.c new file mode 100644 index 0000000..fbc01fd --- /dev/null +++ b/openbox/openbox.c @@ -0,0 +1,779 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + openbox.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "openbox.h" +#include "session.h" +#include "dock.h" +#include "event.h" +#include "menu.h" +#include "client.h" +#include "screen.h" +#include "actions.h" +#include "startupnotify.h" +#include "focus.h" +#include "focus_cycle.h" +#include "focus_cycle_indicator.h" +#include "focus_cycle_popup.h" +#include "moveresize.h" +#include "frame.h" +#include "framerender.h" +#include "keyboard.h" +#include "mouse.h" +#include "menuframe.h" +#include "grab.h" +#include "group.h" +#include "config.h" +#include "ping.h" +#include "prompt.h" +#include "gettext.h" +#include "obrender/render.h" +#include "obrender/theme.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/signal.h" +#include "obt/prop.h" +#include "obt/keyboard.h" +#include "obt/xml.h" + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_WAIT_H +# include <sys/types.h> +# include <sys/wait.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <errno.h> + +#include <X11/cursorfont.h> +#if USE_XCURSOR +#include <X11/Xcursor/Xcursor.h> +#endif + +RrInstance *ob_rr_inst; +RrImageCache *ob_rr_icons; +RrTheme *ob_rr_theme; +GMainLoop *ob_main_loop; +gint ob_screen; +gboolean ob_replace_wm = FALSE; +gboolean ob_sm_use = TRUE; +gchar *ob_sm_id = NULL; +gchar *ob_sm_save_file = NULL; +gboolean ob_sm_restore = TRUE; +gboolean ob_debug_xinerama = FALSE; +const gchar *ob_locale_msg = NULL; + +static ObState state; +static gboolean xsync = FALSE; +static gboolean reconfigure = FALSE; +static gboolean restart = FALSE; +static gchar *restart_path = NULL; +static Cursor cursors[OB_NUM_CURSORS]; +static gint exitcode = 0; +static guint remote_control = 0; +static gboolean being_replaced = FALSE; +static gchar *config_file = NULL; +static gchar *startup_cmd = NULL; + +static void signal_handler(gint signal, gpointer data); +static void remove_args(gint *argc, gchar **argv, gint index, gint num); +static void parse_env(); +static void parse_args(gint *argc, gchar **argv); +static Cursor load_cursor(const gchar *name, guint fontval); +static void run_startup_cmd(void); + +gint main(gint argc, gchar **argv) +{ + gchar *program_name; + + obt_signal_listen(); + + ob_set_state(OB_STATE_STARTING); + + ob_debug_startup(); + + /* initialize the locale */ + if (!(ob_locale_msg = setlocale(LC_MESSAGES, ""))) + g_message("Couldn't set messages locale category from environment."); + if (!setlocale(LC_ALL, "")) + g_message("Couldn't set locale from environment."); + bindtextdomain(PACKAGE_NAME, LOCALEDIR); + bind_textdomain_codeset(PACKAGE_NAME, "UTF-8"); + textdomain(PACKAGE_NAME); + + if (chdir(g_get_home_dir()) == -1) + g_message(_("Unable to change to home directory \"%s\": %s"), + g_get_home_dir(), g_strerror(errno)); + + /* parse the command line args, which can change the argv[0] */ + parse_args(&argc, argv); + /* parse the environment variables */ + parse_env(); + + program_name = g_path_get_basename(argv[0]); + g_set_prgname(program_name); + + if (!remote_control) + session_startup(argc, argv); + + if (!obt_display_open(NULL)) + ob_exit_with_error(_("Failed to open the display from the DISPLAY environment variable.")); + + if (remote_control) { + /* Send client message telling the OB process to: + * remote_control = 1 -> reconfigure + * remote_control = 2 -> restart */ + OBT_PROP_MSG(ob_screen, obt_root(ob_screen), + OB_CONTROL, remote_control, 0, 0, 0, 0); + obt_display_close(); + exit(EXIT_SUCCESS); + } + + ob_main_loop = g_main_loop_new(NULL, FALSE); + + /* set up signal handlers, they are called from the mainloop + in the main program's thread */ + obt_signal_add_callback(SIGUSR1, signal_handler, NULL); + obt_signal_add_callback(SIGUSR2, signal_handler, NULL); + obt_signal_add_callback(SIGTERM, signal_handler, NULL); + obt_signal_add_callback(SIGINT, signal_handler, NULL); + obt_signal_add_callback(SIGHUP, signal_handler, NULL); + obt_signal_add_callback(SIGPIPE, signal_handler, NULL); + obt_signal_add_callback(SIGCHLD, signal_handler, NULL); + obt_signal_add_callback(SIGTTIN, signal_handler, NULL); + obt_signal_add_callback(SIGTTOU, signal_handler, NULL); + + ob_screen = DefaultScreen(obt_display); + + ob_rr_inst = RrInstanceNew(obt_display, ob_screen); + if (ob_rr_inst == NULL) + ob_exit_with_error(_("Failed to initialize the obrender library.")); + /* Saving 3 resizes of an RrImage makes a lot of sense for icons, as there + are generally 3 icon sizes needed: the titlebar icon, the menu icon, + and the alt-tab icon + */ + ob_rr_icons = RrImageCacheNew(3); + + XSynchronize(obt_display, xsync); + + /* check for locale support */ + if (!XSupportsLocale()) + g_message(_("X server does not support locale.")); + if (!XSetLocaleModifiers("")) + g_message(_("Cannot set locale modifiers for the X server.")); + + /* set the DISPLAY environment variable for any lauched children, to the + display we're using, so they open in the right place. */ + g_setenv("DISPLAY", DisplayString(obt_display), TRUE); + + /* create available cursors */ + cursors[OB_CURSOR_NONE] = None; + cursors[OB_CURSOR_POINTER] = load_cursor("left_ptr", XC_left_ptr); + cursors[OB_CURSOR_BUSYPOINTER] = load_cursor("left_ptr_watch",XC_left_ptr); + cursors[OB_CURSOR_BUSY] = load_cursor("watch", XC_watch); + cursors[OB_CURSOR_MOVE] = load_cursor("fleur", XC_fleur); + cursors[OB_CURSOR_NORTH] = load_cursor("top_side", XC_top_side); + cursors[OB_CURSOR_NORTHEAST] = load_cursor("top_right_corner", + XC_top_right_corner); + cursors[OB_CURSOR_EAST] = load_cursor("right_side", XC_right_side); + cursors[OB_CURSOR_SOUTHEAST] = load_cursor("bottom_right_corner", + XC_bottom_right_corner); + cursors[OB_CURSOR_SOUTH] = load_cursor("bottom_side", XC_bottom_side); + cursors[OB_CURSOR_SOUTHWEST] = load_cursor("bottom_left_corner", + XC_bottom_left_corner); + cursors[OB_CURSOR_WEST] = load_cursor("left_side", XC_left_side); + cursors[OB_CURSOR_NORTHWEST] = load_cursor("top_left_corner", + XC_top_left_corner); + + if (screen_annex()) { /* it will be ours! */ + + /* get a timestamp from after taking over as the WM. if we use the + old timestamp to set focus it can fail when replacing another WM. */ + event_reset_time(); + + do { + ObPrompt *xmlprompt = NULL; + + if (reconfigure) obt_keyboard_reload(); + + { + ObtXmlInst *i; + + /* startup the parsing so everything can register sections + of the rc */ + i = obt_xml_instance_new(); + + /* register all the available actions */ + actions_startup(reconfigure); + /* start up config which sets up with the parser */ + config_startup(i); + + /* parse/load user options */ + if ((config_file && + obt_xml_load_file(i, config_file, "openbox_config")) || + obt_xml_load_config_file(i, "openbox", "rc.xml", + "openbox_config")) + { + obt_xml_tree_from_root(i); + obt_xml_close(i); + } + else { + g_message(_("Unable to find a valid config file, using some simple defaults")); + config_file = NULL; + } + + if (config_file) { + gchar *p = g_filename_to_utf8(config_file, -1, + NULL, NULL, NULL); + if (p) + OBT_PROP_SETS(obt_root(ob_screen), OB_CONFIG_FILE, p); + g_free(p); + } + else + OBT_PROP_ERASE(obt_root(ob_screen), OB_CONFIG_FILE); + + /* we're done with parsing now, kill it */ + obt_xml_instance_unref(i); + } + + /* load the theme specified in the rc file */ + { + RrTheme *theme; + if ((theme = RrThemeNew(ob_rr_inst, config_theme, TRUE, + config_font_activewindow, + config_font_inactivewindow, + config_font_menutitle, + config_font_menuitem, + config_font_activeosd, + config_font_inactiveosd))) + { + RrThemeFree(ob_rr_theme); + ob_rr_theme = theme; + } + if (ob_rr_theme == NULL) + ob_exit_with_error(_("Unable to load a theme.")); + + OBT_PROP_SETS(obt_root(ob_screen), OB_THEME, + ob_rr_theme->name); + } + + if (reconfigure) { + GList *it; + + /* update all existing windows for the new theme */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + frame_adjust_theme(c->frame); + } + } + event_startup(reconfigure); + /* focus_backup is used for stacking, so this needs to come before + anything that calls stacking_add */ + sn_startup(reconfigure); + window_startup(reconfigure); + focus_startup(reconfigure); + focus_cycle_startup(reconfigure); + focus_cycle_indicator_startup(reconfigure); + focus_cycle_popup_startup(reconfigure); + screen_startup(reconfigure); + grab_startup(reconfigure); + group_startup(reconfigure); + ping_startup(reconfigure); + client_startup(reconfigure); + dock_startup(reconfigure); + moveresize_startup(reconfigure); + keyboard_startup(reconfigure); + mouse_startup(reconfigure); + menu_frame_startup(reconfigure); + menu_startup(reconfigure); + prompt_startup(reconfigure); + + /* do this after everything is started so no events will get + missed */ + xqueue_listen(); + + if (!reconfigure) { + guint32 xid; + ObWindow *w; + + /* get all the existing windows */ + window_manage_all(); + + /* focus what was focused if a wm was already running */ + if (OBT_PROP_GET32(obt_root(ob_screen), + NET_ACTIVE_WINDOW, WINDOW, &xid) && + (w = window_find(xid)) && WINDOW_IS_CLIENT(w)) + { + client_focus(WINDOW_AS_CLIENT(w)); + } + } else { + GList *it; + + /* redecorate all existing windows */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + + /* the new config can change the window's decorations */ + client_setup_decor_and_functions(c, FALSE); + /* redraw the frames */ + frame_adjust_area(c->frame, TRUE, TRUE, FALSE); + /* the decor sizes may have changed, so the windows may + end up in new positions */ + client_reconfigure(c, FALSE); + } + } + + ob_set_state(OB_STATE_RUNNING); + + if (!reconfigure && startup_cmd) run_startup_cmd(); + + reconfigure = FALSE; + + /* look for parsing errors */ + { + xmlErrorPtr e = xmlGetLastError(); + if (e) { + gchar *m; + + m = g_strdup_printf(_("One or more XML syntax errors were found while parsing the Openbox configuration files. See stdout for more information. The last error seen was in file \"%s\" line %d, with message: %s"), e->file, e->line, e->message); + xmlprompt = + prompt_show_message(m, _("Openbox Syntax Error"), _("Close")); + g_free(m); + xmlResetError(e); + } + } + + g_main_loop_run(ob_main_loop); + ob_set_state(reconfigure ? + OB_STATE_RECONFIGURING : OB_STATE_EXITING); + + if (xmlprompt) { + prompt_unref(xmlprompt); + xmlprompt = NULL; + } + + if (!reconfigure) + window_unmanage_all(); + + prompt_shutdown(reconfigure); + menu_shutdown(reconfigure); + menu_frame_shutdown(reconfigure); + mouse_shutdown(reconfigure); + keyboard_shutdown(reconfigure); + moveresize_shutdown(reconfigure); + dock_shutdown(reconfigure); + client_shutdown(reconfigure); + ping_shutdown(reconfigure); + group_shutdown(reconfigure); + grab_shutdown(reconfigure); + screen_shutdown(reconfigure); + focus_cycle_popup_shutdown(reconfigure); + focus_cycle_indicator_shutdown(reconfigure); + focus_cycle_shutdown(reconfigure); + focus_shutdown(reconfigure); + window_shutdown(reconfigure); + sn_shutdown(reconfigure); + event_shutdown(reconfigure); + config_shutdown(); + actions_shutdown(reconfigure); + } while (reconfigure); + } + + XSync(obt_display, FALSE); + + RrThemeFree(ob_rr_theme); + RrImageCacheUnref(ob_rr_icons); + RrInstanceFree(ob_rr_inst); + + session_shutdown(being_replaced); + + obt_display_close(); + + if (restart) { + ob_debug_shutdown(); + obt_signal_stop(); + if (restart_path != NULL) { + gint argcp; + gchar **argvp; + GError *err = NULL; + + /* run other window manager */ + if (g_shell_parse_argv(restart_path, &argcp, &argvp, &err)) { + execvp(argvp[0], argvp); + g_strfreev(argvp); + } else { + g_message( + _("Restart failed to execute new executable \"%s\": %s"), + restart_path, err->message); + g_error_free(err); + } + } + + /* we remove the session arguments from argv, so put them back, + also don't restore the session on restart */ + if (ob_sm_save_file != NULL || ob_sm_id != NULL) { + gchar **nargv; + gint i, l; + + l = argc + 1 + + (ob_sm_save_file != NULL ? 2 : 0) + + (ob_sm_id != NULL ? 2 : 0); + nargv = g_new0(gchar*, l+1); + for (i = 0; i < argc; ++i) + nargv[i] = argv[i]; + + if (ob_sm_save_file != NULL) { + nargv[i++] = g_strdup("--sm-save-file"); + nargv[i++] = ob_sm_save_file; + } + if (ob_sm_id != NULL) { + nargv[i++] = g_strdup("--sm-client-id"); + nargv[i++] = ob_sm_id; + } + nargv[i++] = g_strdup("--sm-no-load"); + g_assert(i == l); + argv = nargv; + } + + /* re-run me */ + execvp(argv[0], argv); /* try how we were run */ + execlp(argv[0], program_name, (gchar*)NULL); /* last resort */ + } + + /* free stuff passed in from the command line or environment */ + g_free(ob_sm_save_file); + g_free(ob_sm_id); + g_free(program_name); + + if (!restart) { + ob_debug_shutdown(); + obt_signal_stop(); + } + + return exitcode; +} + +static void signal_handler(gint signal, gpointer data) +{ + switch (signal) { + case SIGUSR1: + ob_debug("Caught signal %d. Restarting.", signal); + ob_restart(); + break; + case SIGUSR2: + ob_debug("Caught signal %d. Reconfiguring.", signal); + ob_reconfigure(); + break; + case SIGCHLD: + /* reap children */ + while (waitpid(-1, NULL, WNOHANG) > 0); + break; + case SIGTTIN: + case SIGTTOU: + ob_debug("Caught signal %d. Ignoring.", signal); + break; + default: + ob_debug("Caught signal %d. Exiting.", signal); + /* TERM and INT return a 0 code */ + ob_exit(!(signal == SIGTERM || signal == SIGINT)); + } +} + +static void print_version(void) +{ + g_print("Openbox %s\n", PACKAGE_VERSION); + g_print(_("Copyright (c)")); + g_print(" 2008-2011 Mikael Magnusson\n"); + g_print(_("Copyright (c)")); + g_print(" 2003-2011 Dana Jansens\n\n"); + g_print("This program comes with ABSOLUTELY NO WARRANTY.\n"); + g_print("This is free software, and you are welcome to redistribute it\n"); + g_print("under certain conditions. See the file COPYING for details.\n\n"); +} + +static void print_help(void) +{ + g_print(_("Syntax: openbox [options]\n")); + g_print(_("\nOptions:\n")); + g_print(_(" --help Display this help and exit\n")); + g_print(_(" --version Display the version and exit\n")); + g_print(_(" --replace Replace the currently running window manager\n")); + /* TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." + aligned still, if you have to, make a new line with \n and 22 spaces. It's + fine to leave it as FILE though. */ + g_print(_(" --config-file FILE Specify the path to the config file to use\n")); + g_print(_(" --sm-disable Disable connection to the session manager\n")); + g_print(_("\nPassing messages to a running Openbox instance:\n")); + g_print(_(" --reconfigure Reload Openbox's configuration\n")); + g_print(_(" --restart Restart Openbox\n")); + g_print(_(" --exit Exit Openbox\n")); + g_print(_("\nDebugging options:\n")); + g_print(_(" --sync Run in synchronous mode\n")); + g_print(_(" --startup CMD Run CMD after starting\n")); + g_print(_(" --debug Display debugging output\n")); + g_print(_(" --debug-focus Display debugging output for focus handling\n")); + g_print(_(" --debug-session Display debugging output for session management\n")); + g_print(_(" --debug-xinerama Split the display into fake xinerama screens\n")); + g_print(_("\nPlease report bugs at %s\n"), PACKAGE_BUGREPORT); +} + +static void remove_args(gint *argc, gchar **argv, gint index, gint num) +{ + gint i; + + for (i = index; i < *argc - num; ++i) + argv[i] = argv[i+num]; + for (; i < *argc; ++i) + argv[i] = NULL; + *argc -= num; +} + +static void run_startup_cmd(void) +{ + gchar **argv = NULL; + GError *e = NULL; + gboolean ok; + + if (!g_shell_parse_argv(startup_cmd, NULL, &argv, &e)) { + g_message("Error parsing startup command: %s", + e->message); + g_error_free(e); + e = NULL; + } + ok = g_spawn_async(NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, NULL, &e); + if (!ok) { + g_message("Error launching startup command: %s", + e->message); + g_error_free(e); + e = NULL; + } +} + +static void parse_env(void) +{ + const gchar *id; + + /* unset this so we don't pass it on unknowingly */ + g_unsetenv("DESKTOP_STARTUP_ID"); + + /* this is how gnome-session passes in a session client id */ + id = g_getenv("DESKTOP_AUTOSTART_ID"); + if (id) { + g_unsetenv("DESKTOP_AUTOSTART_ID"); + if (ob_sm_id) g_free(ob_sm_id); + ob_sm_id = g_strdup(id); + ob_debug_type(OB_DEBUG_SM, + "DESKTOP_AUTOSTART_ID %s supercedes --sm-client-id\n", + ob_sm_id); + } +} + +static void parse_args(gint *argc, gchar **argv) +{ + gint i; + + for (i = 1; i < *argc; ++i) { + if (!strcmp(argv[i], "--version")) { + print_version(); + exit(0); + } + else if (!strcmp(argv[i], "--help")) { + print_help(); + exit(0); + } + else if (!strcmp(argv[i], "--g-fatal-warnings")) { + g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL); + } + else if (!strcmp(argv[i], "--replace")) { + ob_replace_wm = TRUE; + remove_args(argc, argv, i, 1); + --i; /* this arg was removed so go back */ + } + else if (!strcmp(argv[i], "--sync")) { + xsync = TRUE; + } + else if (!strcmp(argv[i], "--startup")) { + if (i == *argc - 1) /* no args left */ + g_printerr(_("%s requires an argument\n"), "--startup"); + else { + /* this will be in the current locale encoding, which is + what we want */ + startup_cmd = argv[i+1]; + remove_args(argc, argv, i, 2); + --i; /* this arg was removed so go back */ + ob_debug("--startup %s", startup_cmd); + } + } + else if (!strcmp(argv[i], "--debug")) { + ob_debug_enable(OB_DEBUG_NORMAL, TRUE); + ob_debug_enable(OB_DEBUG_APP_BUGS, TRUE); + } + else if (!strcmp(argv[i], "--debug-focus")) { + ob_debug_enable(OB_DEBUG_FOCUS, TRUE); + } + else if (!strcmp(argv[i], "--debug-session")) { + ob_debug_enable(OB_DEBUG_SM, TRUE); + } + else if (!strcmp(argv[i], "--debug-xinerama")) { + ob_debug_xinerama = TRUE; + } + else if (!strcmp(argv[i], "--reconfigure")) { + remote_control = 1; + } + else if (!strcmp(argv[i], "--restart")) { + remote_control = 2; + } + else if (!strcmp(argv[i], "--exit")) { + remote_control = 3; + } + else if (!strcmp(argv[i], "--config-file")) { + if (i == *argc - 1) /* no args left */ + g_printerr(_("%s requires an argument\n"), "--config-file"); + else { + /* this will be in the current locale encoding, which is + what we want */ + config_file = argv[i+1]; + ++i; /* skip the argument */ + ob_debug("--config-file %s", config_file); + } + } + else if (!strcmp(argv[i], "--sm-save-file")) { + if (i == *argc - 1) /* no args left */ + /* not translated cuz it's sekret */ + g_printerr("--sm-save-file requires an argument\n"); + else { + ob_sm_save_file = g_strdup(argv[i+1]); + remove_args(argc, argv, i, 2); + --i; /* this arg was removed so go back */ + ob_debug_type(OB_DEBUG_SM, "--sm-save-file %s", + ob_sm_save_file); + } + } + else if (!strcmp(argv[i], "--sm-client-id")) { + if (i == *argc - 1) /* no args left */ + /* not translated cuz it's sekret */ + g_printerr("--sm-client-id requires an argument\n"); + else { + ob_sm_id = g_strdup(argv[i+1]); + remove_args(argc, argv, i, 2); + --i; /* this arg was removed so go back */ + ob_debug_type(OB_DEBUG_SM, "--sm-client-id %s", ob_sm_id); + } + } + else if (!strcmp(argv[i], "--sm-disable")) { + ob_sm_use = FALSE; + } + else if (!strcmp(argv[i], "--sm-no-load")) { + ob_sm_restore = FALSE; + remove_args(argc, argv, i, 1); + --i; /* this arg was removed so go back */ + } + else { + /* this is a memleak.. oh well.. heh */ + gchar *err = g_strdup_printf + (_("Invalid command line argument \"%s\"\n"), argv[i]); + ob_exit_with_error(err); + } + } +} + +static Cursor load_cursor(const gchar *name, guint fontval) +{ + Cursor c = None; + +#if USE_XCURSOR + c = XcursorLibraryLoadCursor(obt_display, name); +#endif + if (c == None) + c = XCreateFontCursor(obt_display, fontval); + return c; +} + +void ob_exit_with_error(const gchar *msg) +{ + g_message("%s", msg); + session_shutdown(TRUE); + exit(EXIT_FAILURE); +} + +void ob_restart_other(const gchar *path) +{ + restart_path = g_strdup(path); + ob_restart(); +} + +void ob_restart(void) +{ + restart = TRUE; + ob_exit(0); +} + +void ob_reconfigure(void) +{ + reconfigure = TRUE; + ob_exit(0); +} + +void ob_exit(gint code) +{ + exitcode = code; + g_main_loop_quit(ob_main_loop); +} + +void ob_exit_replace(void) +{ + exitcode = 0; + being_replaced = TRUE; + g_main_loop_quit(ob_main_loop); +} + +Cursor ob_cursor(ObCursor cursor) +{ + g_assert(cursor < OB_NUM_CURSORS); + return cursors[cursor]; +} + +ObState ob_state(void) +{ + return state; +} + +void ob_set_state(ObState s) +{ + state = s; +} diff --git a/openbox/openbox.h b/openbox/openbox.h new file mode 100644 index 0000000..94f4910 --- /dev/null +++ b/openbox/openbox.h @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + openbox.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __openbox_h +#define __openbox_h + +#include "misc.h" + +#include "obrender/render.h" +#include "obrender/theme.h" +#include "obt/display.h" + +#include <glib.h> + +extern RrInstance *ob_rr_inst; +extern RrImageCache *ob_rr_icons; +extern RrTheme *ob_rr_theme; + +extern GMainLoop *ob_main_loop; + +/*! The number of the screen on which we're running */ +extern gint ob_screen; + +extern gboolean ob_sm_use; +extern gchar *ob_sm_id; +/* This save_file will get pass to ourselves if we restart too! So we won't + make a new file every time, yay. */ +extern gchar *ob_sm_save_file; +extern gboolean ob_sm_restore; +extern gboolean ob_replace_wm; +extern gboolean ob_debug_xinerama; + +/*! The current locale for the LC_MESSAGES category */ +extern const gchar *ob_locale_msg; + +/* The state of execution of the window manager */ +ObState ob_state(void); +void ob_set_state(ObState state); + +void ob_restart_other(const gchar *path); +void ob_restart(void); +void ob_exit(gint code); +void ob_exit_replace(void); + +void ob_reconfigure(void); + +void ob_exit_with_error(const gchar *msg) G_GNUC_NORETURN; + +Cursor ob_cursor(ObCursor cursor); + +#endif diff --git a/openbox/ping.c b/openbox/ping.c new file mode 100644 index 0000000..7cb4766 --- /dev/null +++ b/openbox/ping.c @@ -0,0 +1,165 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "ping.h" +#include "client.h" +#include "event.h" +#include "debug.h" +#include "openbox.h" +#include "obt/prop.h" + +typedef struct _ObPingTarget +{ + ObClient *client; + ObPingEventHandler h; + guint32 id; + guint loopid; + gint waiting; +} ObPingTarget; + +static GHashTable *ping_ids = NULL; +static guint32 ping_next_id = 1; + +#define PING_TIMEOUT 3000 /* in MS */ +/*! Warn the user after this many PING_TIMEOUT intervals */ +#define PING_TIMEOUT_WARN 2 + +static void ping_send(ObPingTarget *t); +static void ping_end(ObClient *client, gpointer data); +static gboolean ping_timeout(gpointer data); +static gboolean find_client(gpointer key, gpointer value, gpointer client); + +void ping_startup(gboolean reconfigure) +{ + if (reconfigure) return; + + ping_ids = g_hash_table_new(g_int_hash, g_int_equal); + + /* listen for clients to disappear */ + client_add_destroy_notify(ping_end, NULL); +} + +void ping_shutdown(gboolean reconfigure) +{ + if (reconfigure) return; + + g_hash_table_unref(ping_ids); + ping_ids = NULL; + + client_remove_destroy_notify(ping_end); +} + +void ping_start(struct _ObClient *client, ObPingEventHandler h) +{ + ObPingTarget *t; + + /* make sure the client supports ping! */ + g_assert(client->ping == TRUE); + + /* make sure we're not already pinging the client */ + if (g_hash_table_find(ping_ids, find_client, client) != NULL) return; + + t = g_slice_new0(ObPingTarget); + t->client = client; + t->h = h; + + t->loopid = g_timeout_add_full(G_PRIORITY_DEFAULT, PING_TIMEOUT, + ping_timeout, t, NULL); + /* act like we just timed out immediately, to start the pinging process + now instead of after the first delay. this makes sure the client + ends up in the ping_ids hash table now. */ + ping_timeout(t); + + /* make sure we can remove the client later */ + g_assert(g_hash_table_find(ping_ids, find_client, client) != NULL); +} + +void ping_got_pong(guint32 id) +{ + ObPingTarget *t; + + if ((t = g_hash_table_lookup(ping_ids, &id))) { + /*ob_debug("-PONG: '%s' (id %u)", t->client->title, t->id);*/ + if (t->waiting > PING_TIMEOUT_WARN) { + /* we had notified that they weren't responding, so now we + need to notify that they are again */ + t->h(t->client, FALSE); + } + t->waiting = 0; /* not waiting for a reply anymore */ + + /* we got a pong so we're happy now */ + ping_end(t->client, NULL); + } + else + ob_debug("Got PONG with id %u but not waiting for one", id); +} + +static gboolean find_client(gpointer key, gpointer value, gpointer client) +{ + ObPingTarget *t = value; + return t->client == client; +} + +static void ping_send(ObPingTarget *t) +{ + /* t->id is 0 when it hasn't been assigned an id ever yet. + we can reuse ids when t->waiting == 0, because we won't be getting a + pong for that id in the future again. that way for apps that aren't + timing out we don't need to remove/add them from/to the hash table */ + if (t->id == 0 || t->waiting > 0) { + /* pick an id, and reinsert in the hash table with the new id */ + if (t->id) g_hash_table_remove(ping_ids, &t->id); + t->id = ping_next_id; + if (++ping_next_id == 0) ++ping_next_id; /* skip 0 on wraparound */ + g_hash_table_insert(ping_ids, &t->id, t); + } + + /*ob_debug("+PING: '%s' (id %u)", t->client->title, t->id);*/ + OBT_PROP_MSG_TO(t->client->window, t->client->window, WM_PROTOCOLS, + OBT_PROP_ATOM(NET_WM_PING), t->id, t->client->window, 0, 0, + NoEventMask); +} + +static gboolean ping_timeout(gpointer data) +{ + ObPingTarget *t = data; + + ping_send(t); + + /* if the client hasn't been responding then do something about it */ + if (t->waiting == PING_TIMEOUT_WARN) + t->h(t->client, TRUE); /* notify that the client isn't responding */ + + ++t->waiting; + + return TRUE; /* repeat */ +} + +static void ping_end(ObClient *client, gpointer data) +{ + ObPingTarget *t; + + if ((t = g_hash_table_find(ping_ids, find_client, client))) { + g_hash_table_remove(ping_ids, &t->id); + + g_source_remove(t->loopid); + + g_slice_free(ObPingTarget, t); + } +} diff --git a/openbox/ping.h b/openbox/ping.h new file mode 100644 index 0000000..ceb0bdb --- /dev/null +++ b/openbox/ping.h @@ -0,0 +1,43 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ping_h +#define __ping_h + +#include <X11/Xlib.h> +#include <glib.h> + +struct _ObClient; + +/*! + Notifies when the client application isn't responding to pings, or when it + starts responding again. + @param dead TRUE if the app isn't responding, FALSE if it starts responding + again +*/ +typedef void (*ObPingEventHandler) (struct _ObClient *c, gboolean dead); + +void ping_startup(gboolean reconfigure); +void ping_shutdown(gboolean reconfigure); + +void ping_start(struct _ObClient *c, ObPingEventHandler h); + +void ping_got_pong(guint32 id); + +#endif diff --git a/openbox/place.c b/openbox/place.c new file mode 100644 index 0000000..2cd21bb --- /dev/null +++ b/openbox/place.c @@ -0,0 +1,593 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + place.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "client.h" +#include "group.h" +#include "screen.h" +#include "frame.h" +#include "focus.h" +#include "config.h" +#include "dock.h" +#include "debug.h" + +extern ObDock *dock; + +static Rect *pick_pointer_head(ObClient *c) +{ + return screen_area(c->desktop, screen_monitor_pointer(), NULL); +} + +/* use the following priority lists for pick_head() + + When a window is being placed in the FOREGROUND, use a monitor chosen in + the following order: + 1. per-app settings + 2. same monitor as parent + 3. primary monitor if placement=PRIMARY + active monitor if placement=ACTIVE + pointer monitor if placement=MOUSE + 4. primary monitor + 5. other monitors where the window has group members on the same desktop + 6. other monitors where the window has group members on other desktops + 7. other monitors + + When a window is being placed in the BACKGROUND, use a monitor chosen in the + following order: + 1. per-app settings + 2. same monitor as parent + 3. other monitors where the window has group members on the same desktop + 3a. primary monitor in this set + 3b. other monitors in this set + 4. other monitors where the window has group members on other desktops + 4a. primary monitor in this set + 4b. other monitors in this set + 5. other monitors + 5a. primary monitor in this set + 5b. other monitors in this set +*/ + +/*! One for each possible head, used to sort them in order of precedence. */ +typedef struct { + guint monitor; + guint flags; +} ObPlaceHead; + +/*! Flags for ObPlaceHead */ +enum { + HEAD_PARENT = 1 << 0, /* parent's monitor */ + HEAD_PLACED = 1 << 1, /* chosen monitor by placement */ + HEAD_PRIMARY = 1 << 2, /* primary monitor */ + HEAD_GROUP_DESK = 1 << 3, /* has a group member on the same desktop */ + HEAD_GROUP = 1 << 4, /* has a group member on another desktop */ + HEAD_PERAPP = 1 << 5, /* chosen by per-app settings */ +}; + +gint cmp_foreground(const void *a, const void *b) +{ + const ObPlaceHead *h1 = a; + const ObPlaceHead *h2 = b; + gint i = 0; + + if (h1->monitor == h2->monitor) return 0; + + if (h1->flags & HEAD_PERAPP) --i; + if (h2->flags & HEAD_PERAPP) ++i; + if (i) return i; + + if (h1->flags & HEAD_PARENT) --i; + if (h2->flags & HEAD_PARENT) ++i; + if (i) return i; + + if (h1->flags & HEAD_PLACED) --i; + if (h2->flags & HEAD_PLACED) ++i; + if (i) return i; + + if (h1->flags & HEAD_PRIMARY) --i; + if (h2->flags & HEAD_PRIMARY) ++i; + if (i) return i; + + if (h1->flags & HEAD_GROUP_DESK) --i; + if (h2->flags & HEAD_GROUP_DESK) ++i; + if (i) return i; + + if (h1->flags & HEAD_GROUP) --i; + if (h2->flags & HEAD_GROUP) ++i; + if (i) return i; + + return h1->monitor - h2->monitor; +} + +gint cmp_background(const void *a, const void *b) +{ + const ObPlaceHead *h1 = a; + const ObPlaceHead *h2 = b; + gint i = 0; + + if (h1->monitor == h2->monitor) return 0; + + if (h1->flags & HEAD_PERAPP) --i; + if (h2->flags & HEAD_PERAPP) ++i; + if (i) return i; + + if (h1->flags & HEAD_PARENT) --i; + if (h2->flags & HEAD_PARENT) ++i; + if (i) return i; + + if (h1->flags & HEAD_GROUP_DESK || h2->flags & HEAD_GROUP_DESK) { + if (h1->flags & HEAD_GROUP_DESK) --i; + if (h2->flags & HEAD_GROUP_DESK) ++i; + if (i) return i; + if (h1->flags & HEAD_PRIMARY) --i; + if (h2->flags & HEAD_PRIMARY) ++i; + if (i) return i; + } + + if (h1->flags & HEAD_GROUP || h2->flags & HEAD_GROUP) { + if (h1->flags & HEAD_GROUP) --i; + if (h2->flags & HEAD_GROUP) ++i; + if (i) return i; + if (h1->flags & HEAD_PRIMARY) --i; + if (h2->flags & HEAD_PRIMARY) ++i; + if (i) return i; + } + + if (h1->flags & HEAD_PRIMARY) --i; + if (h2->flags & HEAD_PRIMARY) ++i; + if (i) return i; + + return h1->monitor - h2->monitor; +} + +/*! Pick a monitor to place a window on. */ +static Rect *pick_head(ObClient *c, gboolean foreground, + ObAppSettings *settings) +{ + Rect *area; + ObPlaceHead *choice; + guint i; + ObClient *p; + GSList *it; + + choice = g_new(ObPlaceHead, screen_num_monitors); + for (i = 0; i < screen_num_monitors; ++i) { + choice[i].monitor = i; + choice[i].flags = 0; + } + + /* find monitors with group members */ + if (c->group) { + for (it = c->group->members; it; it = g_slist_next(it)) { + ObClient *itc = it->data; + if (itc != c) { + guint m = client_monitor(itc); + + if (m < screen_num_monitors) { + if (screen_compare_desktops(itc->desktop, c->desktop)) + choice[m].flags |= HEAD_GROUP_DESK; + else + choice[m].flags |= HEAD_GROUP; + } + } + } + } + + i = screen_monitor_primary(FALSE); + if (i < screen_num_monitors) { + choice[i].flags |= HEAD_PRIMARY; + if (config_place_monitor == OB_PLACE_MONITOR_PRIMARY) + choice[i].flags |= HEAD_PLACED; + if (settings && + settings->monitor_type == OB_PLACE_MONITOR_PRIMARY) + choice[i].flags |= HEAD_PERAPP; + } + + i = screen_monitor_active(); + if (i < screen_num_monitors) { + if (config_place_monitor == OB_PLACE_MONITOR_ACTIVE) + choice[i].flags |= HEAD_PLACED; + if (settings && + settings->monitor_type == OB_PLACE_MONITOR_ACTIVE) + choice[i].flags |= HEAD_PERAPP; + } + + i = screen_monitor_pointer(); + if (i < screen_num_monitors) { + if (config_place_monitor == OB_PLACE_MONITOR_MOUSE) + choice[i].flags |= HEAD_PLACED; + if (settings && + settings->monitor_type == OB_PLACE_MONITOR_MOUSE) + choice[i].flags |= HEAD_PERAPP; + } + + if (settings) { + i = settings->monitor - 1; + if (i < screen_num_monitors) + choice[i].flags |= HEAD_PERAPP; + } + + /* direct parent takes highest precedence */ + if ((p = client_direct_parent(c))) { + i = client_monitor(p); + if (i < screen_num_monitors) + choice[i].flags |= HEAD_PARENT; + } + + qsort(choice, screen_num_monitors, sizeof(ObPlaceHead), + foreground ? cmp_foreground : cmp_background); + + /* save the areas of the monitors in order of their being chosen */ + for (i = 0; i < screen_num_monitors; ++i) + { + ob_debug("placement choice %d is monitor %d", i, choice[i].monitor); + if (choice[i].flags & HEAD_PARENT) + ob_debug(" - parent on monitor"); + if (choice[i].flags & HEAD_PLACED) + ob_debug(" - placement choice"); + if (choice[i].flags & HEAD_PRIMARY) + ob_debug(" - primary monitor"); + if (choice[i].flags & HEAD_GROUP_DESK) + ob_debug(" - group on same desktop"); + if (choice[i].flags & HEAD_GROUP) + ob_debug(" - group on other desktop"); + } + + area = screen_area(c->desktop, choice[0].monitor, NULL); + + g_free(choice); + + /* return the area for the chosen monitor */ + return area; +} + +static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y) +{ + gint l, r, t, b; + + ob_debug("placing randomly"); + + l = area->x; + t = area->y; + r = area->x + area->width - client->frame->area.width; + b = area->y + area->height - client->frame->area.height; + + if (r > l) *x = g_random_int_range(l, r + 1); + else *x = area->x; + if (b > t) *y = g_random_int_range(t, b + 1); + else *y = area->y; + + return TRUE; +} + +static GSList* area_add(GSList *list, Rect *a) +{ + Rect *r = g_slice_new(Rect); + *r = *a; + return g_slist_prepend(list, r); +} + +static GSList* area_remove(GSList *list, Rect *a) +{ + GSList *sit; + GSList *result = NULL; + + for (sit = list; sit; sit = g_slist_next(sit)) { + Rect *r = sit->data; + + if (!RECT_INTERSECTS_RECT(*r, *a)) { + result = g_slist_prepend(result, r); + /* dont free r, it's moved to the result list */ + } else { + Rect isect, extra; + + /* Use an intersection of a and r to determine the space + around r that we can use. + + NOTE: the spaces calculated can overlap. + */ + + RECT_SET_INTERSECTION(isect, *r, *a); + + if (RECT_LEFT(isect) > RECT_LEFT(*r)) { + RECT_SET(extra, r->x, r->y, + RECT_LEFT(isect) - r->x, r->height); + result = area_add(result, &extra); + } + + if (RECT_TOP(isect) > RECT_TOP(*r)) { + RECT_SET(extra, r->x, r->y, + r->width, RECT_TOP(isect) - r->y + 1); + result = area_add(result, &extra); + } + + if (RECT_RIGHT(isect) < RECT_RIGHT(*r)) { + RECT_SET(extra, RECT_RIGHT(isect) + 1, r->y, + RECT_RIGHT(*r) - RECT_RIGHT(isect), r->height); + result = area_add(result, &extra); + } + + if (RECT_BOTTOM(isect) < RECT_BOTTOM(*r)) { + RECT_SET(extra, r->x, RECT_BOTTOM(isect) + 1, + r->width, RECT_BOTTOM(*r) - RECT_BOTTOM(isect)); + result = area_add(result, &extra); + } + + /* 'r' is not being added to the result list, so free it */ + g_slice_free(Rect, r); + } + } + g_slist_free(list); + return result; +} + +enum { + IGNORE_FULLSCREEN = 1, + IGNORE_MAXIMIZED = 2, + IGNORE_MENUTOOL = 3, + /*IGNORE_SHADED = 3,*/ + IGNORE_NONGROUP = 4, + IGNORE_BELOW = 5, + /*IGNORE_NONFOCUS = 1 << 5,*/ + IGNORE_DOCK = 6, + IGNORE_END = 7 +}; + +static gboolean place_nooverlap(ObClient *c, Rect *area, gint *x, gint *y) +{ + gint ignore; + gboolean ret; + gint maxsize; + GSList *spaces = NULL, *sit, *maxit; + + ob_debug("placing nonoverlap"); + + ret = FALSE; + maxsize = 0; + maxit = NULL; + + /* try ignoring different things to find empty space */ + for (ignore = 0; ignore < IGNORE_END && !ret; ignore++) { + GList *it; + + /* add the whole monitor */ + spaces = area_add(spaces, area); + + /* go thru all the windows */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *test = it->data; + + /* should we ignore this client? */ + if (screen_showing_desktop) continue; + if (c == test) continue; + if (test->iconic) continue; + if (c->desktop != DESKTOP_ALL) { + if (test->desktop != c->desktop && + test->desktop != DESKTOP_ALL) continue; + } else { + if (test->desktop != screen_desktop && + test->desktop != DESKTOP_ALL) continue; + } + if (test->type == OB_CLIENT_TYPE_SPLASH || + test->type == OB_CLIENT_TYPE_DESKTOP) continue; + + + if ((ignore >= IGNORE_FULLSCREEN) && + test->fullscreen) continue; + if ((ignore >= IGNORE_MAXIMIZED) && + test->max_horz && test->max_vert) continue; + if ((ignore >= IGNORE_MENUTOOL) && + (test->type == OB_CLIENT_TYPE_MENU || + test->type == OB_CLIENT_TYPE_TOOLBAR) && + client_has_parent(c)) continue; + /* + if ((ignore >= IGNORE_SHADED) && + test->shaded) continue; + */ + if ((ignore >= IGNORE_NONGROUP) && + client_has_group_siblings(c) && + test->group != c->group) continue; + if ((ignore >= IGNORE_BELOW) && + test->layer < c->layer) continue; + /* + if ((ignore >= IGNORE_NONFOCUS) && + focus_client != test) continue; + */ + /* don't ignore this window, so remove it from the available + area */ + spaces = area_remove(spaces, &test->frame->area); + } + + if (ignore < IGNORE_DOCK) { + Rect a; + dock_get_area(&a); + spaces = area_remove(spaces, &a); + } + + for (sit = spaces; sit; sit = g_slist_next(sit)) { + Rect *r = sit->data; + + if (r->width >= c->frame->area.width && + r->height >= c->frame->area.height && + r->width * r->height > maxsize) + { + maxsize = r->width * r->height; + maxit = sit; + } + } + + if (maxit) { + Rect *r = maxit->data; + + /* center it in the area */ + *x = r->x; + *y = r->y; + if (config_place_center) { + *x += (r->width - c->frame->area.width) / 2; + *y += (r->height - c->frame->area.height) / 2; + } + ret = TRUE; + } + + while (spaces) { + g_slice_free(Rect, spaces->data); + spaces = g_slist_delete_link(spaces, spaces); + } + } + + return ret; +} + +static gboolean place_under_mouse(ObClient *client, gint *x, gint *y) +{ + gint l, r, t, b; + gint px, py; + Rect *area; + + ob_debug("placing under mouse"); + + if (!screen_pointer_pos(&px, &py)) + return FALSE; + area = pick_pointer_head(client); + + l = area->x; + t = area->y; + r = area->x + area->width - client->frame->area.width; + b = area->y + area->height - client->frame->area.height; + + *x = px - client->area.width / 2 - client->frame->size.left; + *x = MIN(MAX(*x, l), r); + *y = py - client->area.height / 2 - client->frame->size.top; + *y = MIN(MAX(*y, t), b); + + g_slice_free(Rect, area); + + return TRUE; +} + +static gboolean place_per_app_setting(ObClient *client, Rect *screen, + gint *x, gint *y, + ObAppSettings *settings) +{ + if (!settings || (settings && !settings->pos_given)) + return FALSE; + + ob_debug("placing by per-app settings"); + + if (settings->position.x.center) + *x = screen->x + screen->width / 2 - client->area.width / 2; + else if (settings->position.x.opposite) + *x = screen->x + screen->width - client->frame->area.width - + settings->position.x.pos; + else + *x = screen->x + settings->position.x.pos; + if (settings->position.x.denom) + *x = (*x * screen->width) / settings->position.x.denom; + + if (settings->position.y.center) + *y = screen->y + screen->height / 2 - client->area.height / 2; + else if (settings->position.y.opposite) + *y = screen->y + screen->height - client->frame->area.height - + settings->position.y.pos; + else + *y = screen->y + settings->position.y.pos; + if (settings->position.y.denom) + *y = (*y * screen->height) / settings->position.y.denom; + + return TRUE; +} + +static gboolean place_transient_splash(ObClient *client, Rect *area, + gint *x, gint *y) +{ + if (client->type == OB_CLIENT_TYPE_DIALOG) { + GSList *it; + gboolean first = TRUE; + gint l, r, t, b; + + ob_debug("placing dialog"); + + for (it = client->parents; it; it = g_slist_next(it)) { + ObClient *m = it->data; + if (!m->iconic) { + if (first) { + l = RECT_LEFT(m->frame->area); + t = RECT_TOP(m->frame->area); + r = RECT_RIGHT(m->frame->area); + b = RECT_BOTTOM(m->frame->area); + first = FALSE; + } else { + l = MIN(l, RECT_LEFT(m->frame->area)); + t = MIN(t, RECT_TOP(m->frame->area)); + r = MAX(r, RECT_RIGHT(m->frame->area)); + b = MAX(b, RECT_BOTTOM(m->frame->area)); + } + } + if (!first) { + *x = ((r + 1 - l) - client->frame->area.width) / 2 + l; + *y = ((b + 1 - t) - client->frame->area.height) / 2 + t; + return TRUE; + } + } + } + + if (client->type == OB_CLIENT_TYPE_DIALOG || + client->type == OB_CLIENT_TYPE_SPLASH) + { + ob_debug("placing dialog or splash"); + + *x = (area->width - client->frame->area.width) / 2 + area->x; + *y = (area->height - client->frame->area.height) / 2 + area->y; + return TRUE; + } + + return FALSE; +} + +/*! Return TRUE if openbox chose the position for the window, and FALSE if + the application chose it */ +gboolean place_client(ObClient *client, gboolean foreground, gint *x, gint *y, + ObAppSettings *settings) +{ + Rect *area; + gboolean ret; + + /* per-app settings override program specified position + * but not user specified, unless pos_force is enabled */ + if (((client->positioned & USPosition) && + !(settings && settings->pos_given && settings->pos_force)) || + ((client->positioned & PPosition) && + !(settings && settings->pos_given))) + return FALSE; + + area = pick_head(client, foreground, settings); + + /* try a number of methods */ + ret = place_per_app_setting(client, area, x, y, settings) || + place_transient_splash(client, area, x, y) || + (config_place_policy == OB_PLACE_POLICY_MOUSE && + place_under_mouse(client, x, y)) || + place_nooverlap(client, area, x, y) || + place_random(client, area, x, y); + g_assert(ret); + + g_slice_free(Rect, area); + + /* get where the client should be */ + frame_frame_gravity(client->frame, x, y); + return TRUE; +} diff --git a/openbox/place.h b/openbox/place.h new file mode 100644 index 0000000..94e2dc0 --- /dev/null +++ b/openbox/place.h @@ -0,0 +1,45 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + place.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__place_h +#define ob__place_h + +#include <glib.h> + +struct _ObClient; +struct _ObAppSettings; + +typedef enum +{ + OB_PLACE_POLICY_SMART, + OB_PLACE_POLICY_MOUSE +} ObPlacePolicy; + +typedef enum +{ + OB_PLACE_MONITOR_ANY, + OB_PLACE_MONITOR_ACTIVE, + OB_PLACE_MONITOR_MOUSE, + OB_PLACE_MONITOR_PRIMARY +} ObPlaceMonitor; + +gboolean place_client(struct _ObClient *client, gboolean foreground, + gint *x, gint *y, struct _ObAppSettings *settings); + +#endif diff --git a/openbox/popup.c b/openbox/popup.c new file mode 100644 index 0000000..2a83d7b --- /dev/null +++ b/openbox/popup.c @@ -0,0 +1,569 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + popup.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "popup.h" + +#include "openbox.h" +#include "frame.h" +#include "client.h" +#include "stacking.h" +#include "event.h" +#include "screen.h" +#include "obrender/render.h" +#include "obrender/theme.h" + +ObPopup *popup_new(void) +{ + XSetWindowAttributes attrib; + ObPopup *self = g_slice_new0(ObPopup); + + self->obwin.type = OB_WINDOW_CLASS_INTERNAL; + self->gravity = NorthWestGravity; + self->x = self->y = self->textw = self->h = 0; + self->a_bg = RrAppearanceCopy(ob_rr_theme->osd_bg); + self->a_text = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); + self->iconwm = self->iconhm = 1; + + attrib.override_redirect = True; + self->bg = XCreateWindow(obt_display, obt_root(ob_screen), + 0, 0, 1, 1, 0, RrDepth(ob_rr_inst), + InputOutput, RrVisual(ob_rr_inst), + CWOverrideRedirect, &attrib); + + self->text = XCreateWindow(obt_display, self->bg, + 0, 0, 1, 1, 0, RrDepth(ob_rr_inst), + InputOutput, RrVisual(ob_rr_inst), 0, NULL); + + XSetWindowBorderWidth(obt_display, self->bg, ob_rr_theme->obwidth); + XSetWindowBorder(obt_display, self->bg, + RrColorPixel(ob_rr_theme->osd_border_color)); + + XMapWindow(obt_display, self->text); + + stacking_add(INTERNAL_AS_WINDOW(self)); + window_add(&self->bg, INTERNAL_AS_WINDOW(self)); + return self; +} + +void popup_free(ObPopup *self) +{ + if (self) { + popup_hide(self); /* make sure it's not showing or is being delayed and + will be shown */ + XDestroyWindow(obt_display, self->bg); + XDestroyWindow(obt_display, self->text); + RrAppearanceFree(self->a_bg); + RrAppearanceFree(self->a_text); + window_remove(self->bg); + stacking_remove(self); + g_slice_free(ObPopup, self); + } +} + +void popup_position(ObPopup *self, gint gravity, gint x, gint y) +{ + self->gravity = gravity; + self->x = x; + self->y = y; +} + +void popup_text_width(ObPopup *self, gint w) +{ + self->textw = w; +} + +void popup_min_width(ObPopup *self, gint minw) +{ + self->minw = minw; +} + +void popup_max_width(ObPopup *self, gint maxw) +{ + self->maxw = maxw; +} + +void popup_height(ObPopup *self, gint h) +{ + gint texth; + + /* don't let the height be smaller than the text */ + texth = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2; + self->h = MAX(h, texth); +} + +void popup_text_width_to_string(ObPopup *self, gchar *text) +{ + if (text[0] != '\0') { + self->a_text->texture[0].data.text.string = text; + self->textw = RrMinWidth(self->a_text); + } else + self->textw = 0; +} + +void popup_height_to_string(ObPopup *self, gchar *text) +{ + self->h = RrMinHeight(self->a_text) + ob_rr_theme->paddingy * 2; +} + +void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num) +{ + gint i, maxw; + + maxw = 0; + for (i = 0; i < num; ++i) { + popup_text_width_to_string(self, strings[i]); + maxw = MAX(maxw, self->textw); + } + self->textw = maxw; +} + +void popup_set_text_align(ObPopup *self, RrJustify align) +{ + self->a_text->texture[0].data.text.justify = align; +} + +static gboolean popup_show_timeout(gpointer data) +{ + ObPopup *self = data; + + XMapWindow(obt_display, self->bg); + stacking_raise(INTERNAL_AS_WINDOW(self)); + self->mapped = TRUE; + self->delay_mapped = FALSE; + self->delay_timer = 0; + + return FALSE; /* don't repeat */ +} + +void popup_delay_show(ObPopup *self, gulong msec, gchar *text) +{ + gint l, t, r, b; + gint x, y, w, h; + guint m; + gint emptyx, emptyy; /* empty space between elements */ + gint textx, texty, textw, texth; + gint iconx, icony, iconw, iconh; + const Rect *area; + Rect mon; + gboolean hasicon = self->hasicon; + + /* when there is no icon and the text is not parent relative, then + fill the whole dialog with the text appearance, don't use the bg at all + */ + if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL) + RrMargins(self->a_bg, &l, &t, &r, &b); + else + l = t = r = b = 0; + + /* set up the textures */ + self->a_text->texture[0].data.text.string = text; + + /* measure the text out */ + if (text[0] != '\0') { + RrMinSize(self->a_text, &textw, &texth); + } else { + textw = 0; + texth = RrMinHeight(self->a_text); + } + + /* get the height, which is also used for the icon width */ + emptyy = t + b + ob_rr_theme->paddingy * 2; + if (self->h) + texth = self->h - emptyy; + h = texth * self->iconhm + emptyy; + + if (self->textw) + textw = self->textw; + + iconx = textx = l + ob_rr_theme->paddingx; + + emptyx = l + r + ob_rr_theme->paddingx * 2; + if (hasicon) { + iconw = texth * self->iconwm; + iconh = texth * self->iconhm; + textx += iconw + ob_rr_theme->paddingx; + if (textw) + emptyx += ob_rr_theme->paddingx; /* between the icon and text */ + icony = (h - iconh - emptyy) / 2 + t + ob_rr_theme->paddingy; + } else + iconw = 0; + + texty = (h - texth - emptyy) / 2 + t + ob_rr_theme->paddingy; + + /* when there is no icon, then fill the whole dialog with the text + appearance + */ + if (!hasicon) + { + textx = texty = 0; + texth += emptyy; + textw += emptyx; + emptyx = emptyy = 0; + } + + w = textw + emptyx + iconw; + /* cap it at maxw/minw */ + if (self->maxw) w = MIN(w, self->maxw); + if (self->minw) w = MAX(w, self->minw); + textw = w - emptyx - iconw; + + /* sanity checks to avoid crashes! */ + if (w < 1) w = 1; + if (h < 1) h = 1; + if (texth < 1) texth = 1; + + /* set up the x coord */ + x = self->x; + switch (self->gravity) { + case NorthGravity: case CenterGravity: case SouthGravity: + x -= w / 2; + break; + case NorthEastGravity: case EastGravity: case SouthEastGravity: + x -= w; + break; + } + + /* set up the y coord */ + y = self->y; + switch (self->gravity) { + case WestGravity: case CenterGravity: case EastGravity: + y -= h / 2; + break; + case SouthWestGravity: case SouthGravity: case SouthEastGravity: + y -= h; + break; + } + + /* Find the monitor which contains the biggest part of the popup. + * If the popup is completely off screen, limit it to the intersection + * of all monitors and then try again. If it's still off screen, put it + * on monitor 0. */ + RECT_SET(mon, x, y, w, h); + m = screen_find_monitor(&mon); + area = screen_physical_area_monitor(m); + + x=MAX(MIN(x, area->x+area->width-w),area->x); + y=MAX(MIN(y, area->y+area->height-h),area->y); + + if (m == screen_num_monitors) { + RECT_SET(mon, x, y, w, h); + m = screen_find_monitor(&mon); + if (m == screen_num_monitors) + m = 0; + area = screen_physical_area_monitor(m); + + x=MAX(MIN(x, area->x+area->width-w),area->x); + y=MAX(MIN(y, area->y+area->height-h),area->y); + } + + /* set the windows/appearances up */ + XMoveResizeWindow(obt_display, self->bg, x, y, w, h); + /* when there is no icon and the text is not parent relative, then + fill the whole dialog with the text appearance, don't use the bg at all + */ + if (hasicon || self->a_text->surface.grad == RR_SURFACE_PARENTREL) + RrPaint(self->a_bg, self->bg, w, h); + + if (textw) { + self->a_text->surface.parent = self->a_bg; + self->a_text->surface.parentx = textx; + self->a_text->surface.parenty = texty; + XMoveResizeWindow(obt_display, self->text, textx, texty, textw, texth); + RrPaint(self->a_text, self->text, textw, texth); + } + + if (hasicon) + self->draw_icon(iconx, icony, iconw, iconh, self->draw_icon_data); + + /* do the actual showing */ + if (!self->mapped) { + if (msec) { + /* don't kill previous show timers */ + if (!self->delay_mapped) { + self->delay_timer = + g_timeout_add(msec, popup_show_timeout, self); + self->delay_mapped = TRUE; + } + } else { + popup_show_timeout(self); + } + } +} + +void popup_hide(ObPopup *self) +{ + if (self->mapped) { + gulong ignore_start; + + /* kill enter events cause by this unmapping */ + ignore_start = event_start_ignore_all_enters(); + + XUnmapWindow(obt_display, self->bg); + self->mapped = FALSE; + + event_end_ignore_all_enters(ignore_start); + } else if (self->delay_mapped) { + g_source_remove(self->delay_timer); + self->delay_timer = 0; + self->delay_mapped = FALSE; + } +} + +static void icon_popup_draw_icon(gint x, gint y, gint w, gint h, gpointer data) +{ + ObIconPopup *self = data; + + self->a_icon->surface.parent = self->popup->a_bg; + self->a_icon->surface.parentx = x; + self->a_icon->surface.parenty = y; + XMoveResizeWindow(obt_display, self->icon, x, y, w, h); + RrPaint(self->a_icon, self->icon, w, h); +} + +ObIconPopup *icon_popup_new(void) +{ + ObIconPopup *self; + + self = g_slice_new0(ObIconPopup); + self->popup = popup_new(); + self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex); + self->icon = XCreateWindow(obt_display, self->popup->bg, + 0, 0, 1, 1, 0, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), 0, NULL); + XMapWindow(obt_display, self->icon); + + self->popup->hasicon = TRUE; + self->popup->draw_icon = icon_popup_draw_icon; + self->popup->draw_icon_data = self; + + return self; +} + +void icon_popup_free(ObIconPopup *self) +{ + if (self) { + XDestroyWindow(obt_display, self->icon); + RrAppearanceFree(self->a_icon); + popup_free(self->popup); + g_slice_free(ObIconPopup, self); + } +} + +void icon_popup_delay_show(ObIconPopup *self, gulong msec, + gchar *text, RrImage *icon) +{ + if (icon) { + RrAppearanceClearTextures(self->a_icon); + self->a_icon->texture[0].type = RR_TEXTURE_IMAGE; + self->a_icon->texture[0].data.image.alpha = 0xff; + self->a_icon->texture[0].data.image.image = icon; + } else { + RrAppearanceClearTextures(self->a_icon); + self->a_icon->texture[0].type = RR_TEXTURE_NONE; + } + + popup_delay_show(self->popup, msec, text); +} + +void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm) +{ + /* cap them at 1 */ + self->popup->iconwm = MAX(1, wm); + self->popup->iconhm = MAX(1, hm); +} + +static void pager_popup_draw_icon(gint px, gint py, gint w, gint h, + gpointer data) +{ + ObPagerPopup *self = data; + gint x, y; + guint rown, n; + guint horz_inc; + guint vert_inc; + guint r, c; + gint eachw, eachh; + const guint cols = screen_desktop_layout.columns; + const guint rows = screen_desktop_layout.rows; + const gint linewidth = ob_rr_theme->obwidth; + + eachw = (w - ((cols + 1) * linewidth)) / cols; + eachh = (h - ((rows + 1) * linewidth)) / rows; + /* make them squares */ + eachw = eachh = MIN(eachw, eachh); + + /* center */ + px += (w - (cols * (eachw + linewidth) + linewidth)) / 2; + py += (h - (rows * (eachh + linewidth) + linewidth)) / 2; + + if (eachw <= 0 || eachh <= 0) + return; + + switch (screen_desktop_layout.orientation) { + case OB_ORIENTATION_HORZ: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + n = 0; + horz_inc = 1; + vert_inc = cols; + break; + case OB_CORNER_TOPRIGHT: + n = cols - 1; + horz_inc = -1; + vert_inc = cols; + break; + case OB_CORNER_BOTTOMRIGHT: + n = rows * cols - 1; + horz_inc = -1; + vert_inc = -screen_desktop_layout.columns; + break; + case OB_CORNER_BOTTOMLEFT: + n = (rows - 1) * cols; + horz_inc = 1; + vert_inc = -cols; + break; + default: + g_assert_not_reached(); + } + break; + case OB_ORIENTATION_VERT: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + n = 0; + horz_inc = rows; + vert_inc = 1; + break; + case OB_CORNER_TOPRIGHT: + n = rows * (cols - 1); + horz_inc = -rows; + vert_inc = 1; + break; + case OB_CORNER_BOTTOMRIGHT: + n = rows * cols - 1; + horz_inc = -rows; + vert_inc = -1; + break; + case OB_CORNER_BOTTOMLEFT: + n = rows - 1; + horz_inc = rows; + vert_inc = -1; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } + + rown = n; + for (r = 0, y = 0; r < rows; ++r, y += eachh + linewidth) + { + for (c = 0, x = 0; c < cols; ++c, x += eachw + linewidth) + { + RrAppearance *a; + + if (n < self->desks) { + a = (n == self->curdesk ? self->hilight : self->unhilight); + + a->surface.parent = self->popup->a_bg; + a->surface.parentx = x + px; + a->surface.parenty = y + py; + XMoveResizeWindow(obt_display, self->wins[n], + x + px, y + py, eachw, eachh); + RrPaint(a, self->wins[n], eachw, eachh); + } + n += horz_inc; + } + n = rown += vert_inc; + } +} + +ObPagerPopup *pager_popup_new(void) +{ + ObPagerPopup *self; + + self = g_slice_new(ObPagerPopup); + self->popup = popup_new(); + + self->desks = 0; + self->wins = g_new(Window, self->desks); + self->hilight = RrAppearanceCopy(ob_rr_theme->osd_hilite_bg); + self->unhilight = RrAppearanceCopy(ob_rr_theme->osd_unhilite_bg); + + self->popup->hasicon = TRUE; + self->popup->draw_icon = pager_popup_draw_icon; + self->popup->draw_icon_data = self; + + return self; +} + +void pager_popup_free(ObPagerPopup *self) +{ + if (self) { + guint i; + + for (i = 0; i < self->desks; ++i) + XDestroyWindow(obt_display, self->wins[i]); + g_free(self->wins); + RrAppearanceFree(self->hilight); + RrAppearanceFree(self->unhilight); + popup_free(self->popup); + g_slice_free(ObPagerPopup, self); + } +} + +void pager_popup_delay_show(ObPagerPopup *self, gulong msec, + gchar *text, guint desk) +{ + guint i; + + if (screen_num_desktops < self->desks) + for (i = screen_num_desktops; i < self->desks; ++i) + XDestroyWindow(obt_display, self->wins[i]); + + if (screen_num_desktops != self->desks) + self->wins = g_renew(Window, self->wins, screen_num_desktops); + + if (screen_num_desktops > self->desks) + for (i = self->desks; i < screen_num_desktops; ++i) { + XSetWindowAttributes attr; + + attr.border_pixel = + RrColorPixel(ob_rr_theme->osd_border_color); + self->wins[i] = XCreateWindow(obt_display, self->popup->bg, + 0, 0, 1, 1, ob_rr_theme->obwidth, + RrDepth(ob_rr_inst), InputOutput, + RrVisual(ob_rr_inst), CWBorderPixel, + &attr); + XMapWindow(obt_display, self->wins[i]); + } + + self->desks = screen_num_desktops; + self->curdesk = desk; + + popup_delay_show(self->popup, msec, text); +} + +void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm) +{ + /* cap them at 1 */ + self->popup->iconwm = MAX(1, wm); + self->popup->iconhm = MAX(1, hm); +} diff --git a/openbox/popup.h b/openbox/popup.h new file mode 100644 index 0000000..6de9d18 --- /dev/null +++ b/openbox/popup.h @@ -0,0 +1,147 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + popup.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __popup_h +#define __popup_h + +#include "window.h" +#include "obrender/render.h" +#include <glib.h> + +struct _ObClientIcon; + +#define POPUP_WIDTH 320 +#define POPUP_HEIGHT 48 + +typedef struct _ObPopup ObPopup; +typedef struct _ObIconPopup ObIconPopup; +typedef struct _ObPagerPopup ObPagerPopup; + +struct _ObPopup +{ + ObWindow obwin; + Window bg; + + Window text; + + gboolean hasicon; + RrAppearance *a_bg; + RrAppearance *a_text; + gint gravity; + gint x; + gint y; + gint textw; + gint h; + gint minw; + gint maxw; + guint iconwm; /* icon width multiplier. multiplied by the normal width */ + guint iconhm; /* icon height multiplier. multipled by the normal height */ + gboolean mapped; + gboolean delay_mapped; + guint delay_timer; + + void (*draw_icon)(gint x, gint y, gint w, gint h, gpointer data); + gpointer draw_icon_data; +}; + +struct _ObIconPopup +{ + ObPopup *popup; + + Window icon; + RrAppearance *a_icon; +}; + +struct _ObPagerPopup +{ + ObPopup *popup; + + guint desks; + guint curdesk; + Window *wins; + RrAppearance *hilight; + RrAppearance *unhilight; +}; + +ObPopup *popup_new(void); +void popup_free(ObPopup *self); + +/*! Position the popup. The gravity rules are not the same X uses for windows, + instead of the position being the top-left of the window, the gravity + specifies which corner of the popup will be placed at the given coords. + Static and Forget gravity are equivilent to NorthWest. +*/ +void popup_position(ObPopup *self, gint gravity, gint x, gint y); +/*! Set the sizes for the popup. When set to 0, the size will be based on + the text size. */ +void popup_height(ObPopup *self, gint w); +void popup_min_width(ObPopup *self, gint minw); +void popup_max_width(ObPopup *self, gint maxw); +void popup_text_width(ObPopup *self, gint w); +void popup_text_width_to_string(ObPopup *self, gchar *text); +void popup_height_to_string(ObPopup *self, gchar *text); +void popup_text_width_to_strings(ObPopup *self, gchar **strings, gint num); + +void popup_set_text_align(ObPopup *self, RrJustify align); + +#define popup_show(s, t) popup_delay_show((s),0,(t)) +void popup_delay_show(ObPopup *self, gulong msec, gchar *text); +void popup_hide(ObPopup *self); + +RrAppearance *popup_icon_appearance(ObPopup *self); + + +ObIconPopup *icon_popup_new(void); +void icon_popup_free(ObIconPopup *self); + +#define icon_popup_show(s, t, i) icon_popup_delay_show((s),0,(t),(i)) +void icon_popup_delay_show(ObIconPopup *self, gulong msec, + gchar *text, RrImage *icon); +#define icon_popup_hide(p) popup_hide((p)->popup) +#define icon_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y)) +#define icon_popup_text_width(p, w) popup_text_width((p)->popup,(w)) +#define icon_popup_height(p, h) popup_height((p)->popup,(h)) +#define icon_popup_min_width(p, m) popup_min_width((p)->popup,(m)) +#define icon_popup_max_width(p, m) popup_max_width((p)->popup,(m)) +#define icon_popup_text_width_to_string(p, s) \ + popup_text_width_to_string((p)->popup,(s)) +#define icon_popup_text_width_to_strings(p, s, n) \ + popup_text_width_to_strings((p)->popup,(s),(n)) +#define icon_popup_set_text_align(p, j) popup_set_text_align((p)->popup,(j)) +void icon_popup_icon_size_multiplier(ObIconPopup *self, guint wm, guint hm); + +ObPagerPopup *pager_popup_new(void); +void pager_popup_free(ObPagerPopup *self); + +#define pager_popup_show(s, t, d) pager_popup_delay_show((s),0,(t),(d)) +void pager_popup_delay_show(ObPagerPopup *self, gulong msec, + gchar *text, guint desk); +#define pager_popup_hide(p) popup_hide((p)->popup) +#define pager_popup_position(p, g, x, y) popup_position((p)->popup,(g),(x),(y)) +#define pager_popup_text_width(p, w) popup_text_width((p)->popup,(w)) +#define pager_popup_height(p, h) popup_height((p)->popup,(h)) +#define pager_popup_min_width(p, m) popup_min_width((p)->popup,(m)) +#define pager_popup_max_width(p, m) popup_max_width((p)->popup,(m)) +#define pager_popup_text_width_to_string(p, s) \ + popup_text_width_to_string((p)->popup,(s)) +#define pager_popup_text_width_to_strings(p, s, n) \ + popup_text_width_to_strings((p)->popup,(s),(n)) +#define pager_popup_set_text_align(p, j) popup_set_text_align((p)->popup,(j)) +void pager_popup_icon_size_multiplier(ObPagerPopup *self, guint wm, guint hm); + +#endif diff --git a/openbox/prompt.c b/openbox/prompt.c new file mode 100644 index 0000000..1aa79d2 --- /dev/null +++ b/openbox/prompt.c @@ -0,0 +1,603 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + prompt.c for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "prompt.h" +#include "openbox.h" +#include "screen.h" +#include "client.h" +#include "group.h" +#include "event.h" +#include "obt/display.h" +#include "obt/keyboard.h" +#include "obt/prop.h" +#include "gettext.h" + +static GList *prompt_list = NULL; + +/* we construct these */ +static RrAppearance *prompt_a_bg; +static RrAppearance *prompt_a_button; +static RrAppearance *prompt_a_focus; +static RrAppearance *prompt_a_press; +/* we change the max width which would screw with others */ +static RrAppearance *prompt_a_msg; + +/* sizing stuff */ +#define OUTSIDE_MARGIN 4 +#define MSG_BUTTON_SEPARATION 4 +#define BUTTON_SEPARATION 4 +#define BUTTON_VMARGIN 4 +#define BUTTON_HMARGIN 12 +#define MAX_WIDTH 400 + +static void prompt_layout(ObPrompt *self); +static void render_all(ObPrompt *self); +static void render_button(ObPrompt *self, ObPromptElement *e); +static void prompt_resize(ObPrompt *self, gint w, gint h); +static void prompt_run_callback(ObPrompt *self, gint result); + +void prompt_startup(gboolean reconfig) +{ + /* note: this is not a copy, don't free it */ + prompt_a_bg = ob_rr_theme->osd_bg; + + prompt_a_button = RrAppearanceCopy(ob_rr_theme->osd_unpressed_button); + prompt_a_focus = RrAppearanceCopy(ob_rr_theme->osd_focused_button); + prompt_a_press = RrAppearanceCopy(ob_rr_theme->osd_pressed_button); + + prompt_a_msg = RrAppearanceCopy(ob_rr_theme->osd_hilite_label); + prompt_a_msg->texture[0].data.text.flow = TRUE; + + if (reconfig) { + GList *it; + for (it = prompt_list; it; it = g_list_next(it)) { + ObPrompt *p = it->data; + prompt_layout(p); + render_all(p); + } + } +} + +void prompt_shutdown(gboolean reconfig) +{ + GList *it; + + if (!reconfig) { + for (it = prompt_list; it; it = g_list_next(it)) { + ObPrompt *p = it->data; + if (p->cleanup) p->cleanup(p, p->data); + } + + g_assert(prompt_list == NULL); + } + + RrAppearanceFree(prompt_a_button); + RrAppearanceFree(prompt_a_focus); + RrAppearanceFree(prompt_a_press); + RrAppearanceFree(prompt_a_msg); +} + +ObPrompt* prompt_new(const gchar *msg, const gchar *title, + const ObPromptAnswer *answers, gint n_answers, + gint default_result, gint cancel_result, + ObPromptCallback func, ObPromptCleanup cleanup, + gpointer data) +{ + ObPrompt *self; + XSetWindowAttributes attrib; + gint i; + + attrib.override_redirect = FALSE; + + self = g_slice_new0(ObPrompt); + self->ref = 1; + self->func = func; + self->cleanup = cleanup; + self->data = data; + self->default_result = default_result; + self->cancel_result = cancel_result; + self->super.type = OB_WINDOW_CLASS_PROMPT; + self->super.window = XCreateWindow(obt_display, obt_root(ob_screen), + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, + CWOverrideRedirect, + &attrib); + self->ic = obt_keyboard_context_new(self->super.window, + self->super.window); + + /* make it a dialog type window */ + OBT_PROP_SET32(self->super.window, NET_WM_WINDOW_TYPE, ATOM, + OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG)); + + /* set the window's title */ + if (title) + OBT_PROP_SETS(self->super.window, NET_WM_NAME, title); + + /* listen for key presses on the window */ + self->event_mask = KeyPressMask; + + /* set up the text message widow */ + self->msg.text = g_strdup(msg); + self->msg.window = XCreateWindow(obt_display, self->super.window, + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + XMapWindow(obt_display, self->msg.window); + + /* set up the buttons from the answers */ + + self->n_buttons = n_answers; + if (!self->n_buttons) + self->n_buttons = 1; + + self->button = g_new0(ObPromptElement, self->n_buttons); + + if (n_answers == 0) { + g_assert(self->n_buttons == 1); /* should be set to this above.. */ + self->button[0].text = g_strdup(_("OK")); + } + else { + g_assert(self->n_buttons > 0); + for (i = 0; i < self->n_buttons; ++i) { + self->button[i].text = g_strdup(answers[i].text); + self->button[i].result = answers[i].result; + } + } + + for (i = 0; i < self->n_buttons; ++i) { + self->button[i].window = XCreateWindow(obt_display, self->super.window, + 0, 0, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, 0, NULL); + XMapWindow(obt_display, self->button[i].window); + window_add(&self->button[i].window, PROMPT_AS_WINDOW(self)); + + /* listen for button presses on the buttons */ + XSelectInput(obt_display, self->button[i].window, + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask); + } + + prompt_list = g_list_prepend(prompt_list, self); + + return self; +} + +void prompt_ref(ObPrompt *self) +{ + ++self->ref; +} + +void prompt_unref(ObPrompt *self) +{ + if (self && --self->ref == 0) { + gint i; + + if (self->mapped) + prompt_hide(self); + + prompt_list = g_list_remove(prompt_list, self); + + obt_keyboard_context_unref(self->ic); + + for (i = 0; i < self->n_buttons; ++i) { + window_remove(self->button[i].window); + XDestroyWindow(obt_display, self->button[i].window); + } + + XDestroyWindow(obt_display, self->msg.window); + XDestroyWindow(obt_display, self->super.window); + g_slice_free(ObPrompt, self); + } +} + +static void prompt_layout(ObPrompt *self) +{ + gint l, r, t, b; + gint i; + gint allbuttonsw, allbuttonsh, buttonx; + gint w, h; + gint maxw; + + RrMargins(prompt_a_bg, &l, &t, &r, &b); + l += OUTSIDE_MARGIN; + t += OUTSIDE_MARGIN; + r += OUTSIDE_MARGIN; + b += OUTSIDE_MARGIN; + + { + const Rect *area = screen_physical_area_all_monitors(); + maxw = MIN(MAX_WIDTH, area->width*4/5); + } + + /* find the button sizes and how much space we need for them */ + allbuttonsw = allbuttonsh = 0; + for (i = 0; i < self->n_buttons; ++i) { + gint bw, bh; + + prompt_a_button->texture[0].data.text.string = self->button[i].text; + prompt_a_focus->texture[0].data.text.string = self->button[i].text; + prompt_a_press->texture[0].data.text.string = self->button[i].text; + RrMinSize(prompt_a_button, &bw, &bh); + self->button[i].width = bw; + self->button[i].height = bh; + RrMinSize(prompt_a_focus, &bw, &bh); + self->button[i].width = MAX(self->button[i].width, bw); + self->button[i].height = MAX(self->button[i].height, bh); + RrMinSize(prompt_a_press, &bw, &bh); + self->button[i].width = MAX(self->button[i].width, bw); + self->button[i].height = MAX(self->button[i].height, bh); + + self->button[i].width += BUTTON_HMARGIN * 2; + self->button[i].height += BUTTON_VMARGIN * 2; + + allbuttonsw += self->button[i].width + (i > 0 ? BUTTON_SEPARATION : 0); + allbuttonsh = MAX(allbuttonsh, self->button[i].height); + } + + self->msg_wbound = MAX(allbuttonsw, maxw); + + /* measure the text message area */ + prompt_a_msg->texture[0].data.text.string = self->msg.text; + prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; + RrMinSize(prompt_a_msg, &self->msg.width, &self->msg.height); + + /* width and height inside the outer margins */ + w = MAX(self->msg.width, allbuttonsw); + h = self->msg.height + MSG_BUTTON_SEPARATION + allbuttonsh; + + /* position the text message */ + self->msg.x = l + (w - self->msg.width) / 2; + self->msg.y = t; + + /* position the button buttons on the right of the dialog */ + buttonx = l + w; + for (i = self->n_buttons - 1; i >= 0; --i) { + self->button[i].x = buttonx - self->button[i].width; + buttonx -= self->button[i].width + BUTTON_SEPARATION; + self->button[i].y = t + h - allbuttonsh; + self->button[i].y += (allbuttonsh - self->button[i].height) / 2; + } + + /* size and position the toplevel window */ + prompt_resize(self, w + l + r, h + t + b); + + /* move and resize the internal windows */ + XMoveResizeWindow(obt_display, self->msg.window, + self->msg.x, self->msg.y, + self->msg.width, self->msg.height); + for (i = 0; i < self->n_buttons; ++i) + XMoveResizeWindow(obt_display, self->button[i].window, + self->button[i].x, self->button[i].y, + self->button[i].width, self->button[i].height); +} + +static void prompt_resize(ObPrompt *self, gint w, gint h) +{ + XConfigureRequestEvent req; + XSizeHints hints; + + self->width = w; + self->height = h; + + /* the user can't resize the prompt */ + hints.flags = PMinSize | PMaxSize; + hints.min_width = hints.max_width = w; + hints.min_height = hints.max_height = h; + XSetWMNormalHints(obt_display, self->super.window, &hints); + + if (self->mapped) { + /* send a configure request like a normal client would */ + req.type = ConfigureRequest; + req.display = obt_display; + req.parent = obt_root(ob_screen); + req.window = self->super.window; + req.width = w; + req.height = h; + req.value_mask = CWWidth | CWHeight; + XSendEvent(req.display, req.window, FALSE, StructureNotifyMask, + (XEvent*)&req); + } + else + XResizeWindow(obt_display, self->super.window, w, h); +} + +static void setup_button_focus_tex(ObPromptElement *e, RrAppearance *a, + gboolean on) +{ + gint i, l, r, t, b; + + for (i = 1; i < 5; ++i) + a->texture[i].type = on ? RR_TEXTURE_LINE_ART : RR_TEXTURE_NONE; + + if (!on) return; + + RrMargins(a, &l, &t, &r, &b); + l += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + r += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + t += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + b += MIN(BUTTON_HMARGIN, BUTTON_VMARGIN) / 2; + + /* top line */ + a->texture[1].data.lineart.x1 = l; + a->texture[1].data.lineart.x2 = e->width - r - 1; + a->texture[1].data.lineart.y1 = t; + a->texture[1].data.lineart.y2 = t; + + /* bottom line */ + a->texture[2].data.lineart.x1 = l; + a->texture[2].data.lineart.x2 = e->width - r - 1; + a->texture[2].data.lineart.y1 = e->height - b - 1; + a->texture[2].data.lineart.y2 = e->height - b - 1; + + /* left line */ + a->texture[3].data.lineart.x1 = l; + a->texture[3].data.lineart.x2 = l; + a->texture[3].data.lineart.y1 = t; + a->texture[3].data.lineart.y2 = e->height - b - 1; + + /* right line */ + a->texture[4].data.lineart.x1 = e->width - r - 1; + a->texture[4].data.lineart.x2 = e->width - r - 1; + a->texture[4].data.lineart.y1 = t; + a->texture[4].data.lineart.y2 = e->height - b - 1; +} + +static void render_button(ObPrompt *self, ObPromptElement *e) +{ + RrAppearance *a; + + if (e->hover && e->pressed) a = prompt_a_press; + else if (self->focus == e) a = prompt_a_focus; + else a = prompt_a_button; + + a->surface.parent = prompt_a_bg; + a->surface.parentx = e->x; + a->surface.parenty = e->y; + + /* draw the keyfocus line */ + if (self->focus == e) + setup_button_focus_tex(e, a, TRUE); + + a->texture[0].data.text.string = e->text; + RrPaint(a, e->window, e->width, e->height); + + /* turn off the keyfocus line so that it doesn't affect size calculations + */ + if (self->focus == e) + setup_button_focus_tex(e, a, FALSE); +} + +static void render_all(ObPrompt *self) +{ + gint i; + + RrPaint(prompt_a_bg, self->super.window, self->width, self->height); + + prompt_a_msg->surface.parent = prompt_a_bg; + prompt_a_msg->surface.parentx = self->msg.x; + prompt_a_msg->surface.parenty = self->msg.y; + + prompt_a_msg->texture[0].data.text.string = self->msg.text; + prompt_a_msg->texture[0].data.text.maxwidth = self->msg_wbound; + RrPaint(prompt_a_msg, self->msg.window, self->msg.width, self->msg.height); + + for (i = 0; i < self->n_buttons; ++i) + render_button(self, &self->button[i]); +} + +void prompt_show(ObPrompt *self, ObClient *parent, gboolean modal) +{ + gint i; + + if (self->mapped) { + /* activate the prompt */ + OBT_PROP_MSG(ob_screen, self->super.window, NET_ACTIVE_WINDOW, + 1, /* from an application.. */ + event_time(), + 0, + 0, 0); + return; + } + + /* set the focused button (if not found then the first button is used) */ + self->focus = &self->button[0]; + for (i = 0; i < self->n_buttons; ++i) + if (self->button[i].result == self->default_result) { + self->focus = &self->button[i]; + break; + } + + if (parent) { + Atom states[1]; + gint nstates; + Window p; + XWMHints h; + + if (parent->group) { + /* make it transient for the window's group */ + h.flags = WindowGroupHint; + h.window_group = parent->group->leader; + p = obt_root(ob_screen); + } + else { + /* make it transient for the window directly */ + h.flags = 0; + p = parent->window; + } + + XSetWMHints(obt_display, self->super.window, &h); + OBT_PROP_SET32(self->super.window, WM_TRANSIENT_FOR, WINDOW, p); + + states[0] = OBT_PROP_ATOM(NET_WM_STATE_MODAL); + nstates = (modal ? 1 : 0); + OBT_PROP_SETA32(self->super.window, NET_WM_STATE, ATOM, + states, nstates); + } + else + OBT_PROP_ERASE(self->super.window, WM_TRANSIENT_FOR); + + /* set up the dialog and render it */ + prompt_layout(self); + render_all(self); + + client_manage(self->super.window, self); + + self->mapped = TRUE; +} + +void prompt_hide(ObPrompt *self) +{ + XUnmapWindow(obt_display, self->super.window); + self->mapped = FALSE; +} + +gboolean prompt_key_event(ObPrompt *self, XEvent *e) +{ + gboolean shift; + guint shift_mask, mods; + KeySym sym; + + if (e->type != KeyPress) return FALSE; + + shift_mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT); + mods = obt_keyboard_only_modmasks(e->xkey.state); + shift = !!(mods & shift_mask); + + /* only accept shift */ + if (mods != 0 && mods != shift_mask) + return FALSE; + + sym = obt_keyboard_keypress_to_keysym(e); + + if (sym == XK_Escape) + prompt_cancel(self); + else if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_space) + prompt_run_callback(self, self->focus->result); + else if (sym == XK_Tab || sym == XK_Left || sym == XK_Right) { + gint i; + gboolean left; + ObPromptElement *oldfocus; + + left = (sym == XK_Left) || ((sym == XK_Tab) && shift); + oldfocus = self->focus; + + for (i = 0; i < self->n_buttons; ++i) + if (self->focus == &self->button[i]) break; + i += (left ? -1 : 1); + if (i < 0) i = self->n_buttons - 1; + else if (i >= self->n_buttons) i = 0; + self->focus = &self->button[i]; + + if (oldfocus != self->focus) render_button(self, oldfocus); + render_button(self, self->focus); + } + return TRUE; +} + +gboolean prompt_mouse_event(ObPrompt *self, XEvent *e) +{ + gint i; + ObPromptElement *but; + + if (e->type != ButtonPress && e->type != ButtonRelease && + e->type != MotionNotify) return FALSE; + + /* find the button */ + but = NULL; + for (i = 0; i < self->n_buttons; ++i) + if (self->button[i].window == + (e->type == MotionNotify ? e->xmotion.window : e->xbutton.window)) + { + but = &self->button[i]; + break; + } + if (!but) return FALSE; + + if (e->type == ButtonPress) { + ObPromptElement *oldfocus; + + oldfocus = self->focus; + + but->pressed = but->hover = TRUE; + self->focus = but; + + if (oldfocus != but) render_button(self, oldfocus); + render_button(self, but); + } + else if (e->type == ButtonRelease) { + if (but->hover) + prompt_run_callback(self, but->result); + but->pressed = FALSE; + } + else if (e->type == MotionNotify) { + if (but->pressed) { + gboolean hover; + + hover = (e->xmotion.x >= 0 && e->xmotion.y >= 0 && + e->xmotion.x < but->width && e->xmotion.y < but->height); + + if (hover != but->hover) { + but->hover = hover; + render_button(self, but); + } + } + } + return TRUE; +} + +void prompt_cancel(ObPrompt *self) +{ + prompt_run_callback(self, self->cancel_result); +} + +static gboolean prompt_show_message_cb(ObPrompt *p, int res, gpointer data) +{ + return TRUE; /* call the cleanup func */ +} + +static void prompt_show_message_cleanup(ObPrompt *p, gpointer data) +{ + prompt_unref(p); +} + +ObPrompt* prompt_show_message(const gchar *msg, const gchar *title, + const gchar *answer) +{ + ObPrompt *p; + ObPromptAnswer ans[] = { + { answer, 0 } + }; + + p = prompt_new(msg, title, ans, 1, 0, 0, + prompt_show_message_cb, prompt_show_message_cleanup, NULL); + prompt_show(p, NULL, FALSE); + return p; +} + +static void prompt_run_callback(ObPrompt *self, gint result) +{ + prompt_ref(self); + if (self->func) { + gboolean clean = self->func(self, self->focus->result, self->data); + if (clean && self->cleanup) + self->cleanup(self, self->data); + } + prompt_hide(self); + prompt_unref(self); +} diff --git a/openbox/prompt.h b/openbox/prompt.h new file mode 100644 index 0000000..1358574 --- /dev/null +++ b/openbox/prompt.h @@ -0,0 +1,125 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + prompt.h for the Openbox window manager + Copyright (c) 2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__prompt_h +#define ob__prompt_h + +#include "window.h" +#include "geom.h" +#include "obrender/render.h" +#include "obt/keyboard.h" +#include <glib.h> +#include <X11/Xlib.h> + +typedef struct _ObPrompt ObPrompt; +typedef struct _ObPromptElement ObPromptElement; +typedef struct _ObPromptAnswer ObPromptAnswer; + +typedef gboolean (*ObPromptCallback)(ObPrompt *p, gint result, gpointer data); +typedef void (*ObPromptCleanup)(ObPrompt *p, gpointer data); + +struct _ObPromptElement { + gchar *text; + Window window; + + gint x, y, width, height; + gboolean pressed; + gboolean hover; + gint result; +}; + +struct _ObPrompt +{ + ObInternalWindow super; + gint ref; + + ObtIC *ic; + guint event_mask; + + /* keep a copy of this because we re-render things that may need it + (i.e. the buttons) */ + RrAppearance *a_bg; + + gboolean mapped; + gint width, height; + gint msg_wbound; + + ObPromptElement msg; + + /* one for each answer */ + ObPromptElement *button; + gint n_buttons; + + /* points to the button with the focus */ + ObPromptElement *focus; + /* the default button to have selected */ + gint default_result; + /* the cancel result if the dialog is closed */ + gint cancel_result; + + ObPromptCallback func; + ObPromptCleanup cleanup; + gpointer data; +}; + +struct _ObPromptAnswer { + const gchar *text; + gint result; +}; + +void prompt_startup(gboolean reconfig); +void prompt_shutdown(gboolean reconfig); + +/*! Create a new prompt + @param answers A number of ObPromptAnswers which define the buttons which + will appear in the dialog from left to right, and the result + returned when they are selected. + @param n_answers The number of answers + @param default_result The result for the answer button selected by default + @param cancel_result The result that is given if the dialog is closed instead + of having a button presssed + @param func The callback function which is called when the dialog is closed + or a button is pressed + @param cleanup The cleanup function which is called if the prompt system + is shutting down, and someone is still holding a reference to the + prompt. This callback should cause the prompt's refcount to go to + zero so it can be freed, and free any other memory associated with + the prompt. The cleanup function is also called if the prompt's + callback function returns TRUE. + @param data User defined data which will be passed to the callback +*/ +ObPrompt* prompt_new(const gchar *msg, const gchar *title, + const ObPromptAnswer *answers, gint n_answers, + gint default_result, gint cancel_result, + ObPromptCallback func, ObPromptCleanup cleanup, + gpointer data); +void prompt_ref(ObPrompt *self); +void prompt_unref(ObPrompt *self); + +/*! Show the prompt. It will be centered within the given area rectangle */ +void prompt_show(ObPrompt *self, struct _ObClient *parent, gboolean modal); +void prompt_hide(ObPrompt *self); + +gboolean prompt_key_event(ObPrompt *self, XEvent *e); +gboolean prompt_mouse_event(ObPrompt *self, XEvent *e); +void prompt_cancel(ObPrompt *self); + +ObPrompt* prompt_show_message(const gchar *msg, const gchar *title, + const gchar *answer); + +#endif diff --git a/openbox/resist.c b/openbox/resist.c new file mode 100644 index 0000000..12f5632 --- /dev/null +++ b/openbox/resist.c @@ -0,0 +1,416 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + resist.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "resist.h" +#include "client.h" +#include "frame.h" +#include "stacking.h" +#include "screen.h" +#include "dock.h" +#include "config.h" + +#include <glib.h> + +static gboolean resist_move_window(Rect window, + Rect target, gint resist, + gint *x, gint *y) +{ + gint l, t, r, b; /* requested edges */ + gint cl, ct, cr, cb; /* current edges */ + gint w, h; /* current size */ + gint tl, tt, tr, tb; /* 1 past the target's edges on each side */ + gboolean snapx = 0, snapy = 0; + + w = window.width; + h = window.height; + + l = *x; + t = *y; + r = l + w - 1; + b = t + h - 1; + + cl = RECT_LEFT(window); + ct = RECT_TOP(window); + cr = RECT_RIGHT(window); + cb = RECT_BOTTOM(window); + + tl = RECT_LEFT(target) - 1; + tt = RECT_TOP(target) - 1; + tr = RECT_RIGHT(target) + 1; + tb = RECT_BOTTOM(target) + 1; + + /* snapx and snapy ensure that the window snaps to the top-most + window edge available, without going all the way from + bottom-to-top in the stacking list + */ + if (!snapx) { + if (ct < tb && cb > tt) { + if (cl >= tr && l < tr && l >= tr - resist) + *x = tr, snapx = TRUE; + else if (cr <= tl && r > tl && + r <= tl + resist) + *x = tl - w + 1, snapx = TRUE; + if (snapx) { + /* try to corner snap to the window */ + if (ct > tt && t <= tt && + t > tt - resist) + *y = tt + 1, snapy = TRUE; + else if (cb < tb && b >= tb && + b < tb + resist) + *y = tb - h, snapy = TRUE; + } + } + } + if (!snapy) { + if (cl < tr && cr > tl) { + if (ct >= tb && t < tb && t >= tb - resist) + *y = tb, snapy = TRUE; + else if (cb <= tt && b > tt && + b <= tt + resist) + *y = tt - h + 1, snapy = TRUE; + if (snapy) { + /* try to corner snap to the window */ + if (cl > tl && l <= tl && + l > tl - resist) + *x = tl + 1, snapx = TRUE; + else if (cr < tr && r >= tr && + r < tr + resist) + *x = tr - w, snapx = TRUE; + } + } + } + + return snapx && snapy; +} + +void resist_move_windows(ObClient *c, gint resist, gint *x, gint *y) +{ + GList *it; + Rect dock_area; + + if (!resist) return; + + frame_client_gravity(c->frame, x, y); + + + for (it = stacking_list; it; it = g_list_next(it)) { + ObClient *target; + + if (!WINDOW_IS_CLIENT(it->data)) + continue; + target = it->data; + + /* don't snap to self or non-visibles */ + if (!target->frame->visible || target == c) + continue; + /* don't snap to windows set to below and skip_taskbar (desklets) */ + if (target->below && !c->below && target->skip_taskbar) + continue; + + if (resist_move_window(c->frame->area, target->frame->area, + resist, x, y)) + break; + } + dock_get_area(&dock_area); + resist_move_window(c->frame->area, dock_area, resist, x, y); + + frame_frame_gravity(c->frame, x, y); +} + +void resist_move_monitors(ObClient *c, gint resist, gint *x, gint *y) +{ + Rect *area; + const Rect *parea; + guint i; + gint l, t, r, b; /* requested edges */ + gint al, at, ar, ab; /* screen area edges */ + gint pl, pt, pr, pb; /* physical screen area edges */ + gint cl, ct, cr, cb; /* current edges */ + gint w, h; /* current size */ + Rect desired_area; + + if (!resist) return; + + frame_client_gravity(c->frame, x, y); + + w = c->frame->area.width; + h = c->frame->area.height; + + l = *x; + t = *y; + r = l + w - 1; + b = t + h - 1; + + cl = RECT_LEFT(c->frame->area); + ct = RECT_TOP(c->frame->area); + cr = RECT_RIGHT(c->frame->area); + cb = RECT_BOTTOM(c->frame->area); + + RECT_SET(desired_area, c->frame->area.x, c->frame->area.y, + c->frame->area.width, c->frame->area.height); + + for (i = 0; i < screen_num_monitors; ++i) { + parea = screen_physical_area_monitor(i); + + if (!RECT_INTERSECTS_RECT(*parea, c->frame->area)) + continue; + + area = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, + &desired_area); + + al = RECT_LEFT(*area); + at = RECT_TOP(*area); + ar = RECT_RIGHT(*area); + ab = RECT_BOTTOM(*area); + pl = RECT_LEFT(*parea); + pt = RECT_TOP(*parea); + pr = RECT_RIGHT(*parea); + pb = RECT_BOTTOM(*parea); + + if (cl >= al && l < al && l >= al - resist) + *x = al; + else if (cr <= ar && r > ar && r <= ar + resist) + *x = ar - w + 1; + else if (cl >= pl && l < pl && l >= pl - resist) + *x = pl; + else if (cr <= pr && r > pr && r <= pr + resist) + *x = pr - w + 1; + + if (ct >= at && t < at && t >= at - resist) + *y = at; + else if (cb <= ab && b > ab && b < ab + resist) + *y = ab - h + 1; + else if (ct >= pt && t < pt && t >= pt - resist) + *y = pt; + else if (cb <= pb && b > pb && b < pb + resist) + *y = pb - h + 1; + + g_slice_free(Rect, area); + } + + frame_frame_gravity(c->frame, x, y); +} + +static gboolean resist_size_window(Rect window, Rect target, gint resist, + gint *w, gint *h, ObDirection dir) +{ + gint l, t, r, b; /* my left, top, right and bottom sides */ + gint tl, tt, tr, tb; /* target's left, top, right and bottom bottom sides*/ + gint dlt, drb; /* my destination left/top and right/bottom sides */ + gboolean snapx = 0, snapy = 0; + gint orgw, orgh; + + l = RECT_LEFT(window); + t = RECT_TOP(window); + r = RECT_RIGHT(window); + b = RECT_BOTTOM(window); + + orgw = window.width; + orgh = window.height; + + tl = RECT_LEFT(target); + tt = RECT_TOP(target); + tr = RECT_RIGHT(target); + tb = RECT_BOTTOM(target); + + if (!snapx) { + /* horizontal snapping */ + if (t < tb && b > tt) { + switch (dir) { + case OB_DIRECTION_EAST: + case OB_DIRECTION_NORTHEAST: + case OB_DIRECTION_SOUTHEAST: + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + dlt = l; + drb = r + *w - orgw; + if (r < tl && drb >= tl && + drb < tl + resist) + *w = tl - l, snapx = TRUE; + break; + case OB_DIRECTION_WEST: + case OB_DIRECTION_NORTHWEST: + case OB_DIRECTION_SOUTHWEST: + dlt = l - *w + orgw; + drb = r; + if (l > tr && dlt <= tr && + dlt > tr - resist) + *w = r - tr, snapx = TRUE; + break; + } + } + } + + if (!snapy) { + /* vertical snapping */ + if (l < tr && r > tl) { + switch (dir) { + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_SOUTHWEST: + case OB_DIRECTION_SOUTHEAST: + case OB_DIRECTION_EAST: + case OB_DIRECTION_WEST: + dlt = t; + drb = b + *h - orgh; + if (b < tt && drb >= tt && + drb < tt + resist) + *h = tt - t, snapy = TRUE; + break; + case OB_DIRECTION_NORTH: + case OB_DIRECTION_NORTHWEST: + case OB_DIRECTION_NORTHEAST: + dlt = t - *h + orgh; + drb = b; + if (t > tb && dlt <= tb && + dlt > tb - resist) + *h = b - tb, snapy = TRUE; + break; + } + } + } + + /* snapped both ways */ + return snapx && snapy; +} + +void resist_size_windows(ObClient *c, gint resist, gint *w, gint *h, + ObDirection dir) +{ + GList *it; + ObClient *target; /* target */ + Rect dock_area; + + if (!resist) return; + + for (it = stacking_list; it; it = g_list_next(it)) { + if (!WINDOW_IS_CLIENT(it->data)) + continue; + target = it->data; + + /* don't snap to invisibles or ourself */ + if (!target->frame->visible || target == c) + continue; + /* don't snap to windows set to below and skip_taskbar (desklets) */ + if (target->below && !c->below && target->skip_taskbar) + continue; + + if (resist_size_window(c->frame->area, target->frame->area, + resist, w, h, dir)) + break; + } + dock_get_area(&dock_area); + resist_size_window(c->frame->area, dock_area, + resist, w, h, dir); +} + +void resist_size_monitors(ObClient *c, gint resist, gint *w, gint *h, + ObDirection dir) +{ + gint l, t, r, b; /* my left, top, right and bottom sides */ + gint dlt, drb; /* my destination left/top and right/bottom sides */ + Rect *area; + const Rect *parea; + gint al, at, ar, ab; /* screen boundaries */ + gint pl, pt, pr, pb; /* physical screen boundaries */ + guint i; + Rect desired_area; + + if (!resist) return; + + l = RECT_LEFT(c->frame->area); + r = RECT_RIGHT(c->frame->area); + t = RECT_TOP(c->frame->area); + b = RECT_BOTTOM(c->frame->area); + + RECT_SET(desired_area, c->area.x, c->area.y, *w, *h); + + for (i = 0; i < screen_num_monitors; ++i) { + parea = screen_physical_area_monitor(i); + + if (!RECT_INTERSECTS_RECT(*parea, c->frame->area)) + continue; + + area = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, + &desired_area); + + /* get the screen boundaries */ + al = RECT_LEFT(*area); + at = RECT_TOP(*area); + ar = RECT_RIGHT(*area); + ab = RECT_BOTTOM(*area); + pl = RECT_LEFT(*parea); + pt = RECT_TOP(*parea); + pr = RECT_RIGHT(*parea); + pb = RECT_BOTTOM(*parea); + + /* horizontal snapping */ + switch (dir) { + case OB_DIRECTION_EAST: + case OB_DIRECTION_NORTHEAST: + case OB_DIRECTION_SOUTHEAST: + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + dlt = l; + drb = r + *w - c->frame->area.width; + if (r <= ar && drb > ar && drb <= ar + resist) + *w = ar - l + 1; + else if (r <= pr && drb > pr && drb <= pr + resist) + *w = pr - l + 1; + break; + case OB_DIRECTION_WEST: + case OB_DIRECTION_NORTHWEST: + case OB_DIRECTION_SOUTHWEST: + dlt = l - *w + c->frame->area.width; + drb = r; + if (l >= al && dlt < al && dlt >= al - resist) + *w = r - al + 1; + else if (l >= pl && dlt < pl && dlt >= pl - resist) + *w = r - pl + 1; + break; + } + + /* vertical snapping */ + switch (dir) { + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_SOUTHWEST: + case OB_DIRECTION_SOUTHEAST: + case OB_DIRECTION_WEST: + case OB_DIRECTION_EAST: + dlt = t; + drb = b + *h - c->frame->area.height; + if (b <= ab && drb > ab && drb <= ab + resist) + *h = ab - t + 1; + else if (b <= pb && drb > pb && drb <= pb + resist) + *h = pb - t + 1; + break; + case OB_DIRECTION_NORTH: + case OB_DIRECTION_NORTHWEST: + case OB_DIRECTION_NORTHEAST: + dlt = t - *h + c->frame->area.height; + drb = b; + if (t >= at && dlt < at && dlt >= at - resist) + *h = b - at + 1; + else if (t >= pt && dlt < pt && dlt >= pt - resist) + *h = b - pt + 1; + break; + } + + g_slice_free(Rect, area); + } +} diff --git a/openbox/resist.h b/openbox/resist.h new file mode 100644 index 0000000..31cc717 --- /dev/null +++ b/openbox/resist.h @@ -0,0 +1,41 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + resist.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__resist_h +#define ob__resist_h + +#include "misc.h" + +#include <glib.h> + +struct _ObClient; + +/*! @x The client's x destination (in the client's coordinates, not the frame's + @y The client's y destination (in the client's coordinates, not the frame's +*/ +void resist_move_windows(struct _ObClient *c, gint resist, gint *x, gint *y); +/*! @x The client's x destination (in the client's coordinates, not the frame's + @y The client's y destination (in the client's coordinates, not the frame's +*/ +void resist_move_monitors(struct _ObClient *c, gint resist, gint *x, gint *y); +void resist_size_windows(struct _ObClient *c, gint resist, gint *w, gint *h, + ObDirection dir); +void resist_size_monitors(struct _ObClient *c, gint resist, gint *w, gint *h, + ObDirection dir); + +#endif diff --git a/openbox/screen.c b/openbox/screen.c new file mode 100644 index 0000000..ffe74a0 --- /dev/null +++ b/openbox/screen.c @@ -0,0 +1,1761 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + screen.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "debug.h" +#include "openbox.h" +#include "dock.h" +#include "grab.h" +#include "startupnotify.h" +#include "moveresize.h" +#include "config.h" +#include "screen.h" +#include "client.h" +#include "session.h" +#include "frame.h" +#include "event.h" +#include "focus.h" +#include "focus_cycle.h" +#include "popup.h" +#include "version.h" +#include "obrender/render.h" +#include "gettext.h" +#include "obt/display.h" +#include "obt/xqueue.h" +#include "obt/prop.h" + +#include <X11/Xlib.h> +#ifdef HAVE_UNISTD_H +# include <sys/types.h> +# include <unistd.h> +#endif +#include <assert.h> + +/*! The event mask to grab on the root window */ +#define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \ + EnterWindowMask | LeaveWindowMask | \ + SubstructureRedirectMask | FocusChangeMask | \ + ButtonPressMask | ButtonReleaseMask) + +static gboolean screen_validate_layout(ObDesktopLayout *l); +static gboolean replace_wm(void); +static void screen_tell_ksplash(void); +static void screen_fallback_focus(void); + +guint screen_num_desktops; +guint screen_num_monitors; +guint screen_desktop; +guint screen_last_desktop; +gboolean screen_showing_desktop; +ObDesktopLayout screen_desktop_layout; +gchar **screen_desktop_names; +Window screen_support_win; +Time screen_desktop_user_time = CurrentTime; + +static Size screen_physical_size; +static guint screen_old_desktop; +static gboolean screen_desktop_timeout = TRUE; +static guint screen_desktop_timer = 0; +/*! An array of desktops, holding an array of areas per monitor */ +static Rect *monitor_area = NULL; +/*! An array of desktops, holding an array of struts */ +static GSList *struts_top = NULL; +static GSList *struts_left = NULL; +static GSList *struts_right = NULL; +static GSList *struts_bottom = NULL; + +static ObPagerPopup *desktop_popup; +static guint desktop_popup_timer = 0; +static gboolean desktop_popup_perm; + +/*! The number of microseconds that you need to be on a desktop before it will + replace the remembered "last desktop" */ +#define REMEMBER_LAST_DESKTOP_TIME 750 + +static gboolean replace_wm(void) +{ + gchar *wm_sn; + Atom wm_sn_atom; + Window current_wm_sn_owner; + Time timestamp; + + wm_sn = g_strdup_printf("WM_S%d", ob_screen); + wm_sn_atom = XInternAtom(obt_display, wm_sn, FALSE); + g_free(wm_sn); + + current_wm_sn_owner = XGetSelectionOwner(obt_display, wm_sn_atom); + if (current_wm_sn_owner == screen_support_win) + current_wm_sn_owner = None; + if (current_wm_sn_owner) { + if (!ob_replace_wm) { + g_message(_("A window manager is already running on screen %d"), + ob_screen); + return FALSE; + } + obt_display_ignore_errors(TRUE); + + /* We want to find out when the current selection owner dies */ + XSelectInput(obt_display, current_wm_sn_owner, StructureNotifyMask); + XSync(obt_display, FALSE); + + obt_display_ignore_errors(FALSE); + if (obt_display_error_occured) + current_wm_sn_owner = None; + } + + timestamp = event_time(); + + XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win, + timestamp); + + if (XGetSelectionOwner(obt_display, wm_sn_atom) != screen_support_win) { + g_message(_("Could not acquire window manager selection on screen %d"), + ob_screen); + return FALSE; + } + + /* Wait for old window manager to go away */ + if (current_wm_sn_owner) { + gulong wait = 0; + const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */ + ObtXQueueWindowType wt; + + wt.window = current_wm_sn_owner; + wt.type = DestroyNotify; + + while (wait < timeout) { + /* Checks the local queue and incoming events for this event */ + if (xqueue_exists_local(xqueue_match_window_type, &wt)) + break; + g_usleep(G_USEC_PER_SEC / 10); + wait += G_USEC_PER_SEC / 10; + } + + if (wait >= timeout) { + g_message(_("The WM on screen %d is not exiting"), ob_screen); + return FALSE; + } + } + + /* Send client message indicating that we are now the WM */ + obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER), + timestamp, wm_sn_atom, screen_support_win, 0, 0, + SubstructureNotifyMask); + + return TRUE; +} + +gboolean screen_annex(void) +{ + XSetWindowAttributes attrib; + pid_t pid; + gint i, num_support; + gulong *supported; + + /* create the netwm support window */ + attrib.override_redirect = TRUE; + attrib.event_mask = PropertyChangeMask; + screen_support_win = XCreateWindow(obt_display, obt_root(ob_screen), + -100, -100, 1, 1, 0, + CopyFromParent, InputOutput, + CopyFromParent, + CWEventMask | CWOverrideRedirect, + &attrib); + XMapWindow(obt_display, screen_support_win); + XLowerWindow(obt_display, screen_support_win); + + if (!replace_wm()) { + XDestroyWindow(obt_display, screen_support_win); + return FALSE; + } + + obt_display_ignore_errors(TRUE); + XSelectInput(obt_display, obt_root(ob_screen), ROOT_EVENTMASK); + obt_display_ignore_errors(FALSE); + if (obt_display_error_occured) { + g_message(_("A window manager is already running on screen %d"), + ob_screen); + + XDestroyWindow(obt_display, screen_support_win); + return FALSE; + } + + screen_set_root_cursor(); + + /* set the OPENBOX_PID hint */ + pid = getpid(); + OBT_PROP_SET32(obt_root(ob_screen), OPENBOX_PID, CARDINAL, pid); + + /* set supporting window */ + OBT_PROP_SET32(obt_root(ob_screen), + NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win); + + /* set properties on the supporting window */ + OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox"); + OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK, + WINDOW, screen_support_win); + + /* set the _NET_SUPPORTED_ATOMS hint */ + + /* this is all the atoms after NET_SUPPORTED in the ObtPropAtoms enum */ + num_support = OBT_PROP_NUM_ATOMS - OBT_PROP_NET_SUPPORTED - 1; + i = 0; + supported = g_new(gulong, num_support); + supported[i++] = OBT_PROP_ATOM(NET_SUPPORTING_WM_CHECK); + supported[i++] = OBT_PROP_ATOM(NET_WM_FULL_PLACEMENT); + supported[i++] = OBT_PROP_ATOM(NET_CURRENT_DESKTOP); + supported[i++] = OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS); + supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_GEOMETRY); + supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_VIEWPORT); + supported[i++] = OBT_PROP_ATOM(NET_ACTIVE_WINDOW); + supported[i++] = OBT_PROP_ATOM(NET_WORKAREA); + supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST); + supported[i++] = OBT_PROP_ATOM(NET_CLIENT_LIST_STACKING); + supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_NAMES); + supported[i++] = OBT_PROP_ATOM(NET_CLOSE_WINDOW); + supported[i++] = OBT_PROP_ATOM(NET_DESKTOP_LAYOUT); + supported[i++] = OBT_PROP_ATOM(NET_SHOWING_DESKTOP); + supported[i++] = OBT_PROP_ATOM(NET_WM_NAME); + supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_NAME); + supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_NAME); + supported[i++] = OBT_PROP_ATOM(NET_WM_VISIBLE_ICON_NAME); + supported[i++] = OBT_PROP_ATOM(NET_WM_DESKTOP); + supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT); + supported[i++] = OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL); + supported[i++] = OBT_PROP_ATOM(NET_WM_ICON); + supported[i++] = OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL); + supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS); + supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE); + supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW); + supported[i++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION); + supported[i++] = OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW); + supported[i++] = OBT_PROP_ATOM(NET_WM_MOVERESIZE); + supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME); +/* + supported[i++] = OBT_PROP_ATOM(NET_WM_USER_TIME_WINDOW); +*/ + supported[i++] = OBT_PROP_ATOM(NET_FRAME_EXTENTS); + supported[i++] = OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS); + supported[i++] = OBT_PROP_ATOM(NET_RESTACK_WINDOW); + supported[i++] = OBT_PROP_ATOM(NET_STARTUP_ID); +#ifdef SYNC + supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST); + supported[i++] = OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER); +#endif + supported[i++] = OBT_PROP_ATOM(NET_WM_PID); + supported[i++] = OBT_PROP_ATOM(NET_WM_PING); + + supported[i++] = OBT_PROP_ATOM(KDE_WM_CHANGE_STATE); + supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_FRAME_STRUT); + supported[i++] = OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE); + + supported[i++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE); + supported[i++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED); + supported[i++] = OBT_PROP_ATOM(OPENBOX_PID); + supported[i++] = OBT_PROP_ATOM(OB_THEME); + supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE); + supported[i++] = OBT_PROP_ATOM(OB_CONTROL); + supported[i++] = OBT_PROP_ATOM(OB_VERSION); + supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE); + supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE); + supported[i++] = OBT_PROP_ATOM(OB_APP_NAME); + supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS); + supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE); + g_assert(i == num_support); + + OBT_PROP_SETA32(obt_root(ob_screen), + NET_SUPPORTED, ATOM, supported, num_support); + g_free(supported); + + OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION, + OPENBOX_VERSION); + + screen_tell_ksplash(); + + return TRUE; +} + +static void screen_tell_ksplash(void) +{ + XEvent e; + char **argv; + + argv = g_new(gchar*, 6); + argv[0] = g_strdup("dcop"); + argv[1] = g_strdup("ksplash"); + argv[2] = g_strdup("ksplash"); + argv[3] = g_strdup("upAndRunning(QString)"); + argv[4] = g_strdup("wm started"); + argv[5] = NULL; + + /* tell ksplash through the dcop server command line interface */ + g_spawn_async(NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_STDERR_TO_DEV_NULL | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, NULL); + g_strfreev(argv); + + /* i'm not sure why we do this, kwin does it, but ksplash doesn't seem to + hear it anyways. perhaps it is for old ksplash. or new ksplash. or + something. oh well. */ + e.xclient.type = ClientMessage; + e.xclient.display = obt_display; + e.xclient.window = obt_root(ob_screen); + e.xclient.message_type = + XInternAtom(obt_display, "_KDE_SPLASH_PROGRESS", False); + e.xclient.format = 8; + strcpy(e.xclient.data.b, "wm started"); + XSendEvent(obt_display, obt_root(ob_screen), + False, SubstructureNotifyMask, &e); +} + +void screen_startup(gboolean reconfig) +{ + gchar **names = NULL; + guint32 d; + gboolean namesexist = FALSE; + + desktop_popup = pager_popup_new(); + desktop_popup_perm = FALSE; + pager_popup_height(desktop_popup, POPUP_HEIGHT); + + if (reconfig) { + /* update the pager popup's width */ + pager_popup_text_width_to_strings(desktop_popup, + screen_desktop_names, + screen_num_desktops); + return; + } + + /* get the initial size */ + screen_resize(); + + /* have names already been set for the desktops? */ + if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) { + g_strfreev(names); + namesexist = TRUE; + } + + /* if names don't exist and we have session names, set those. + do this stuff BEFORE setting the number of desktops, because that + will create default names for them + */ + if (!namesexist && session_desktop_names != NULL) { + guint i, numnames; + GSList *it; + + /* get the desktop names */ + numnames = g_slist_length(session_desktop_names); + names = g_new(gchar*, numnames + 1); + names[numnames] = NULL; + for (i = 0, it = session_desktop_names; it; ++i, it = g_slist_next(it)) + names[i] = g_strdup(it->data); + + /* set the root window property */ + OBT_PROP_SETSS(obt_root(ob_screen), + NET_DESKTOP_NAMES, (const gchar*const*)names); + + g_strfreev(names); + } + + /* set the number of desktops, if it's not already set. + + this will also set the default names from the config file up for + desktops that don't have names yet */ + screen_num_desktops = 0; + if (OBT_PROP_GET32(obt_root(ob_screen), + NET_NUMBER_OF_DESKTOPS, CARDINAL, &d)) + { + if (d != config_desktops_num) { + /* TRANSLATORS: If you need to specify a different order of the + arguments, you can use %1$d for the first one and %2$d for the + second one. For example, + "The current session has %2$d desktops, but Openbox is configured for %1$d ..." */ + g_warning(ngettext("Openbox is configured for %d desktop, but the current session has %d. Overriding the Openbox configuration.", "Openbox is configured for %d desktops, but the current session has %d. Overriding the Openbox configuration.", config_desktops_num), + config_desktops_num, d); + } + screen_set_num_desktops(d); + } + /* restore from session if possible */ + else if (session_num_desktops) + screen_set_num_desktops(session_num_desktops); + else + screen_set_num_desktops(config_desktops_num); + + screen_desktop = screen_num_desktops; /* something invalid */ + /* start on the current desktop when a wm was already running */ + if (OBT_PROP_GET32(obt_root(ob_screen), + NET_CURRENT_DESKTOP, CARDINAL, &d) && + d < screen_num_desktops) + { + screen_set_desktop(d, FALSE); + } else if (session_desktop >= 0) + screen_set_desktop(MIN((guint)session_desktop, + screen_num_desktops), FALSE); + else + screen_set_desktop(MIN(config_screen_firstdesk, + screen_num_desktops) - 1, FALSE); + screen_last_desktop = screen_desktop; + + /* don't start in showing-desktop mode */ + screen_showing_desktop = FALSE; + OBT_PROP_SET32(obt_root(ob_screen), + NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop); + + if (session_desktop_layout_present && + screen_validate_layout(&session_desktop_layout)) + { + screen_desktop_layout = session_desktop_layout; + } + else + screen_update_layout(); +} + +void screen_shutdown(gboolean reconfig) +{ + pager_popup_free(desktop_popup); + + if (reconfig) + return; + + XSelectInput(obt_display, obt_root(ob_screen), NoEventMask); + + /* we're not running here no more! */ + OBT_PROP_ERASE(obt_root(ob_screen), OPENBOX_PID); + /* not without us */ + OBT_PROP_ERASE(obt_root(ob_screen), NET_SUPPORTED); + /* don't keep this mode */ + OBT_PROP_ERASE(obt_root(ob_screen), NET_SHOWING_DESKTOP); + + XDestroyWindow(obt_display, screen_support_win); + + g_strfreev(screen_desktop_names); + screen_desktop_names = NULL; +} + +void screen_resize(void) +{ + gint w, h; + GList *it; + gulong geometry[2]; + + w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)); + h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)); + + /* Set the _NET_DESKTOP_GEOMETRY hint */ + screen_physical_size.width = geometry[0] = w; + screen_physical_size.height = geometry[1] = h; + OBT_PROP_SETA32(obt_root(ob_screen), + NET_DESKTOP_GEOMETRY, CARDINAL, geometry, 2); + + if (ob_state() != OB_STATE_RUNNING) + return; + + /* this calls screen_update_areas(), which we need ! */ + dock_configure(); + + for (it = client_list; it; it = g_list_next(it)) { + client_move_onscreen(it->data, FALSE); + client_reconfigure(it->data, FALSE); + } +} + +void screen_set_num_desktops(guint num) +{ + gulong *viewport; + GList *it, *stacking_copy; + + g_assert(num > 0); + + if (screen_num_desktops == num) return; + + screen_num_desktops = num; + OBT_PROP_SET32(obt_root(ob_screen), NET_NUMBER_OF_DESKTOPS, CARDINAL, num); + + /* set the viewport hint */ + viewport = g_new0(gulong, num * 2); + OBT_PROP_SETA32(obt_root(ob_screen), + NET_DESKTOP_VIEWPORT, CARDINAL, viewport, num * 2); + g_free(viewport); + + /* the number of rows/columns will differ */ + screen_update_layout(); + + /* move windows on desktops that will no longer exist! + make a copy of the list cuz we're changing it */ + stacking_copy = g_list_copy(stacking_list); + for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (c->desktop != DESKTOP_ALL && c->desktop >= num) + client_set_desktop(c, num - 1, FALSE, TRUE); + /* raise all the windows that are on the current desktop which + is being merged */ + else if (screen_desktop == num - 1 && + (c->desktop == DESKTOP_ALL || + c->desktop == screen_desktop)) + stacking_raise(CLIENT_AS_WINDOW(c)); + } + } + g_list_free(stacking_copy); + + /* change our struts/area to match (after moving windows) */ + screen_update_areas(); + + /* may be some unnamed desktops that we need to fill in with names + (after updating the areas so the popup can resize) */ + screen_update_desktop_names(); + + /* change our desktop if we're on one that no longer exists! */ + if (screen_desktop >= screen_num_desktops) + screen_set_desktop(num - 1, TRUE); +} + +static void screen_fallback_focus(void) +{ + ObClient *c; + gboolean allow_omni; + + /* only allow omnipresent windows to get focus on desktop change if + an omnipresent window is already focused (it'll keep focus probably, but + maybe not depending on mouse-focus options) */ + allow_omni = focus_client && (client_normal(focus_client) && + focus_client->desktop == DESKTOP_ALL); + + /* the client moved there already so don't move focus. prevent flicker + on sendtodesktop + follow */ + if (focus_client && focus_client->desktop == screen_desktop) + return; + + /* have to try focus here because when you leave an empty desktop + there is no focus out to watch for. also, we have different rules + here. we always allow it to look under the mouse pointer if + config_focus_last is FALSE + + do this before hiding the windows so if helper windows are coming + with us, they don't get hidden + */ + if ((c = focus_fallback(TRUE, !config_focus_last, allow_omni, + !allow_omni))) + { + /* only do the flicker reducing stuff ahead of time if we are going + to call xsetinputfocus on the window ourselves. otherwise there is + no guarantee the window will actually take focus.. */ + if (c->can_focus) { + /* reduce flicker by hiliting now rather than waiting for the + server FocusIn event */ + frame_adjust_focus(c->frame, TRUE); + /* do this here so that if you switch desktops to a window with + helper windows then the helper windows won't flash */ + client_bring_helper_windows(c); + } + } +} + +static gboolean last_desktop_func(gpointer data) +{ + screen_desktop_timeout = TRUE; + screen_desktop_timer = 0; + return FALSE; /* don't repeat */ +} + +void screen_set_desktop(guint num, gboolean dofocus) +{ + GList *it; + guint previous; + gulong ignore_start; + + g_assert(num < screen_num_desktops); + + previous = screen_desktop; + screen_desktop = num; + + if (previous == num) return; + + OBT_PROP_SET32(obt_root(ob_screen), NET_CURRENT_DESKTOP, CARDINAL, num); + + /* This whole thing decides when/how to save the screen_last_desktop so + that it can be restored later if you want */ + if (screen_desktop_timeout) { + /* If screen_desktop_timeout is true, then we've been on this desktop + long enough and we can save it as the last desktop. */ + + if (screen_last_desktop == previous) + /* this is the startup state only */ + screen_old_desktop = screen_desktop; + else { + /* save the "last desktop" as the "old desktop" */ + screen_old_desktop = screen_last_desktop; + /* save the desktop we're coming from as the "last desktop" */ + screen_last_desktop = previous; + } + } + else { + /* If screen_desktop_timeout is false, then we just got to this desktop + and we are moving away again. */ + + if (screen_desktop == screen_last_desktop) { + /* If we are moving to the "last desktop" .. */ + if (previous == screen_old_desktop) { + /* .. from the "old desktop", change the last desktop to + be where we are coming from */ + screen_last_desktop = screen_old_desktop; + } + else if (screen_last_desktop == screen_old_desktop) { + /* .. and also to the "old desktop", change the "last + desktop" to be where we are coming from */ + screen_last_desktop = previous; + } + else { + /* .. from some other desktop, then set the "last desktop" to + be the saved "old desktop", i.e. where we were before the + "last desktop" */ + screen_last_desktop = screen_old_desktop; + } + } + else { + /* If we are moving to any desktop besides the "last desktop".. + (this is the normal case) */ + if (screen_desktop == screen_old_desktop) { + /* If moving to the "old desktop", which is not the + "last desktop", don't save anything */ + } + else if (previous == screen_old_desktop) { + /* If moving from the "old desktop", and not to the + "last desktop", don't save anything */ + } + else if (screen_last_desktop == screen_old_desktop) { + /* If the "last desktop" is the same as "old desktop" and + you're not moving to the "last desktop" then save where + we're coming from as the "last desktop" */ + screen_last_desktop = previous; + } + else { + /* If the "last desktop" is different from the "old desktop" + and you're not moving to the "last desktop", then don't save + anything */ + } + } + } + screen_desktop_timeout = FALSE; + if (screen_desktop_timer) g_source_remove(screen_desktop_timer); + screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME, + last_desktop_func, NULL); + + ob_debug("Moving to desktop %d", num+1); + + if (ob_state() == OB_STATE_RUNNING) + screen_show_desktop_popup(screen_desktop, FALSE); + + /* ignore enter events caused by the move */ + ignore_start = event_start_ignore_all_enters(); + + if (moveresize_client) + client_set_desktop(moveresize_client, num, TRUE, FALSE); + + /* show windows before hiding the rest to lessen the enter/leave events */ + + /* show windows from top to bottom */ + for (it = stacking_list; it; it = g_list_next(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + client_show(c); + } + } + + if (dofocus) screen_fallback_focus(); + + /* hide windows from bottom to top */ + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (client_hide(c)) { + if (c == focus_client) { + /* c was focused and we didn't do fallback clearly so make + sure openbox doesnt still consider the window focused. + this happens when using NextWindow with allDesktops, + since it doesnt want to move focus on desktop change, + but the focus is not going to stay with the current + window, which has now disappeared. + only do this if the client was actually hidden, + otherwise it can keep focus. */ + focus_set_client(NULL); + } + } + } + } + + focus_cycle_addremove(NULL, TRUE); + + event_end_ignore_all_enters(ignore_start); + + if (event_source_time() != CurrentTime) + screen_desktop_user_time = event_source_time(); +} + +void screen_add_desktop(gboolean current) +{ + gulong ignore_start; + + /* ignore enter events caused by this */ + ignore_start = event_start_ignore_all_enters(); + + screen_set_num_desktops(screen_num_desktops+1); + + /* move all the clients over */ + if (current) { + GList *it; + + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (c->desktop != DESKTOP_ALL && c->desktop >= screen_desktop && + /* don't move direct children, they'll be moved with their + parent - which will have to be on the same desktop */ + !client_direct_parent(c)) + { + ob_debug("moving window %s", c->title); + client_set_desktop(c, c->desktop+1, FALSE, TRUE); + } + } + } + + event_end_ignore_all_enters(ignore_start); +} + +void screen_remove_desktop(gboolean current) +{ + guint rmdesktop, movedesktop; + GList *it, *stacking_copy; + gulong ignore_start; + + if (screen_num_desktops <= 1) return; + + /* ignore enter events caused by this */ + ignore_start = event_start_ignore_all_enters(); + + /* what desktop are we removing and moving to? */ + if (current) + rmdesktop = screen_desktop; + else + rmdesktop = screen_num_desktops - 1; + if (rmdesktop < screen_num_desktops - 1) + movedesktop = rmdesktop + 1; + else + movedesktop = rmdesktop; + + /* make a copy of the list cuz we're changing it */ + stacking_copy = g_list_copy(stacking_list); + for (it = g_list_last(stacking_copy); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + guint d = c->desktop; + if (d != DESKTOP_ALL && d >= movedesktop && + /* don't move direct children, they'll be moved with their + parent - which will have to be on the same desktop */ + !client_direct_parent(c)) + { + ob_debug("moving window %s", c->title); + client_set_desktop(c, c->desktop - 1, TRUE, TRUE); + } + /* raise all the windows that are on the current desktop which + is being merged */ + if ((screen_desktop == rmdesktop - 1 || + screen_desktop == rmdesktop) && + (d == DESKTOP_ALL || d == screen_desktop)) + { + stacking_raise(CLIENT_AS_WINDOW(c)); + ob_debug("raising window %s", c->title); + } + } + } + g_list_free(stacking_copy); + + /* fallback focus like we're changing desktops */ + if (screen_desktop < screen_num_desktops - 1) { + screen_fallback_focus(); + ob_debug("fake desktop change"); + } + + screen_set_num_desktops(screen_num_desktops-1); + + event_end_ignore_all_enters(ignore_start); +} + +static void get_row_col(guint d, guint *r, guint *c) +{ + switch (screen_desktop_layout.orientation) { + case OB_ORIENTATION_HORZ: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + *r = d / screen_desktop_layout.columns; + *c = d % screen_desktop_layout.columns; + break; + case OB_CORNER_BOTTOMLEFT: + *r = screen_desktop_layout.rows - 1 - + d / screen_desktop_layout.columns; + *c = d % screen_desktop_layout.columns; + break; + case OB_CORNER_TOPRIGHT: + *r = d / screen_desktop_layout.columns; + *c = screen_desktop_layout.columns - 1 - + d % screen_desktop_layout.columns; + break; + case OB_CORNER_BOTTOMRIGHT: + *r = screen_desktop_layout.rows - 1 - + d / screen_desktop_layout.columns; + *c = screen_desktop_layout.columns - 1 - + d % screen_desktop_layout.columns; + break; + } + break; + case OB_ORIENTATION_VERT: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + *r = d % screen_desktop_layout.rows; + *c = d / screen_desktop_layout.rows; + break; + case OB_CORNER_BOTTOMLEFT: + *r = screen_desktop_layout.rows - 1 - + d % screen_desktop_layout.rows; + *c = d / screen_desktop_layout.rows; + break; + case OB_CORNER_TOPRIGHT: + *r = d % screen_desktop_layout.rows; + *c = screen_desktop_layout.columns - 1 - + d / screen_desktop_layout.rows; + break; + case OB_CORNER_BOTTOMRIGHT: + *r = screen_desktop_layout.rows - 1 - + d % screen_desktop_layout.rows; + *c = screen_desktop_layout.columns - 1 - + d / screen_desktop_layout.rows; + break; + } + break; + } +} + +static guint translate_row_col(guint r, guint c) +{ + switch (screen_desktop_layout.orientation) { + case OB_ORIENTATION_HORZ: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + return r % screen_desktop_layout.rows * + screen_desktop_layout.columns + + c % screen_desktop_layout.columns; + case OB_CORNER_BOTTOMLEFT: + return (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows) * + screen_desktop_layout.columns + + c % screen_desktop_layout.columns; + case OB_CORNER_TOPRIGHT: + return r % screen_desktop_layout.rows * + screen_desktop_layout.columns + + (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns); + case OB_CORNER_BOTTOMRIGHT: + return (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows) * + screen_desktop_layout.columns + + (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns); + } + case OB_ORIENTATION_VERT: + switch (screen_desktop_layout.start_corner) { + case OB_CORNER_TOPLEFT: + return c % screen_desktop_layout.columns * + screen_desktop_layout.rows + + r % screen_desktop_layout.rows; + case OB_CORNER_BOTTOMLEFT: + return c % screen_desktop_layout.columns * + screen_desktop_layout.rows + + (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows); + case OB_CORNER_TOPRIGHT: + return (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns) * + screen_desktop_layout.rows + + r % screen_desktop_layout.rows; + case OB_CORNER_BOTTOMRIGHT: + return (screen_desktop_layout.columns - 1 - + c % screen_desktop_layout.columns) * + screen_desktop_layout.rows + + (screen_desktop_layout.rows - 1 - + r % screen_desktop_layout.rows); + } + } + g_assert_not_reached(); + return 0; +} + +static gboolean hide_desktop_popup_func(gpointer data) +{ + pager_popup_hide(desktop_popup); + desktop_popup_timer = 0; + return FALSE; /* don't repeat */ +} + +void screen_show_desktop_popup(guint d, gboolean perm) +{ + const Rect *a; + + /* 0 means don't show the popup */ + if (!config_desktop_popup_time) return; + + a = screen_physical_area_primary(FALSE); + pager_popup_position(desktop_popup, CenterGravity, + a->x + a->width / 2, a->y + a->height / 2); + pager_popup_icon_size_multiplier(desktop_popup, + (screen_desktop_layout.columns / + screen_desktop_layout.rows) / 2, + (screen_desktop_layout.rows/ + screen_desktop_layout.columns) / 2); + pager_popup_max_width(desktop_popup, + MAX(a->width/3, POPUP_WIDTH)); + pager_popup_show(desktop_popup, screen_desktop_names[d], d); + + if (desktop_popup_timer) g_source_remove(desktop_popup_timer); + desktop_popup_timer = 0; + if (!perm && !desktop_popup_perm) + /* only hide if its not already being show permanently */ + desktop_popup_timer = g_timeout_add(config_desktop_popup_time, + hide_desktop_popup_func, + desktop_popup); + if (perm) + desktop_popup_perm = TRUE; +} + +void screen_hide_desktop_popup(void) +{ + if (desktop_popup_timer) g_source_remove(desktop_popup_timer); + desktop_popup_timer = 0; + pager_popup_hide(desktop_popup); + desktop_popup_perm = FALSE; +} + +guint screen_find_desktop(guint from, ObDirection dir, + gboolean wrap, gboolean linear) +{ + guint r, c; + guint d; + + d = from; + get_row_col(d, &r, &c); + if (linear) { + switch (dir) { + case OB_DIRECTION_EAST: + if (d < screen_num_desktops - 1) + ++d; + else if (wrap) + d = 0; + else + return from; + break; + case OB_DIRECTION_WEST: + if (d > 0) + --d; + else if (wrap) + d = screen_num_desktops - 1; + else + return from; + break; + default: + g_assert_not_reached(); + return from; + } + } else { + switch (dir) { + case OB_DIRECTION_EAST: + ++c; + if (c >= screen_desktop_layout.columns) { + if (wrap) + c = 0; + else + return from; + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) + ++c; + else + return from; + } + break; + case OB_DIRECTION_WEST: + --c; + if (c >= screen_desktop_layout.columns) { + if (wrap) + c = screen_desktop_layout.columns - 1; + else + return from; + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) + --c; + else + return from; + } + break; + case OB_DIRECTION_SOUTH: + ++r; + if (r >= screen_desktop_layout.rows) { + if (wrap) + r = 0; + else + return from; + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) + ++r; + else + return from; + } + break; + case OB_DIRECTION_NORTH: + --r; + if (r >= screen_desktop_layout.rows) { + if (wrap) + r = screen_desktop_layout.rows - 1; + else + return from; + } + d = translate_row_col(r, c); + if (d >= screen_num_desktops) { + if (wrap) + --r; + else + return from; + } + break; + default: + g_assert_not_reached(); + return from; + } + + d = translate_row_col(r, c); + } + return d; +} + +static gboolean screen_validate_layout(ObDesktopLayout *l) +{ + if (l->columns == 0 && l->rows == 0) /* both 0's is bad data.. */ + return FALSE; + + /* fill in a zero rows/columns */ + if (l->columns == 0) { + l->columns = screen_num_desktops / l->rows; + if (l->rows * l->columns < screen_num_desktops) + l->columns++; + if (l->rows * l->columns >= screen_num_desktops + l->columns) + l->rows--; + } else if (l->rows == 0) { + l->rows = screen_num_desktops / l->columns; + if (l->columns * l->rows < screen_num_desktops) + l->rows++; + if (l->columns * l->rows >= screen_num_desktops + l->rows) + l->columns--; + } + + /* bounds checking */ + if (l->orientation == OB_ORIENTATION_HORZ) { + l->columns = MIN(screen_num_desktops, l->columns); + l->rows = MIN(l->rows, + (screen_num_desktops + l->columns - 1) / l->columns); + l->columns = screen_num_desktops / l->rows + + !!(screen_num_desktops % l->rows); + } else { + l->rows = MIN(screen_num_desktops, l->rows); + l->columns = MIN(l->columns, + (screen_num_desktops + l->rows - 1) / l->rows); + l->rows = screen_num_desktops / l->columns + + !!(screen_num_desktops % l->columns); + } + return TRUE; +} + +void screen_update_layout(void) + +{ + ObDesktopLayout l; + guint32 *data; + guint num; + + screen_desktop_layout.orientation = OB_ORIENTATION_HORZ; + screen_desktop_layout.start_corner = OB_CORNER_TOPLEFT; + screen_desktop_layout.rows = 1; + screen_desktop_layout.columns = screen_num_desktops; + + if (OBT_PROP_GETA32(obt_root(ob_screen), + NET_DESKTOP_LAYOUT, CARDINAL, &data, &num)) { + if (num == 3 || num == 4) { + + if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_VERT)) + l.orientation = OB_ORIENTATION_VERT; + else if (data[0] == OBT_PROP_ATOM(NET_WM_ORIENTATION_HORZ)) + l.orientation = OB_ORIENTATION_HORZ; + else + return; + + if (num < 4) + l.start_corner = OB_CORNER_TOPLEFT; + else { + if (data[3] == OBT_PROP_ATOM(NET_WM_TOPLEFT)) + l.start_corner = OB_CORNER_TOPLEFT; + else if (data[3] == OBT_PROP_ATOM(NET_WM_TOPRIGHT)) + l.start_corner = OB_CORNER_TOPRIGHT; + else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMRIGHT)) + l.start_corner = OB_CORNER_BOTTOMRIGHT; + else if (data[3] == OBT_PROP_ATOM(NET_WM_BOTTOMLEFT)) + l.start_corner = OB_CORNER_BOTTOMLEFT; + else + return; + } + + l.columns = data[1]; + l.rows = data[2]; + + if (screen_validate_layout(&l)) + screen_desktop_layout = l; + + g_free(data); + } + } +} + +void screen_update_desktop_names(void) +{ + guint i; + + /* empty the array */ + g_strfreev(screen_desktop_names); + screen_desktop_names = NULL; + + if (OBT_PROP_GETSS(obt_root(ob_screen), + NET_DESKTOP_NAMES, &screen_desktop_names)) + for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i); + else + i = 0; + if (i < screen_num_desktops) { + GSList *it; + + screen_desktop_names = g_renew(gchar*, screen_desktop_names, + screen_num_desktops + 1); + screen_desktop_names[screen_num_desktops] = NULL; + + it = g_slist_nth(config_desktops_names, i); + + for (; i < screen_num_desktops; ++i) { + if (it && ((char*)it->data)[0]) /* not empty */ + /* use the names from the config file when possible */ + screen_desktop_names[i] = g_strdup(it->data); + else + /* make up a nice name if it's not though */ + screen_desktop_names[i] = g_strdup_printf(_("desktop %i"), + i + 1); + if (it) it = g_slist_next(it); + } + + /* if we changed any names, then set the root property so we can + all agree on the names */ + OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES, + (const gchar*const*)screen_desktop_names); + } + + /* resize the pager for these names */ + pager_popup_text_width_to_strings(desktop_popup, + screen_desktop_names, + screen_num_desktops); +} + +void screen_show_desktop(gboolean show, ObClient *show_only) +{ + GList *it; + + if (show == screen_showing_desktop) return; /* no change */ + + screen_showing_desktop = show; + + if (show) { + /* hide windows bottom to top */ + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *client = it->data; + client_showhide(client); + } + } + } + else { + /* restore windows top to bottom */ + for (it = stacking_list; it; it = g_list_next(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *client = it->data; + if (client_should_show(client)) { + if (!show_only || client == show_only) + client_show(client); + else + client_iconify(client, TRUE, FALSE, TRUE); + } + } + } + } + + if (show) { + /* focus the desktop */ + for (it = focus_order; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (c->type == OB_CLIENT_TYPE_DESKTOP && + (c->desktop == screen_desktop || c->desktop == DESKTOP_ALL) && + client_focus(it->data)) + break; + } + } + else if (!show_only) { + ObClient *c; + + if ((c = focus_fallback(TRUE, FALSE, TRUE, FALSE))) { + /* only do the flicker reducing stuff ahead of time if we are going + to call xsetinputfocus on the window ourselves. otherwise there + is no guarantee the window will actually take focus.. */ + if (c->can_focus) { + /* reduce flicker by hiliting now rather than waiting for the + server FocusIn event */ + frame_adjust_focus(c->frame, TRUE); + } + } + } + + show = !!show; /* make it boolean */ + OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show); +} + +void screen_install_colormap(ObClient *client, gboolean install) +{ + if (client == NULL || client->colormap == None) { + if (install) + XInstallColormap(obt_display, RrColormap(ob_rr_inst)); + else + XUninstallColormap(obt_display, RrColormap(ob_rr_inst)); + } else { + obt_display_ignore_errors(TRUE); + if (install) + XInstallColormap(obt_display, client->colormap); + else + XUninstallColormap(obt_display, client->colormap); + obt_display_ignore_errors(FALSE); + } +} + +typedef struct { + guint desktop; + StrutPartial *strut; +} ObScreenStrut; + +#define RESET_STRUT_LIST(sl) \ + while (sl) { \ + g_slice_free(ObScreenStrut, (sl)->data); \ + sl = g_slist_delete_link(sl, sl); \ + } + +#define ADD_STRUT_TO_LIST(sl, d, s) \ +{ \ + ObScreenStrut *ss = g_slice_new(ObScreenStrut); \ + ss->desktop = d; \ + ss->strut = s; \ + sl = g_slist_prepend(sl, ss); \ +} + +#define VALIDATE_STRUTS(sl, side, max) \ +{ \ + GSList *it; \ + for (it = sl; it; it = g_slist_next(it)) { \ + ObScreenStrut *ss = it->data; \ + ss->strut->side = MIN(max, ss->strut->side); \ + } \ +} + +static void get_xinerama_screens(Rect **xin_areas, guint *nxin) +{ + guint i; + gint n, l, r, t, b; +#ifdef XINERAMA + XineramaScreenInfo *info; +#endif + + if (ob_debug_xinerama) { + gint w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)); + gint h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen)); + *nxin = 2; + *xin_areas = g_new(Rect, *nxin + 1); + RECT_SET((*xin_areas)[0], 0, 0, w/2, h); + RECT_SET((*xin_areas)[1], w/2, 0, w-(w/2), h); + } +#ifdef XINERAMA + else if (obt_display_extension_xinerama && + (info = XineramaQueryScreens(obt_display, &n))) { + *nxin = n; + *xin_areas = g_new(Rect, *nxin + 1); + for (i = 0; i < *nxin; ++i) + RECT_SET((*xin_areas)[i], info[i].x_org, info[i].y_org, + info[i].width, info[i].height); + XFree(info); + } +#endif + else { + *nxin = 1; + *xin_areas = g_new(Rect, *nxin + 1); + RECT_SET((*xin_areas)[0], 0, 0, + WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen)), + HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen))); + } + + /* returns one extra with the total area in it */ + l = (*xin_areas)[0].x; + t = (*xin_areas)[0].y; + r = (*xin_areas)[0].x + (*xin_areas)[0].width - 1; + b = (*xin_areas)[0].y + (*xin_areas)[0].height - 1; + for (i = 1; i < *nxin; ++i) { + l = MIN(l, (*xin_areas)[i].x); + t = MIN(l, (*xin_areas)[i].y); + r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1); + b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1); + } + RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1); +} + +void screen_update_areas(void) +{ + guint i; + gulong *dims; + GList *it, *onscreen; + + /* collect the clients that are on screen */ + onscreen = NULL; + for (it = client_list; it; it = g_list_next(it)) { + if (client_monitor(it->data) != screen_num_monitors) + onscreen = g_list_prepend(onscreen, it->data); + } + + g_free(monitor_area); + get_xinerama_screens(&monitor_area, &screen_num_monitors); + + /* set up the user-specified margins */ + config_margins.top_start = RECT_LEFT(monitor_area[screen_num_monitors]); + config_margins.top_end = RECT_RIGHT(monitor_area[screen_num_monitors]); + config_margins.bottom_start = RECT_LEFT(monitor_area[screen_num_monitors]); + config_margins.bottom_end = RECT_RIGHT(monitor_area[screen_num_monitors]); + config_margins.left_start = RECT_TOP(monitor_area[screen_num_monitors]); + config_margins.left_end = RECT_BOTTOM(monitor_area[screen_num_monitors]); + config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]); + config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]); + + RESET_STRUT_LIST(struts_left); + RESET_STRUT_LIST(struts_top); + RESET_STRUT_LIST(struts_right); + RESET_STRUT_LIST(struts_bottom); + + /* collect the struts */ + for (it = client_list; it; it = g_list_next(it)) { + ObClient *c = it->data; + if (c->strut.left) + ADD_STRUT_TO_LIST(struts_left, c->desktop, &c->strut); + if (c->strut.top) + ADD_STRUT_TO_LIST(struts_top, c->desktop, &c->strut); + if (c->strut.right) + ADD_STRUT_TO_LIST(struts_right, c->desktop, &c->strut); + if (c->strut.bottom) + ADD_STRUT_TO_LIST(struts_bottom, c->desktop, &c->strut); + } + if (dock_strut.left) + ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &dock_strut); + if (dock_strut.top) + ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &dock_strut); + if (dock_strut.right) + ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &dock_strut); + if (dock_strut.bottom) + ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &dock_strut); + + if (config_margins.left) + ADD_STRUT_TO_LIST(struts_left, DESKTOP_ALL, &config_margins); + if (config_margins.top) + ADD_STRUT_TO_LIST(struts_top, DESKTOP_ALL, &config_margins); + if (config_margins.right) + ADD_STRUT_TO_LIST(struts_right, DESKTOP_ALL, &config_margins); + if (config_margins.bottom) + ADD_STRUT_TO_LIST(struts_bottom, DESKTOP_ALL, &config_margins); + + VALIDATE_STRUTS(struts_left, left, + monitor_area[screen_num_monitors].width / 2); + VALIDATE_STRUTS(struts_right, right, + monitor_area[screen_num_monitors].width / 2); + VALIDATE_STRUTS(struts_top, top, + monitor_area[screen_num_monitors].height / 2); + VALIDATE_STRUTS(struts_bottom, bottom, + monitor_area[screen_num_monitors].height / 2); + + dims = g_new(gulong, 4 * screen_num_desktops); + for (i = 0; i < screen_num_desktops; ++i) { + Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL); + dims[i*4+0] = area->x; + dims[i*4+1] = area->y; + dims[i*4+2] = area->width; + dims[i*4+3] = area->height; + g_slice_free(Rect, area); + } + + /* set the legacy workarea hint to the union of all the monitors */ + OBT_PROP_SETA32(obt_root(ob_screen), NET_WORKAREA, CARDINAL, + dims, 4 * screen_num_desktops); + + /* the area has changed, adjust all the windows if they need it */ + for (it = onscreen; it; it = g_list_next(it)) + client_reconfigure(it->data, FALSE); + + g_free(dims); +} + +#if 0 +Rect* screen_area_all_monitors(guint desktop) +{ + guint i; + Rect *a; + + a = screen_area_monitor(desktop, 0); + + /* combine all the monitors together */ + for (i = 1; i < screen_num_monitors; ++i) { + Rect *m = screen_area_monitor(desktop, i); + gint l, r, t, b; + + l = MIN(RECT_LEFT(*a), RECT_LEFT(*m)); + t = MIN(RECT_TOP(*a), RECT_TOP(*m)); + r = MAX(RECT_RIGHT(*a), RECT_RIGHT(*m)); + b = MAX(RECT_BOTTOM(*a), RECT_BOTTOM(*m)); + + RECT_SET(*a, l, t, r - l + 1, b - t + 1); + + g_free(m); + } + + return a; +} +#endif + +#define STRUT_LEFT_IN_SEARCH(s, search) \ + (RANGES_INTERSECT(search->y, search->height, \ + s->left_start, s->left_end - s->left_start + 1)) +#define STRUT_RIGHT_IN_SEARCH(s, search) \ + (RANGES_INTERSECT(search->y, search->height, \ + s->right_start, s->right_end - s->right_start + 1)) +#define STRUT_TOP_IN_SEARCH(s, search) \ + (RANGES_INTERSECT(search->x, search->width, \ + s->top_start, s->top_end - s->top_start + 1)) +#define STRUT_BOTTOM_IN_SEARCH(s, search) \ + (RANGES_INTERSECT(search->x, search->width, \ + s->bottom_start, s->bottom_end - s->bottom_start + 1)) + +#define STRUT_LEFT_IGNORE(s, us, search) \ + (head == SCREEN_AREA_ALL_MONITORS && us && \ + RECT_LEFT(monitor_area[i]) + s->left > RECT_LEFT(*search)) +#define STRUT_RIGHT_IGNORE(s, us, search) \ + (head == SCREEN_AREA_ALL_MONITORS && us && \ + RECT_RIGHT(monitor_area[i]) - s->right < RECT_RIGHT(*search)) +#define STRUT_TOP_IGNORE(s, us, search) \ + (head == SCREEN_AREA_ALL_MONITORS && us && \ + RECT_TOP(monitor_area[i]) + s->top > RECT_TOP(*search)) +#define STRUT_BOTTOM_IGNORE(s, us, search) \ + (head == SCREEN_AREA_ALL_MONITORS && us && \ + RECT_BOTTOM(monitor_area[i]) - s->bottom < RECT_BOTTOM(*search)) + +Rect* screen_area(guint desktop, guint head, Rect *search) +{ + Rect *a; + GSList *it; + gint l, r, t, b; + guint i, d; + gboolean us = search != NULL; /* user provided search */ + + g_assert(desktop < screen_num_desktops || desktop == DESKTOP_ALL); + g_assert(head < screen_num_monitors || head == SCREEN_AREA_ONE_MONITOR || + head == SCREEN_AREA_ALL_MONITORS); + g_assert(!(head == SCREEN_AREA_ONE_MONITOR && search == NULL)); + + /* find any struts for this monitor + which will be affecting the search area. + */ + + /* search everything if search is null */ + if (!search) { + if (head < screen_num_monitors) search = &monitor_area[head]; + else search = &monitor_area[screen_num_monitors]; + } + if (head == SCREEN_AREA_ONE_MONITOR) head = screen_find_monitor(search); + + /* al is "all left" meaning the furthest left you can get, l is our + "working left" meaning our current strut edge which we're calculating + */ + + /* only include monitors which the search area lines up with */ + if (RECT_INTERSECTS_RECT(monitor_area[screen_num_monitors], *search)) { + l = RECT_RIGHT(monitor_area[screen_num_monitors]); + t = RECT_BOTTOM(monitor_area[screen_num_monitors]); + r = RECT_LEFT(monitor_area[screen_num_monitors]); + b = RECT_TOP(monitor_area[screen_num_monitors]); + for (i = 0; i < screen_num_monitors; ++i) { + /* add the monitor if applicable */ + if (RANGES_INTERSECT(search->x, search->width, + monitor_area[i].x, monitor_area[i].width)) + { + t = MIN(t, RECT_TOP(monitor_area[i])); + b = MAX(b, RECT_BOTTOM(monitor_area[i])); + } + if (RANGES_INTERSECT(search->y, search->height, + monitor_area[i].y, monitor_area[i].height)) + { + l = MIN(l, RECT_LEFT(monitor_area[i])); + r = MAX(r, RECT_RIGHT(monitor_area[i])); + } + } + } else { + l = RECT_LEFT(monitor_area[screen_num_monitors]); + t = RECT_TOP(monitor_area[screen_num_monitors]); + r = RECT_RIGHT(monitor_area[screen_num_monitors]); + b = RECT_BOTTOM(monitor_area[screen_num_monitors]); + } + + for (d = 0; d < screen_num_desktops; ++d) { + if (d != desktop && desktop != DESKTOP_ALL) continue; + + for (i = 0; i < screen_num_monitors; ++i) { + if (head != SCREEN_AREA_ALL_MONITORS && head != i) continue; + + for (it = struts_left; it; it = g_slist_next(it)) { + ObScreenStrut *s = it->data; + if ((s->desktop == d || s->desktop == DESKTOP_ALL) && + STRUT_LEFT_IN_SEARCH(s->strut, search) && + !STRUT_LEFT_IGNORE(s->strut, us, search)) + l = MAX(l, RECT_LEFT(monitor_area[screen_num_monitors]) + + s->strut->left); + } + for (it = struts_top; it; it = g_slist_next(it)) { + ObScreenStrut *s = it->data; + if ((s->desktop == d || s->desktop == DESKTOP_ALL) && + STRUT_TOP_IN_SEARCH(s->strut, search) && + !STRUT_TOP_IGNORE(s->strut, us, search)) + t = MAX(t, RECT_TOP(monitor_area[screen_num_monitors]) + + s->strut->top); + } + for (it = struts_right; it; it = g_slist_next(it)) { + ObScreenStrut *s = it->data; + if ((s->desktop == d || s->desktop == DESKTOP_ALL) && + STRUT_RIGHT_IN_SEARCH(s->strut, search) && + !STRUT_RIGHT_IGNORE(s->strut, us, search)) + r = MIN(r, RECT_RIGHT(monitor_area[screen_num_monitors]) + - s->strut->right); + } + for (it = struts_bottom; it; it = g_slist_next(it)) { + ObScreenStrut *s = it->data; + if ((s->desktop == d || s->desktop == DESKTOP_ALL) && + STRUT_BOTTOM_IN_SEARCH(s->strut, search) && + !STRUT_BOTTOM_IGNORE(s->strut, us, search)) + b = MIN(b, RECT_BOTTOM(monitor_area[screen_num_monitors]) + - s->strut->bottom); + } + + /* limit to this monitor */ + if (head == i) { + l = MAX(l, RECT_LEFT(monitor_area[i])); + t = MAX(t, RECT_TOP(monitor_area[i])); + r = MIN(r, RECT_RIGHT(monitor_area[i])); + b = MIN(b, RECT_BOTTOM(monitor_area[i])); + } + } + } + + a = g_slice_new(Rect); + a->x = l; + a->y = t; + a->width = r - l + 1; + a->height = b - t + 1; + return a; +} + +guint screen_find_monitor(const Rect *search) +{ + guint i; + guint most = screen_num_monitors; + guint mostv = 0; + + for (i = 0; i < screen_num_monitors; ++i) { + const Rect *area = screen_physical_area_monitor(i); + if (RECT_INTERSECTS_RECT(*area, *search)) { + Rect r; + guint v; + + RECT_SET_INTERSECTION(r, *area, *search); + v = r.width * r.height; + + if (v > mostv) { + mostv = v; + most = i; + } + } + } + return most < screen_num_monitors ? most : screen_monitor_primary(FALSE); +} + +const Rect* screen_physical_area_all_monitors(void) +{ + return screen_physical_area_monitor(screen_num_monitors); +} + +const Rect* screen_physical_area_monitor(guint head) +{ + g_assert(head <= screen_num_monitors); + + return &monitor_area[head]; +} + +gboolean screen_physical_area_monitor_contains(guint head, Rect *search) +{ + g_assert(head <= screen_num_monitors); + g_assert(search); + return RECT_INTERSECTS_RECT(monitor_area[head], *search); +} + +guint screen_monitor_active(void) +{ + if (moveresize_client) + return client_monitor(moveresize_client); + else if (focus_client) + return client_monitor(focus_client); + else + return screen_monitor_pointer(); +} + +const Rect* screen_physical_area_active(void) +{ + return screen_physical_area_monitor(screen_monitor_active()); +} + +guint screen_monitor_primary(gboolean fixed) +{ + if (config_primary_monitor_index > 0) { + if (config_primary_monitor_index-1 < screen_num_monitors) + return config_primary_monitor_index - 1; + else + return 0; + } + else if (fixed) + return 0; + else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE) + return screen_monitor_active(); + else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */ + return screen_monitor_pointer(); +} + +const Rect* screen_physical_area_primary(gboolean fixed) +{ + return screen_physical_area_monitor(screen_monitor_primary(fixed)); +} + +void screen_set_root_cursor(void) +{ + if (sn_app_starting()) + XDefineCursor(obt_display, obt_root(ob_screen), + ob_cursor(OB_CURSOR_BUSYPOINTER)); + else + XDefineCursor(obt_display, obt_root(ob_screen), + ob_cursor(OB_CURSOR_POINTER)); +} + +guint screen_find_monitor_point(guint x, guint y) +{ + Rect mon; + RECT_SET(mon, x, y, 1, 1); + return screen_find_monitor(&mon); +} + +guint screen_monitor_pointer() +{ + gint x, y; + if (!screen_pointer_pos(&x, &y)) + x = y = 0; + return screen_find_monitor_point(x, y); +} + +gboolean screen_pointer_pos(gint *x, gint *y) +{ + Window w; + gint i; + guint u; + gboolean ret; + + ret = !!XQueryPointer(obt_display, obt_root(ob_screen), + &w, &w, x, y, &i, &i, &u); + if (!ret) { + for (i = 0; i < ScreenCount(obt_display); ++i) + if (i != ob_screen) + if (XQueryPointer(obt_display, obt_root(i), + &w, &w, x, y, &i, &i, &u)) + break; + } + return ret; +} + +gboolean screen_compare_desktops(guint a, guint b) +{ + if (a == DESKTOP_ALL) + a = screen_desktop; + if (b == DESKTOP_ALL) + b = screen_desktop; + return a == b; +} diff --git a/openbox/screen.h b/openbox/screen.h new file mode 100644 index 0000000..a6a3995 --- /dev/null +++ b/openbox/screen.h @@ -0,0 +1,175 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + screen.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __screen_h +#define __screen_h + +#include "misc.h" +#include "geom.h" + +struct _ObClient; + +#define DESKTOP_ALL (0xffffffff) + +/*! The number of available desktops */ +extern guint screen_num_desktops; +/*! The number of virtual "xinerama" screens/heads */ +extern guint screen_num_monitors; +/*! The current desktop */ +extern guint screen_desktop; +/*! The desktop which was last visible */ +extern guint screen_last_desktop; +/*! Are we in showing-desktop mode? */ +extern gboolean screen_showing_desktop; +/*! The support window also used for focus and stacking */ +extern Window screen_support_win; +/*! The last time at which the user changed desktops */ +extern Time screen_desktop_user_time; + +typedef struct ObDesktopLayout { + ObOrientation orientation; + ObCorner start_corner; + guint rows; + guint columns; +} ObDesktopLayout; +extern ObDesktopLayout screen_desktop_layout; + +/*! An array of gchar*'s which are desktop names in UTF-8 format */ +extern gchar **screen_desktop_names; + +/*! Take over the screen, set the basic hints on it claming it as ours */ +gboolean screen_annex(void); + +/*! Once the screen is ours, set up its initial state */ +void screen_startup(gboolean reconfig); +/*! Free resources */ +void screen_shutdown(gboolean reconfig); + +/*! Figure out the new size of the screen and adjust stuff for it */ +void screen_resize(void); + +/*! Change the number of available desktops */ +void screen_set_num_desktops(guint num); +/*! Change the current desktop */ +void screen_set_desktop(guint num, gboolean dofocus); +/*! Add a new desktop either at the end or inserted at the current desktop */ +void screen_add_desktop(gboolean current); +/*! Remove a desktop, either at the end or the current desktop */ +void screen_remove_desktop(gboolean current); + +guint screen_find_desktop(guint from, ObDirection dir, + gboolean wrap, gboolean linear); + +/*! Show the desktop popup/notification + @permanent If TRUE, the popup will stay on the screen until you call + screen_hide_desktop_popup(). Otherwise it will hide after a + delay. + */ +void screen_show_desktop_popup(guint d, gboolean permanent); +/*! Hide it */ +void screen_hide_desktop_popup(void); + +/*! Shows and focuses the desktop and hides all the client windows, or + returns to the normal state, showing client windows. + @param If show_only is non-NULL, then only that client is shown (assuming + show is FALSE (restoring from show-desktop mode), and the rest are + iconified. +*/ +void screen_show_desktop(gboolean show, struct _ObClient *show_only); + +/*! Updates the desktop layout from the root property if available */ +void screen_update_layout(void); + +/*! Get desktop names from the root window property */ +void screen_update_desktop_names(void); + +/*! Installs or uninstalls a colormap for a client. If client is NULL, then + it handles the root colormap. */ +void screen_install_colormap(struct _ObClient *client, gboolean install); + +void screen_update_areas(void); + +const Rect* screen_physical_area_all_monitors(void); + +/*! Returns a Rect which is owned by the screen code and should not be freed */ +const Rect* screen_physical_area_monitor(guint head); + +/*! Returns the monitor which contains the active window, or the one + containing the pointer otherwise. */ +guint screen_monitor_active(void); + +/*! Returns a Rect which is owned by the screen code and should not be freed */ +const Rect* screen_physical_area_active(void); + +/*! Returns the primary monitor, as specified by the config. + @fixed If TRUE, then this will always return a fixed monitor, otherwise + it may change based on where focus is, or other heuristics. + */ +guint screen_monitor_primary(gboolean fixed); + +/*! Returns physical area for the primary monitor, as specified by the config. + @fixed If TRUE, then this will always use a fixed monitor as primary, + otherwise it may change based on where focus is, or other heuristics. + See screen_monitor_primary(). + @return A Rect which is owned by the screen code and should not be freed +*/ +const Rect* screen_physical_area_primary(gboolean fixed); + +/* doesn't include struts which the search area is already outside of when + 'search' is not NULL */ +#define SCREEN_AREA_ALL_MONITORS ((unsigned)-1) +#define SCREEN_AREA_ONE_MONITOR ((unsigned)-2) + +/*! @param head is the number of the head or one of SCREEN_AREA_ALL_MONITORS, + SCREEN_AREA_ONE_MONITOR + @param search NULL or the whole monitor(s) + @return A Rect allocated with g_slice_new() + */ +Rect* screen_area(guint desktop, guint head, Rect *search); + +gboolean screen_physical_area_monitor_contains(guint head, Rect *search); + +/*! Determines which physical monitor a rectangle is on by calculating the + area of the part of the rectable on each monitor. The number of the + monitor containing the greatest area of the rectangle is returned. +*/ +guint screen_find_monitor(const Rect *search); + +/*! Finds the monitor which contains the point @x, @y */ +guint screen_find_monitor_point(guint x, guint y); + +/*! Sets the root cursor. This function decides which cursor to use, but you + gotta call it to let it know it should change. */ +void screen_set_root_cursor(void); + +/*! Gives back the pointer's position in x and y. Returns TRUE if the pointer + is on this screen and FALSE if it is on another screen. */ +gboolean screen_pointer_pos(gint *x, gint *y); + +/*! Returns the monitor which contains the pointer device */ +guint screen_monitor_pointer(void); + +/*! Compare the desktop for two windows to see if they are considered on the + same desktop. + Windows that are on "all desktops" are treated like they are only on the + current desktop, so they are only in one place at a time. + @return TRUE if they are on the same desktop, FALSE otherwise. +*/ +gboolean screen_compare_desktops(guint a, guint b); + +#endif diff --git a/openbox/session.c b/openbox/session.c new file mode 100644 index 0000000..d6c6f76 --- /dev/null +++ b/openbox/session.c @@ -0,0 +1,866 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + session.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +/* This session code is largely inspired by metacity code. */ + +#include "session.h" + +struct _ObClient; + +GList *session_saved_state = NULL; +gint session_desktop = -1; +gint session_num_desktops = 0; +gboolean session_desktop_layout_present = FALSE; +ObDesktopLayout session_desktop_layout; +GSList *session_desktop_names = NULL; + +#ifndef USE_SM +void session_startup(gint argc, gchar **argv) {} +void session_shutdown(gboolean permanent) {} +GList* session_state_find(struct _ObClient *c) { return NULL; } +void session_request_logout(gboolean silent) {} +gboolean session_connected(void) { return FALSE; } +#else + +#include "debug.h" +#include "openbox.h" +#include "client.h" +#include "focus.h" +#include "gettext.h" +#include "obt/xml.h" +#include "obt/paths.h" + +#include <time.h> +#include <errno.h> +#include <stdio.h> + +#ifdef HAVE_UNISTD_H +# include <sys/types.h> +# include <unistd.h> +#endif + +#include <X11/SM/SMlib.h> + +#define SM_ERR_LEN 1024 + +static SmcConn sm_conn; +static gint sm_argc; +static gchar **sm_argv; + +/* Data saved from the first level save yourself */ +typedef struct { + ObClient *focus_client; + gint desktop; +} ObSMSaveData; + +static gboolean session_connect(); + +static void session_load_file(const gchar *path); +static gboolean session_save_to_file(const ObSMSaveData *savedata); + +static void session_setup_program(); +static void session_setup_user(); +static void session_setup_restart_style(gboolean restart); +static void session_setup_pid(); +static void session_setup_priority(); +static void session_setup_clone_command(); +static void session_setup_restart_command(); + +static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type, + Bool shutdown, gint interact_style, Bool fast); +static void sm_die(SmcConn conn, SmPointer data); +static void sm_save_complete(SmcConn conn, SmPointer data); +static void sm_shutdown_cancelled(SmcConn conn, SmPointer data); + +static gboolean session_state_cmp(ObSessionState *s, ObClient *c); +static void session_state_free(ObSessionState *state); + +void session_startup(gint argc, gchar **argv) +{ + gchar *dir; + ObtPaths *p; + + if (!ob_sm_use) return; + + sm_argc = argc; + sm_argv = argv; + + p = obt_paths_new(); + dir = g_build_filename(obt_paths_cache_home(p), + "openbox", "sessions", NULL); + obt_paths_unref(p), p = NULL; + + if (!obt_paths_mkdir_path(dir, 0700)) { + g_message(_("Unable to make directory \"%s\": %s"), + dir, g_strerror(errno)); + } + + if (ob_sm_save_file != NULL) { + if (ob_sm_restore) { + ob_debug_type(OB_DEBUG_SM, "Loading from session file %s", + ob_sm_save_file); + session_load_file(ob_sm_save_file); + } + } else { + gchar *filename; + + /* this algo is from metacity */ + filename = g_strdup_printf("%u-%u-%u.obs", + (guint)time(NULL), + (guint)getpid(), + g_random_int()); + ob_sm_save_file = g_build_filename(dir, filename, NULL); + g_free(filename); + } + + if (session_connect()) { + session_setup_program(); + session_setup_user(); + session_setup_restart_style(TRUE); + session_setup_pid(); + session_setup_priority(); + session_setup_clone_command(); + } + + g_free(dir); +} + +void session_shutdown(gboolean permanent) +{ + if (!ob_sm_use) return; + + if (sm_conn) { + /* if permanent is true then we will change our session state so that + the SM won't run us again */ + if (permanent) + session_setup_restart_style(FALSE); + + SmcCloseConnection(sm_conn, 0, NULL); + + while (session_saved_state) { + session_state_free(session_saved_state->data); + session_saved_state = g_list_delete_link(session_saved_state, + session_saved_state); + } + } +} + +gboolean session_connected(void) +{ + return !!sm_conn; +} + +/*! Connect to the session manager and set up our callback functions */ +static gboolean session_connect(void) +{ + SmcCallbacks cb; + gchar *oldid; + gchar sm_err[SM_ERR_LEN]; + + /* set up our callback functions */ + cb.save_yourself.callback = sm_save_yourself; + cb.save_yourself.client_data = NULL; + cb.die.callback = sm_die; + cb.die.client_data = NULL; + cb.save_complete.callback = sm_save_complete; + cb.save_complete.client_data = NULL; + cb.shutdown_cancelled.callback = sm_shutdown_cancelled; + cb.shutdown_cancelled.client_data = NULL; + + /* connect to the server */ + oldid = ob_sm_id; + ob_debug_type(OB_DEBUG_SM, "Connecting to SM with id: %s", + oldid ? oldid : "(null)"); + sm_conn = SmcOpenConnection(NULL, NULL, 1, 0, + SmcSaveYourselfProcMask | + SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &cb, oldid, &ob_sm_id, + SM_ERR_LEN-1, sm_err); + g_free(oldid); + ob_debug_type(OB_DEBUG_SM, "Connected to SM with id: %s", ob_sm_id); + if (sm_conn == NULL) + ob_debug("Failed to connect to session manager: %s", sm_err); + return sm_conn != NULL; +} + +static void session_setup_program(void) +{ + SmPropValue vals = { + .value = sm_argv[0], + .length = strlen(sm_argv[0]) + 1 + }; + SmProp prop = { + .name = g_strdup(SmProgram), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + ob_debug_type(OB_DEBUG_SM, "Setting program: %s", sm_argv[0]); + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} + +static void session_setup_user(void) +{ + char *user = g_strdup(g_get_user_name()); + + SmPropValue vals = { + .value = user, + .length = strlen(user) + 1 + }; + SmProp prop = { + .name = g_strdup(SmUserID), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + ob_debug_type(OB_DEBUG_SM, "Setting user: %s", user); + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(user); +} + +static void session_setup_restart_style(gboolean restart) +{ + gchar restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning; + + SmPropValue vals = { + .value = &restart_hint, + .length = 1 + }; + SmProp prop = { + .name = g_strdup(SmRestartStyleHint), + .type = g_strdup(SmCARD8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + ob_debug_type(OB_DEBUG_SM, "Setting restart: %d", restart); + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} + +static void session_setup_pid(void) +{ + gchar *pid = g_strdup_printf("%ld", (glong) getpid()); + + SmPropValue vals = { + .value = pid, + .length = strlen(pid) + 1 + }; + SmProp prop = { + .name = g_strdup(SmProcessID), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + ob_debug_type(OB_DEBUG_SM, "Setting pid: %s", pid); + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(pid); +} + +/*! This is a gnome-session-manager extension */ +static void session_setup_priority(void) +{ + gchar priority = 20; /* 20 is a lower prioity to run before other apps */ + + SmPropValue vals = { + .value = &priority, + .length = 1 + }; + SmProp prop = { + .name = g_strdup("_GSM_Priority"), + .type = g_strdup(SmCARD8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + ob_debug_type(OB_DEBUG_SM, "Setting priority: %d", priority); + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} + +static void session_setup_clone_command(void) +{ + gint i; + + SmPropValue *vals = g_new(SmPropValue, sm_argc); + SmProp prop = { + .name = g_strdup(SmCloneCommand), + .type = g_strdup(SmLISTofARRAY8), + .num_vals = sm_argc, + .vals = vals + }; + SmProp *list = ∝ + + ob_debug_type(OB_DEBUG_SM, "Setting clone command: (%d)", sm_argc); + for (i = 0; i < sm_argc; ++i) { + vals[i].value = sm_argv[i]; + vals[i].length = strlen(sm_argv[i]) + 1; + ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value); + } + + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(vals); +} + +static void session_setup_restart_command(void) +{ + gint i; + + SmPropValue *vals = g_new(SmPropValue, sm_argc + 4); + SmProp prop = { + .name = g_strdup(SmRestartCommand), + .type = g_strdup(SmLISTofARRAY8), + .num_vals = sm_argc + 4, + .vals = vals + }; + SmProp *list = ∝ + + ob_debug_type(OB_DEBUG_SM, "Setting restart command: (%d)", sm_argc+4); + for (i = 0; i < sm_argc; ++i) { + vals[i].value = sm_argv[i]; + vals[i].length = strlen(sm_argv[i]) + 1; + ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value); + } + + vals[i].value = g_strdup("--sm-client-id"); + vals[i].length = strlen("--sm-client-id") + 1; + vals[i+1].value = ob_sm_id; + vals[i+1].length = strlen(ob_sm_id) + 1; + ob_debug_type(OB_DEBUG_SM, " %s", vals[i].value); + ob_debug_type(OB_DEBUG_SM, " %s", vals[i+1].value); + + vals[i+2].value = g_strdup("--sm-save-file"); + vals[i+2].length = strlen("--sm-save-file") + 1; + vals[i+3].value = ob_sm_save_file; + vals[i+3].length = strlen(ob_sm_save_file) + 1; + ob_debug_type(OB_DEBUG_SM, " %s", vals[i+2].value); + ob_debug_type(OB_DEBUG_SM, " %s", vals[i+3].value); + + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(vals[i].value); + g_free(vals[i+2].value); + g_free(vals); +} + +static ObSMSaveData *sm_save_get_data(void) +{ + ObSMSaveData *savedata = g_slice_new0(ObSMSaveData); + /* save the active desktop and client. + we don't bother to preemptively save the other desktop state like + number and names of desktops, cuz those shouldn't be changing during + the save.. */ + savedata->focus_client = focus_client; + savedata->desktop = screen_desktop; + return savedata; +} + +static void sm_save_yourself_2(SmcConn conn, SmPointer data) +{ + gboolean success; + ObSMSaveData *savedata = data; + + /* save the current state */ + ob_debug_type(OB_DEBUG_SM, "Session save phase 2 requested"); + ob_debug_type(OB_DEBUG_SM, + " Saving session to file '%s'", ob_sm_save_file); + if (savedata == NULL) + savedata = sm_save_get_data(); + success = session_save_to_file(savedata); + g_slice_free(ObSMSaveData, savedata); + + /* tell the session manager how to restore this state */ + if (success) session_setup_restart_command(); + + ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)", success); + SmcSaveYourselfDone(conn, success); +} + +static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type, + Bool shutdown, gint interact_style, Bool fast) +{ + ObSMSaveData *savedata = NULL; + gchar *vendor; + +#ifdef DEBUG + { + const gchar *sname = + (save_type == SmSaveLocal ? "SmSaveLocal" : + (save_type == SmSaveGlobal ? "SmSaveGlobal" : + (save_type == SmSaveBoth ? "SmSaveBoth" : "INVALID!!"))); + ob_debug_type(OB_DEBUG_SM, "Session save requested, type %s", sname); + } +#endif + + if (save_type == SmSaveGlobal) { + /* we have no data to save. we only store state to get back to where + we were, we don't keep open writable files or anything */ + SmcSaveYourselfDone(conn, TRUE); + return; + } + + vendor = SmcVendor(sm_conn); + ob_debug_type(OB_DEBUG_SM, "Session manager's vendor: %s", vendor); + + if (!strcmp(vendor, "KDE")) { + /* ksmserver guarantees that phase 1 will complete before allowing any + clients interaction, so we can save this sanely here before they + get messed up from interaction */ + savedata = sm_save_get_data(); + } + free(vendor); + + if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) { + ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed"); + g_slice_free(ObSMSaveData, savedata); + SmcSaveYourselfDone(conn, FALSE); + } +} + +static void sm_die(SmcConn conn, SmPointer data) +{ + ob_debug_type(OB_DEBUG_SM, "Die requested"); + ob_exit(0); +} + +static void sm_save_complete(SmcConn conn, SmPointer data) +{ + ob_debug_type(OB_DEBUG_SM, "Save complete"); +} + +static void sm_shutdown_cancelled(SmcConn conn, SmPointer data) +{ + ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled"); +} + +static gboolean session_save_to_file(const ObSMSaveData *savedata) +{ + FILE *f; + GList *it; + gboolean success = TRUE; + + f = fopen(ob_sm_save_file, "w"); + if (!f) { + success = FALSE; + g_message(_("Unable to save the session to \"%s\": %s"), + ob_sm_save_file, g_strerror(errno)); + } else { + fprintf(f, "<?xml version=\"1.0\"?>\n\n"); + fprintf(f, "<openbox_session>\n\n"); + + fprintf(f, "<desktop>%d</desktop>\n", savedata->desktop); + + fprintf(f, "<numdesktops>%d</numdesktops>\n", screen_num_desktops); + + fprintf(f, "<desktoplayout>\n"); + fprintf(f, " <orientation>%d</orientation>\n", + screen_desktop_layout.orientation); + fprintf(f, " <startcorner>%d</startcorner>\n", + screen_desktop_layout.start_corner); + fprintf(f, " <columns>%d</columns>\n", + screen_desktop_layout.columns); + fprintf(f, " <rows>%d</rows>\n", + screen_desktop_layout.rows); + fprintf(f, "</desktoplayout>\n"); + + if (screen_desktop_names) { + gint i; + gchar *t; + + fprintf(f, "<desktopnames>\n"); + for (i = 0; screen_desktop_names[i]; ++i){ + t = g_markup_escape_text(screen_desktop_names[i], -1); + fprintf(f, " <name>%s</name>\n", t); + g_free(t); + } + fprintf(f, "</desktopnames>\n"); + } + + /* they are ordered top to bottom in stacking order */ + for (it = stacking_list; it; it = g_list_next(it)) { + gint prex, prey, prew, preh; + ObClient *c; + gchar *t; + + if (WINDOW_IS_CLIENT(it->data)) + c = WINDOW_AS_CLIENT(it->data); + else + continue; + + if (!client_normal(c)) + continue; + + if (!c->sm_client_id) { + ob_debug_type(OB_DEBUG_SM, "Client %s does not have a " + "session id set", + c->title); + if (!c->wm_command) { + ob_debug_type(OB_DEBUG_SM, "Client %s does not have an " + "oldskool wm_command set either. We won't " + "be saving its data", + c->title); + continue; + } + } + + ob_debug_type(OB_DEBUG_SM, "Saving state for client %s", + c->title); + + prex = c->area.x; + prey = c->area.y; + prew = c->area.width; + preh = c->area.height; + if (c->fullscreen) { + prex = c->pre_fullscreen_area.x; + prey = c->pre_fullscreen_area.x; + prew = c->pre_fullscreen_area.width; + preh = c->pre_fullscreen_area.height; + } + if (c->max_horz) { + prex = c->pre_max_area.x; + prew = c->pre_max_area.width; + } + if (c->max_vert) { + prey = c->pre_max_area.y; + preh = c->pre_max_area.height; + } + + if (c->sm_client_id) + fprintf(f, "<window id=\"%s\">\n", c->sm_client_id); + else { + t = g_markup_escape_text(c->wm_command, -1); + fprintf(f, "<window command=\"%s\">\n", t); + g_free(t); + } + + t = g_markup_escape_text(c->name, -1); + fprintf(f, "\t<name>%s</name>\n", t); + g_free(t); + + t = g_markup_escape_text(c->class, -1); + fprintf(f, "\t<class>%s</class>\n", t); + g_free(t); + + t = g_markup_escape_text(c->role, -1); + fprintf(f, "\t<role>%s</role>\n", t); + g_free(t); + + fprintf(f, "\t<windowtype>%d</windowtype>\n", c->type); + + fprintf(f, "\t<desktop>%d</desktop>\n", c->desktop); + fprintf(f, "\t<x>%d</x>\n", prex); + fprintf(f, "\t<y>%d</y>\n", prey); + fprintf(f, "\t<width>%d</width>\n", prew); + fprintf(f, "\t<height>%d</height>\n", preh); + if (c->shaded) + fprintf(f, "\t<shaded />\n"); + if (c->iconic) + fprintf(f, "\t<iconic />\n"); + if (c->skip_pager) + fprintf(f, "\t<skip_pager />\n"); + if (c->skip_taskbar) + fprintf(f, "\t<skip_taskbar />\n"); + if (c->fullscreen) + fprintf(f, "\t<fullscreen />\n"); + if (c->above) + fprintf(f, "\t<above />\n"); + if (c->below) + fprintf(f, "\t<below />\n"); + if (c->max_horz) + fprintf(f, "\t<max_horz />\n"); + if (c->max_vert) + fprintf(f, "\t<max_vert />\n"); + if (c->undecorated) + fprintf(f, "\t<undecorated />\n"); + if (savedata->focus_client == c) + fprintf(f, "\t<focused />\n"); + fprintf(f, "</window>\n\n"); + } + + fprintf(f, "</openbox_session>\n"); + + if (fflush(f)) { + success = FALSE; + g_message(_("Error while saving the session to \"%s\": %s"), + ob_sm_save_file, g_strerror(errno)); + } + fclose(f); + } + + return success; +} + +static void session_state_free(ObSessionState *state) +{ + if (state) { + g_free(state->id); + g_free(state->command); + g_free(state->name); + g_free(state->class); + g_free(state->role); + + g_slice_free(ObSessionState, state); + } +} + +static gboolean session_state_cmp(ObSessionState *s, ObClient *c) +{ + ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: "); + ob_debug_type(OB_DEBUG_SM, " client id: %s ", c->sm_client_id); + ob_debug_type(OB_DEBUG_SM, " client name: %s ", c->name); + ob_debug_type(OB_DEBUG_SM, " client class: %s ", c->class); + ob_debug_type(OB_DEBUG_SM, " client role: %s ", c->role); + ob_debug_type(OB_DEBUG_SM, " client type: %d ", c->type); + ob_debug_type(OB_DEBUG_SM, " client command: %s ", + c->wm_command ? c->wm_command : "(null)"); + ob_debug_type(OB_DEBUG_SM, " state id: %s ", s->id); + ob_debug_type(OB_DEBUG_SM, " state name: %s ", s->name); + ob_debug_type(OB_DEBUG_SM, " state class: %s ", s->class); + ob_debug_type(OB_DEBUG_SM, " state role: %s ", s->role); + ob_debug_type(OB_DEBUG_SM, " state type: %d ", s->type); + ob_debug_type(OB_DEBUG_SM, " state command: %s ", + s->command ? s->command : "(null)"); + + if ((c->sm_client_id && s->id && !strcmp(c->sm_client_id, s->id)) || + (c->wm_command && s->command && !strcmp(c->wm_command, s->command))) + { + return (!strcmp(s->name, c->name) && + !strcmp(s->class, c->class) && + !strcmp(s->role, c->role) && + /* the check for type is to catch broken clients, like + firefox, which open a different window on startup + with the same info as the one we saved. only do this + check for old windows that dont use xsmp, others should + know better ! */ + (!s->command || c->type == s->type)); + } + return FALSE; +} + +GList* session_state_find(ObClient *c) +{ + GList *it; + + for (it = session_saved_state; it; it = g_list_next(it)) { + ObSessionState *s = it->data; + if (!s->matched && session_state_cmp(s, c)) { + s->matched = TRUE; + break; + } + } + return it; +} + +static void session_load_file(const gchar *path) +{ + ObtXmlInst *i; + xmlNodePtr node, n, m; + GList *it, *inext; + + i = obt_xml_instance_new(); + + if (!obt_xml_load_file(i, path, "openbox_session")) { + ob_debug_type(OB_DEBUG_SM, "ERROR: session file is missing root node"); + obt_xml_instance_unref(i); + return; + } + node = obt_xml_root(i); + + if ((n = obt_xml_find_node(node->children, "desktop"))) + session_desktop = obt_xml_node_int(n); + + if ((n = obt_xml_find_node(node->children, "numdesktops"))) + session_num_desktops = obt_xml_node_int(n); + + if ((n = obt_xml_find_node(node->children, "desktoplayout"))) { + /* make sure they are all there for it to be valid */ + if ((m = obt_xml_find_node(n->children, "orientation"))) + session_desktop_layout.orientation = obt_xml_node_int(m); + if (m && (m = obt_xml_find_node(n->children, "startcorner"))) + session_desktop_layout.start_corner = obt_xml_node_int(m); + if (m && (m = obt_xml_find_node(n->children, "columns"))) + session_desktop_layout.columns = obt_xml_node_int(m); + if (m && (m = obt_xml_find_node(n->children, "rows"))) + session_desktop_layout.rows = obt_xml_node_int(m); + session_desktop_layout_present = m != NULL; + } + + if ((n = obt_xml_find_node(node->children, "desktopnames"))) { + for (m = obt_xml_find_node(n->children, "name"); m; + m = obt_xml_find_node(m->next, "name")) + { + session_desktop_names = g_slist_append(session_desktop_names, + obt_xml_node_string(m)); + } + } + + ob_debug_type(OB_DEBUG_SM, "loading windows"); + for (node = obt_xml_find_node(node->children, "window"); node != NULL; + node = obt_xml_find_node(node->next, "window")) + { + ObSessionState *state; + + state = g_slice_new0(ObSessionState); + + if (!obt_xml_attr_string(node, "id", &state->id)) + if (!obt_xml_attr_string(node, "command", &state->command)) + goto session_load_bail; + if (!(n = obt_xml_find_node(node->children, "name"))) + goto session_load_bail; + state->name = obt_xml_node_string(n); + if (!(n = obt_xml_find_node(node->children, "class"))) + goto session_load_bail; + state->class = obt_xml_node_string(n); + if (!(n = obt_xml_find_node(node->children, "role"))) + goto session_load_bail; + state->role = obt_xml_node_string(n); + if (!(n = obt_xml_find_node(node->children, "windowtype"))) + goto session_load_bail; + state->type = obt_xml_node_int(n); + if (!(n = obt_xml_find_node(node->children, "desktop"))) + goto session_load_bail; + state->desktop = obt_xml_node_int(n); + if (!(n = obt_xml_find_node(node->children, "x"))) + goto session_load_bail; + state->x = obt_xml_node_int(n); + if (!(n = obt_xml_find_node(node->children, "y"))) + goto session_load_bail; + state->y = obt_xml_node_int(n); + if (!(n = obt_xml_find_node(node->children, "width"))) + goto session_load_bail; + state->w = obt_xml_node_int(n); + if (!(n = obt_xml_find_node(node->children, "height"))) + goto session_load_bail; + state->h = obt_xml_node_int(n); + + state->shaded = + obt_xml_find_node(node->children, "shaded") != NULL; + state->iconic = + obt_xml_find_node(node->children, "iconic") != NULL; + state->skip_pager = + obt_xml_find_node(node->children, "skip_pager") != NULL; + state->skip_taskbar = + obt_xml_find_node(node->children, "skip_taskbar") != NULL; + state->fullscreen = + obt_xml_find_node(node->children, "fullscreen") != NULL; + state->above = + obt_xml_find_node(node->children, "above") != NULL; + state->below = + obt_xml_find_node(node->children, "below") != NULL; + state->max_horz = + obt_xml_find_node(node->children, "max_horz") != NULL; + state->max_vert = + obt_xml_find_node(node->children, "max_vert") != NULL; + state->undecorated = + obt_xml_find_node(node->children, "undecorated") != NULL; + state->focused = + obt_xml_find_node(node->children, "focused") != NULL; + + /* save this. they are in the file in stacking order, so preserve + that order here */ + session_saved_state = g_list_append(session_saved_state, state); + ob_debug_type(OB_DEBUG_SM, "loaded %s", state->name); + continue; + + session_load_bail: + ob_debug_type(OB_DEBUG_SM, "loading FAILED"); + session_state_free(state); + } + + /* Remove any duplicates. This means that if two windows (or more) are + saved with the same session state, we won't restore a session for any + of them because we don't know what window to put what on. AHEM FIREFOX. + + This is going to be an O(2^n) kind of operation unfortunately. + */ + for (it = session_saved_state; it; it = inext) { + GList *jt, *jnext; + gboolean founddup = FALSE; + ObSessionState *s1 = it->data; + + inext = g_list_next(it); + + for (jt = g_list_next(it); jt; jt = jnext) { + ObSessionState *s2 = jt->data; + gboolean match; + + jnext = g_list_next(jt); + + if (s1->id && s2->id) + match = strcmp(s1->id, s2->id) == 0; + else if (s1->command && s2->command) + match = strcmp(s1->command, s2->command) == 0; + else + match = FALSE; + + if (match && + !strcmp(s1->name, s2->name) && + !strcmp(s1->class, s2->class) && + !strcmp(s1->role, s2->role)) + { + ob_debug_type(OB_DEBUG_SM, "removing duplicate %s", s2->name); + session_state_free(s2); + session_saved_state = + g_list_delete_link(session_saved_state, jt); + founddup = TRUE; + } + } + + if (founddup) { + ob_debug_type(OB_DEBUG_SM, "removing duplicate %s", s1->name); + session_state_free(s1); + session_saved_state = g_list_delete_link(session_saved_state, it); + } + } + + obt_xml_instance_unref(i); +} + +void session_request_logout(gboolean silent) +{ + if (sm_conn) { + SmcRequestSaveYourself(sm_conn, + SmSaveGlobal, + TRUE, /* logout */ + (silent ? + SmInteractStyleNone : SmInteractStyleAny), + TRUE, /* if false, with GSM, it shows the old + logout prompt */ + TRUE); /* global */ + } + else + g_message(_("Not connected to a session manager")); +} + +#endif diff --git a/openbox/session.h b/openbox/session.h new file mode 100644 index 0000000..f37e211 --- /dev/null +++ b/openbox/session.h @@ -0,0 +1,60 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + session.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ob__session_h +#define __ob__session_h + +#include "client.h" +#include "screen.h" + +#include <glib.h> + +typedef struct _ObSessionState ObSessionState; + +struct _ObSessionState { + gchar *id, *command, *name, *class, *role; + ObClientType type; + guint desktop; + gint x, y, w, h; + gboolean shaded, iconic, skip_pager, skip_taskbar, fullscreen; + gboolean above, below, max_horz, max_vert, undecorated; + gboolean focused; + + gboolean matched; +}; + +/*! The desktop being viewed when the session was saved. A valud of -1 means + it was not saved */ +extern gint session_desktop; +extern gint session_num_desktops; +extern gboolean session_desktop_layout_present; +extern ObDesktopLayout session_desktop_layout; +extern GSList *session_desktop_names; + +extern GList *session_saved_state; + +void session_startup(gint argc, gchar **argv); +void session_shutdown(gboolean permanent); + +GList* session_state_find(struct _ObClient *c); + +void session_request_logout(gboolean silent); + +gboolean session_connected(void); + +#endif diff --git a/openbox/stacking.c b/openbox/stacking.c new file mode 100644 index 0000000..58551b5 --- /dev/null +++ b/openbox/stacking.c @@ -0,0 +1,710 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + stacking.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "openbox.h" +#include "screen.h" +#include "focus.h" +#include "client.h" +#include "group.h" +#include "frame.h" +#include "window.h" +#include "event.h" +#include "debug.h" +#include "obt/prop.h" + +GList *stacking_list = NULL; +GList *stacking_list_tail = NULL; +/*! When true, stacking changes will not be reflected on the screen. This is + to freeze the on-screen stacking order while a window is being temporarily + raised during focus cycling */ +static gboolean pause_changes = FALSE; + +void stacking_set_list(void) +{ + Window *windows = NULL; + GList *it; + guint i = 0; + + /* on shutdown, don't update the properties, so that we can read it back + in on startup and re-stack the windows as they were before we shut down + */ + if (ob_state() == OB_STATE_EXITING) return; + + /* create an array of the window ids (from bottom to top, + reverse order!) */ + if (stacking_list) { + windows = g_new(Window, g_list_length(stacking_list)); + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) + windows[i++] = WINDOW_AS_CLIENT(it->data)->window; + } + } + + OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST_STACKING, WINDOW, + (gulong*)windows, i); + + g_free(windows); +} + +static void do_restack(GList *wins, GList *before) +{ + GList *it; + Window *win; + gint i; + +#ifdef DEBUG + GList *next; + + g_assert(wins); + /* pls only restack stuff in the same layer at a time */ + for (it = wins; it; it = next) { + next = g_list_next(it); + if (!next) break; + g_assert (window_layer(it->data) == window_layer(next->data)); + } + if (before) + g_assert(window_layer(it->data) >= window_layer(before->data)); +#endif + + win = g_new(Window, g_list_length(wins) + 1); + + if (before == stacking_list) + win[0] = screen_support_win; + else if (!before) + win[0] = window_top(g_list_last(stacking_list)->data); + else + win[0] = window_top(g_list_previous(before)->data); + + for (i = 1, it = wins; it; ++i, it = g_list_next(it)) { + win[i] = window_top(it->data); + g_assert(win[i] != None); /* better not call stacking shit before + setting your top level window value */ + stacking_list = g_list_insert_before(stacking_list, before, it->data); + } + +#ifdef DEBUG + /* some debug checking of the stacking list's order */ + for (it = stacking_list; ; it = next) { + next = g_list_next(it); + if (!next) break; + g_assert(window_layer(it->data) >= window_layer(next->data)); + } +#endif + + if (!pause_changes) + XRestackWindows(obt_display, win, i); + g_free(win); + + stacking_set_list(); +} + +void stacking_temp_raise(ObWindow *window) +{ + Window win[2]; + GList *it; + gulong start; + + /* don't use this for internal windows..! it would lower them.. */ + g_assert(window_layer(window) < OB_STACKING_LAYER_INTERNAL); + + /* find the window to drop it underneath */ + win[0] = screen_support_win; + for (it = stacking_list; it; it = g_list_next(it)) { + ObWindow *w = it->data; + if (window_layer(w) >= OB_STACKING_LAYER_INTERNAL) + win[0] = window_top(w); + else + break; + } + + win[1] = window_top(window); + start = event_start_ignore_all_enters(); + XRestackWindows(obt_display, win, 2); + event_end_ignore_all_enters(start); + + pause_changes = TRUE; +} + +void stacking_restore(void) +{ + Window *win; + GList *it; + gint i; + gulong start; + + win = g_new(Window, g_list_length(stacking_list) + 1); + win[0] = screen_support_win; + for (i = 1, it = stacking_list; it; ++i, it = g_list_next(it)) + win[i] = window_top(it->data); + start = event_start_ignore_all_enters(); + XRestackWindows(obt_display, win, i); + event_end_ignore_all_enters(start); + g_free(win); + + pause_changes = FALSE; +} + +static void do_raise(GList *wins) +{ + GList *it; + GList *layer[OB_NUM_STACKING_LAYERS] = {NULL}; + gint i; + + for (it = wins; it; it = g_list_next(it)) { + ObStackingLayer l; + + l = window_layer(it->data); + layer[l] = g_list_append(layer[l], it->data); + } + + it = stacking_list; + for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) { + if (layer[i]) { + for (; it; it = g_list_next(it)) { + /* look for the top of the layer */ + if (window_layer(it->data) <= (ObStackingLayer) i) + break; + } + do_restack(layer[i], it); + g_list_free(layer[i]); + } + } +} + +static void do_lower(GList *wins) +{ + GList *it; + GList *layer[OB_NUM_STACKING_LAYERS] = {NULL}; + gint i; + + for (it = wins; it; it = g_list_next(it)) { + ObStackingLayer l; + + l = window_layer(it->data); + layer[l] = g_list_append(layer[l], it->data); + } + + it = stacking_list; + for (i = OB_NUM_STACKING_LAYERS - 1; i >= 0; --i) { + if (layer[i]) { + for (; it; it = g_list_next(it)) { + /* look for the top of the next layer down */ + if (window_layer(it->data) < (ObStackingLayer) i) + break; + } + do_restack(layer[i], it); + g_list_free(layer[i]); + } + } +} + +static void restack_windows(ObClient *selected, gboolean raise) +{ + GList *it, *last, *below, *above, *next; + GList *wins = NULL; + + GList *group_helpers = NULL; + GList *group_modals = NULL; + GList *group_trans = NULL; + GList *modals = NULL; + GList *trans = NULL; + + if (raise) { + ObClient *p; + + /* if a window is modal for another single window, then raise it to the + top too, the same is done with the focus order */ + while (selected->modal && (p = client_direct_parent(selected))) + selected = p; + } + + /* remove first so we can't run into ourself */ + it = g_list_find(stacking_list, selected); + g_assert(it); + stacking_list = g_list_delete_link(stacking_list, it); + + /* go from the bottom of the stacking list up. don't move any other windows + when lowering, we call this for each window independently */ + if (raise) { + for (it = g_list_last(stacking_list); it; it = next) { + next = g_list_previous(it); + + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *ch = it->data; + + /* only move windows in the same stacking layer */ + if (ch->layer == selected->layer && + /* looking for windows that are transients, and so would + remain above the selected window */ + client_search_transient(selected, ch)) + { + if (client_is_direct_child(selected, ch)) { + if (ch->modal) + modals = g_list_prepend(modals, ch); + else + trans = g_list_prepend(trans, ch); + } + else if (client_helper(ch)) { + if (selected->transient) { + /* helpers do not stay above transient windows */ + continue; + } + group_helpers = g_list_prepend(group_helpers, ch); + } + else { + if (ch->modal) + group_modals = g_list_prepend(group_modals, ch); + else + group_trans = g_list_prepend(group_trans, ch); + } + stacking_list = g_list_delete_link(stacking_list, it); + } + } + } + } + + /* put modals above other direct transients */ + wins = g_list_concat(modals, trans); + + /* put helpers below direct transients */ + wins = g_list_concat(wins, group_helpers); + + /* put the selected window right below these children */ + wins = g_list_append(wins, selected); + + /* if selected window is transient for group then raise it above others */ + if (selected->transient_for_group) { + /* if it's modal, raise it above those also */ + if (selected->modal) { + wins = g_list_concat(wins, group_modals); + group_modals = NULL; + } + wins = g_list_concat(wins, group_trans); + group_trans = NULL; + } + + /* find where to put the selected window, start from bottom of list, + this is the window below everything we are re-adding to the list */ + last = NULL; + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) + { + if (window_layer(it->data) < selected->layer) { + last = it; + continue; + } + /* if lowering, stop at the beginning of the layer */ + if (!raise) + break; + /* if raising, stop at the end of the layer */ + if (window_layer(it->data) > selected->layer) + break; + + last = it; + } + + /* save this position in the stacking list */ + below = last; + + /* find where to put the group transients, start from the top of list */ + for (it = stacking_list; it; it = g_list_next(it)) { + /* skip past higher layers */ + if (window_layer(it->data) > selected->layer) + continue; + /* if we reach the end of the layer (how?) then don't go further */ + if (window_layer(it->data) < selected->layer) + break; + /* stop when we reach the first window in the group */ + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (c->group == selected->group) + break; + } + /* if we don't hit any other group members, stop here because this + is where we are putting the selected window (and its children) */ + if (it == below) + break; + } + + /* save this position, this is the top of the group of windows between the + group transient ones we're restacking and the others up above that we're + restacking + + we actually want to save 1 position _above_ that, for for loops to work + nicely, so move back one position in the list while saving it + */ + above = it ? g_list_previous(it) : g_list_last(stacking_list); + + /* put the windows inside the gap to the other windows we're stacking + into the restacking list, go from the bottom up so that we can use + g_list_prepend */ + if (below) it = g_list_previous(below); + else it = g_list_last(stacking_list); + for (; it != above; it = next) { + next = g_list_previous(it); + wins = g_list_prepend(wins, it->data); + stacking_list = g_list_delete_link(stacking_list, it); + } + + /* group transients go above the rest of the stuff acquired to now */ + wins = g_list_concat(group_trans, wins); + /* group modals go on the very top */ + wins = g_list_concat(group_modals, wins); + + do_restack(wins, below); + g_list_free(wins); + + /* lower our parents after us, so they go below us */ + if (!raise && selected->parents) { + GSList *parents_copy, *sit; + GSList *reorder = NULL; + + parents_copy = g_slist_copy(selected->parents); + + /* go thru stacking list backwards so we can use g_slist_prepend */ + for (it = g_list_last(stacking_list); it && parents_copy; + it = g_list_previous(it)) + if ((sit = g_slist_find(parents_copy, it->data))) { + reorder = g_slist_prepend(reorder, sit->data); + parents_copy = g_slist_delete_link(parents_copy, sit); + } + g_assert(parents_copy == NULL); + + /* call restack for each of these to lower them */ + for (sit = reorder; sit; sit = g_slist_next(sit)) + restack_windows(sit->data, raise); + } +} + +void stacking_raise(ObWindow *window) +{ + if (WINDOW_IS_CLIENT(window)) { + ObClient *selected; + selected = WINDOW_AS_CLIENT(window); + restack_windows(selected, TRUE); + } else { + GList *wins; + wins = g_list_append(NULL, window); + stacking_list = g_list_remove(stacking_list, window); + do_raise(wins); + g_list_free(wins); + } + stacking_list_tail = g_list_last(stacking_list); +} + +void stacking_lower(ObWindow *window) +{ + if (WINDOW_IS_CLIENT(window)) { + ObClient *selected; + selected = WINDOW_AS_CLIENT(window); + restack_windows(selected, FALSE); + } else { + GList *wins; + wins = g_list_append(NULL, window); + stacking_list = g_list_remove(stacking_list, window); + do_lower(wins); + g_list_free(wins); + } + stacking_list_tail = g_list_last(stacking_list); +} + +void stacking_below(ObWindow *window, ObWindow *below) +{ + GList *wins, *before; + + if (window_layer(window) != window_layer(below)) + return; + + wins = g_list_append(NULL, window); + stacking_list = g_list_remove(stacking_list, window); + before = g_list_next(g_list_find(stacking_list, below)); + do_restack(wins, before); + g_list_free(wins); + stacking_list_tail = g_list_last(stacking_list); +} + +void stacking_add(ObWindow *win) +{ + g_assert(screen_support_win != None); /* make sure I dont break this in the + future */ + /* don't add windows that are being unmanaged ! */ + if (WINDOW_IS_CLIENT(win)) g_assert(WINDOW_AS_CLIENT(win)->managed); + + stacking_list = g_list_append(stacking_list, win); + + stacking_raise(win); + /* stacking_list_tail set by stacking_raise() */ +} + +static GList *find_highest_relative(ObClient *client) +{ + GList *ret = NULL; + + if (client->parents) { + GList *it; + GSList *top; + + /* get all top level relatives of this client */ + top = client_search_all_top_parents_layer(client); + + /* go from the top of the stacking order down */ + for (it = stacking_list; !ret && it; it = g_list_next(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + /* only look at windows in the same layer and that are + visible */ + if (c->layer == client->layer && + !c->iconic && + (c->desktop == client->desktop || + c->desktop == DESKTOP_ALL || + client->desktop == DESKTOP_ALL)) + { + GSList *sit; + + /* go through each top level parent and see it this window + is related to them */ + for (sit = top; !ret && sit; sit = g_slist_next(sit)) { + ObClient *topc = sit->data; + + /* are they related ? */ + if (topc == c || client_search_transient(topc, c)) + ret = it; + } + } + } + } + } + return ret; +} + +void stacking_add_nonintrusive(ObWindow *win) +{ + ObClient *client; + GList *it_below = NULL; /* this client will be below us */ + GList *it_above; + GList *wins; + + if (!WINDOW_IS_CLIENT(win)) { + stacking_add(win); /* no special rules for others */ + return; + } + + client = WINDOW_AS_CLIENT(win); + + /* don't add windows that are being unmanaged ! */ + g_assert(client->managed); + + /* insert above its highest parent (or its highest child !) */ + it_below = find_highest_relative(client); + + if (!it_below) { + /* nothing to put it directly above, so try find the focused client + to put it underneath it */ + if (focus_client && client != focus_client && + focus_client->layer == client->layer) + { + it_below = g_list_find(stacking_list, focus_client); + /* this can give NULL, but it means the focused window is on the + bottom of the stacking order, so go to the bottom in that case, + below it */ + it_below = g_list_next(it_below); + } + else { + /* There is no window to put this directly above, so put it at the + top, so you know it is there. + + It used to do this only if the window was focused and lower + it otherwise. + + We also put it at the top not the bottom to fix a bug with + fullscreen windows. When focusLast is off and followsMouse is + on, when you switch desktops, the fullscreen window loses + focus and goes into its lower layer. If this puts it at the + bottom then when you come back to the desktop, the window is + at the bottom and won't get focus back. + */ + it_below = stacking_list; + } + } + + /* make sure it's not in the wrong layer though ! */ + for (; it_below; it_below = g_list_next(it_below)) { + /* stop when the window is not in a higher layer than the window + it is going above (it_below) */ + if (client->layer >= window_layer(it_below->data)) + break; + } + for (; it_below != stacking_list; it_below = it_above) { + /* stop when the window is not in a lower layer than the + window it is going under (it_above) */ + it_above = it_below ? + g_list_previous(it_below) : g_list_last(stacking_list); + if (client->layer <= window_layer(it_above->data)) + break; + } + + wins = g_list_append(NULL, win); + do_restack(wins, it_below); + g_list_free(wins); + stacking_list_tail = g_list_last(stacking_list); +} + +/*! Returns TRUE if client is occluded by the sibling. If sibling is NULL it + tries against all other clients. +*/ +static gboolean stacking_occluded(ObClient *client, ObClient *sibling) +{ + GList *it; + gboolean occluded = FALSE; + + /* no need for any looping in this case */ + if (sibling && client->layer != sibling->layer) + return occluded; + + for (it = g_list_previous(g_list_find(stacking_list, client)); it; + it = g_list_previous(it)) + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (!c->iconic && + (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL || + c->desktop == client->desktop) && + !client_search_transient(client, c)) + { + if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area)) + { + if (sibling != NULL) { + if (c == sibling) { + occluded = TRUE; + break; + } + } + else if (c->layer == client->layer) { + occluded = TRUE; + break; + } + else if (c->layer > client->layer) + break; /* we past its layer */ + } + } + } + return occluded; +} + +/*! Returns TRUE if client occludes the sibling. If sibling is NULL it tries + against all other clients. +*/ +static gboolean stacking_occludes(ObClient *client, ObClient *sibling) +{ + GList *it; + gboolean occludes = FALSE; + + /* no need for any looping in this case */ + if (sibling && client->layer != sibling->layer) + return occludes; + + for (it = g_list_next(g_list_find(stacking_list, client)); + it; it = g_list_next(it)) + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (!c->iconic && + (c->desktop == DESKTOP_ALL || client->desktop == DESKTOP_ALL || + c->desktop == client->desktop) && + !client_search_transient(c, client)) + { + if (RECT_INTERSECTS_RECT(c->frame->area, client->frame->area)) + { + if (sibling != NULL) { + if (c == sibling) { + occludes = TRUE; + break; + } + } + else if (c->layer == client->layer) { + occludes = TRUE; + break; + } + else if (c->layer < client->layer) + break; /* we past its layer */ + } + } + } + return occludes; +} + +gboolean stacking_restack_request(ObClient *client, ObClient *sibling, + gint detail) +{ + gboolean ret = FALSE; + + if (sibling && ((client->desktop != sibling->desktop && + client->desktop != DESKTOP_ALL && + sibling->desktop != DESKTOP_ALL) || + sibling->iconic)) + { + ob_debug("Setting restack sibling to NULL, they are not on the same " + "desktop or it is iconified"); + sibling = NULL; + } + + switch (detail) { + case Below: + ob_debug("Restack request Below for client %s sibling %s", + client->title, sibling ? sibling->title : "(all)"); + /* just lower it */ + stacking_lower(CLIENT_AS_WINDOW(client)); + ret = TRUE; + break; + case BottomIf: + ob_debug("Restack request BottomIf for client %s sibling %s", + client->title, sibling ? sibling->title : "(all)"); + /* if this client occludes sibling (or anything if NULL), then + lower it to the bottom */ + if (stacking_occludes(client, sibling)) { + stacking_lower(CLIENT_AS_WINDOW(client)); + ret = TRUE; + } + break; + case Above: + ob_debug("Restack request Above for client %s sibling %s", + client->title, sibling ? sibling->title : "(all)"); + stacking_raise(CLIENT_AS_WINDOW(client)); + ret = TRUE; + break; + case TopIf: + ob_debug("Restack request TopIf for client %s sibling %s", + client->title, sibling ? sibling->title : "(all)"); + if (stacking_occluded(client, sibling)) { + stacking_raise(CLIENT_AS_WINDOW(client)); + ret = TRUE; + } + break; + case Opposite: + ob_debug("Restack request Opposite for client %s sibling %s", + client->title, sibling ? sibling->title : "(all)"); + if (stacking_occluded(client, sibling)) { + stacking_raise(CLIENT_AS_WINDOW(client)); + ret = TRUE; + } + else if (stacking_occludes(client, sibling)) { + stacking_lower(CLIENT_AS_WINDOW(client)); + ret = TRUE; + } + break; + } + return ret; +} diff --git a/openbox/stacking.h b/openbox/stacking.h new file mode 100644 index 0000000..c14aa2e --- /dev/null +++ b/openbox/stacking.h @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + stacking.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __stacking_h +#define __stacking_h + +#include <glib.h> +#include <X11/Xlib.h> + +struct _ObWindow; +struct _ObClient; + +/*! The possible stacking layers a client window can be a part of */ +typedef enum { + OB_STACKING_LAYER_INVALID, + OB_STACKING_LAYER_DESKTOP, /*!< 0 - desktop windows */ + OB_STACKING_LAYER_BELOW, /*!< 1 - normal windows w/ below */ + OB_STACKING_LAYER_NORMAL, /*!< 2 - normal windows */ + OB_STACKING_LAYER_ABOVE, /*!< 3 - normal windows w/ above */ + OB_STACKING_LAYER_FULLSCREEN, /*!< 4 - fullscreeen windows */ + OB_STACKING_LAYER_INTERNAL, /*!< 5 - openbox windows/menus */ + OB_NUM_STACKING_LAYERS +} ObStackingLayer; + +/* list of ObWindow*s in stacking order from highest to lowest */ +extern GList *stacking_list; +/* list of ObWindow*s in stacking order from lowest to highest */ +extern GList *stacking_list_tail; + +/*! Sets the window stacking list on the root window from the + stacking_list */ +void stacking_set_list(void); + +void stacking_add(struct _ObWindow *win); +void stacking_add_nonintrusive(struct _ObWindow *win); +#define stacking_remove(win) stacking_list = g_list_remove(stacking_list, win); + +/*! Raises a window above all others in its stacking layer */ +void stacking_raise(struct _ObWindow *window); + +/*! Temporarily raises a window above all others */ +void stacking_temp_raise(struct _ObWindow *window); + +/*! Restores any temporarily raised windows to their correct place */ +void stacking_restore(void); + +/*! Lowers a window below all others in its stacking layer */ +void stacking_lower(struct _ObWindow *window); + +/*! Moves a window below another if its in the same layer. + This function does not enforce stacking rules IRT transients n such, and so + it should really ONLY be used to restore stacking orders from saved sessions +*/ +void stacking_below(struct _ObWindow *window, struct _ObWindow *below); + +/*! Restack a window based upon a sibling (or all windows) in various ways. + @param client The client to be restacked + @param sibling Another client to compare to, or NULL to compare to all + windows + @param detail One of Above, Below, TopIf, BottomIf, Opposite + @return TRUE if the client was restacked + See http://tronche.com/gui/x/xlib/window/configure.html for details on + how each detail works with and without a sibling. +*/ +gboolean stacking_restack_request(struct _ObClient *client, + struct _ObClient *sibling, + gint detail); + +#endif diff --git a/openbox/startupnotify.c b/openbox/startupnotify.c new file mode 100644 index 0000000..e249002 --- /dev/null +++ b/openbox/startupnotify.c @@ -0,0 +1,276 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + startupnotify.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "startupnotify.h" +#include "gettext.h" +#include "event.h" +#include "obt/xqueue.h" + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#ifndef USE_LIBSN + +void sn_startup(gboolean reconfig) {} +void sn_shutdown(gboolean reconfig) {} +gboolean sn_app_starting() { return FALSE; } +Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name) +{ + return CurrentTime; +} +gboolean sn_get_desktop(gchar *id, guint *desktop) { return FALSE; } +void sn_setup_spawn_environment(const gchar *program, const gchar *name, + const gchar *icon_name, const gchar *wmclass, + gint desktop) {} +void sn_spawn_cancel() {} + +#else + +#include "openbox.h" +#include "screen.h" + +#define SN_API_NOT_YET_FROZEN +#include <libsn/sn.h> + +static SnDisplay *sn_display; +static SnMonitorContext *sn_context; +static SnLauncherContext *sn_launcher; +static GSList *sn_waits; /* list of SnStartupSequences we're waiting on */ + +static SnStartupSequence* sequence_find(const gchar *id); + +static void sn_handler(const XEvent *e, gpointer data); +static void sn_event_func(SnMonitorEvent *event, gpointer data); + +void sn_startup(gboolean reconfig) +{ + if (reconfig) return; + + sn_display = sn_display_new(obt_display, NULL, NULL); + sn_context = sn_monitor_context_new(sn_display, ob_screen, + sn_event_func, NULL, NULL); + sn_launcher = sn_launcher_context_new(sn_display, ob_screen); + + xqueue_add_callback(sn_handler, NULL); +} + +void sn_shutdown(gboolean reconfig) +{ + GSList *it; + + if (reconfig) return; + + xqueue_remove_callback(sn_handler, NULL); + + for (it = sn_waits; it; it = g_slist_next(it)) + sn_startup_sequence_unref((SnStartupSequence*)it->data); + g_slist_free(sn_waits); + sn_waits = NULL; + + screen_set_root_cursor(); + + sn_launcher_context_unref(sn_launcher); + sn_monitor_context_unref(sn_context); + sn_display_unref(sn_display); +} + +static SnStartupSequence* sequence_find(const gchar *id) +{ + SnStartupSequence*ret = NULL; + GSList *it; + + for (it = sn_waits; it; it = g_slist_next(it)) { + SnStartupSequence *seq = it->data; + if (!strcmp(id, sn_startup_sequence_get_id(seq))) { + ret = seq; + break; + } + } + return ret; +} + +gboolean sn_app_starting(void) +{ + return sn_waits != NULL; +} + +static gboolean sn_wait_timeout(gpointer data) +{ + SnStartupSequence *seq = data; + sn_waits = g_slist_remove(sn_waits, seq); + screen_set_root_cursor(); + return FALSE; /* don't repeat */ +} + +static void sn_handler(const XEvent *e, gpointer data) +{ + XEvent ec; + ec = *e; + sn_display_process_event(sn_display, &ec); +} + +static void sn_event_func(SnMonitorEvent *ev, gpointer data) +{ + SnStartupSequence *seq; + gboolean change = FALSE; + + if (!(seq = sn_monitor_event_get_startup_sequence(ev))) + return; + + switch (sn_monitor_event_get_type(ev)) { + case SN_MONITOR_EVENT_INITIATED: + sn_startup_sequence_ref(seq); + sn_waits = g_slist_prepend(sn_waits, seq); + /* 20 second timeout for apps to start if the launcher doesn't + have a timeout */ + g_timeout_add_full(G_PRIORITY_DEFAULT, + 20 * 1000, sn_wait_timeout, seq, + (GDestroyNotify)sn_startup_sequence_unref); + change = TRUE; + break; + case SN_MONITOR_EVENT_CHANGED: + /* XXX feedback changed? */ + change = TRUE; + break; + case SN_MONITOR_EVENT_COMPLETED: + case SN_MONITOR_EVENT_CANCELED: + if ((seq = sequence_find(sn_startup_sequence_get_id(seq)))) { + sn_waits = g_slist_remove(sn_waits, seq); + g_source_remove_by_user_data(seq); + change = TRUE; + } + break; + }; + + if (change) + screen_set_root_cursor(); +} + +Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name) +{ + GSList *it; + Time t = CurrentTime; + + if (!id && !wmclass) + return t; + + for (it = sn_waits; it; it = g_slist_next(it)) { + SnStartupSequence *seq = it->data; + gboolean found = FALSE; + const gchar *seqid, *seqclass, *seqbin; + seqid = sn_startup_sequence_get_id(seq); + seqclass = sn_startup_sequence_get_wmclass(seq); + seqbin = sn_startup_sequence_get_binary_name(seq); + + if (id && seqid) { + /* if the app has a startup id, then look for that for highest + accuracy */ + if (!strcmp(seqid, id)) + found = TRUE; + } + else if (seqclass) { + /* seqclass = "a string to match against the "resource name" or + "resource class" hints. These are WM_CLASS[0] and WM_CLASS[1]" + - from the startup-notification spec + */ + found = (seqclass && !strcmp(seqclass, wmclass)) || + (seqclass && !strcmp(seqclass, name)); + } + else if (seqbin) { + /* Check the binary name against the class and name hints + as well, to help apps that don't have the class set + correctly */ + found = (seqbin && !g_ascii_strcasecmp(seqbin, wmclass)) || + (seqbin && !g_ascii_strcasecmp(seqbin, name)); + } + + if (found) { + sn_startup_sequence_complete(seq); + t = sn_startup_sequence_get_timestamp(seq); + break; + } + } + return t; +} + +gboolean sn_get_desktop(gchar *id, guint *desktop) +{ + SnStartupSequence *seq; + + if (id && (seq = sequence_find(id))) { + gint desk = sn_startup_sequence_get_workspace(seq); + if (desk != -1) { + *desktop = desk; + return TRUE; + } + } + return FALSE; +} + +static gboolean sn_launch_wait_timeout(gpointer data) +{ + SnLauncherContext *sn = data; + sn_launcher_context_complete(sn); + return FALSE; /* don't repeat */ +} + +void sn_setup_spawn_environment(const gchar *program, const gchar *name, + const gchar *icon_name, const gchar *wmclass, + gint desktop) +{ + gchar *desc; + const char *id; + + desc = g_strdup_printf(_("Running %s"), program); + + if (sn_launcher_context_get_initiated(sn_launcher)) { + sn_launcher_context_unref(sn_launcher); + sn_launcher = sn_launcher_context_new(sn_display, ob_screen); + } + + sn_launcher_context_set_name(sn_launcher, name ? name : program); + sn_launcher_context_set_description(sn_launcher, desc); + sn_launcher_context_set_icon_name(sn_launcher, icon_name ? + icon_name : program); + sn_launcher_context_set_binary_name(sn_launcher, program); + if (wmclass) sn_launcher_context_set_wmclass(sn_launcher, wmclass); + if (desktop >= 0 && (unsigned) desktop < screen_num_desktops) + sn_launcher_context_set_workspace(sn_launcher, (signed) desktop); + sn_launcher_context_initiate(sn_launcher, "openbox", program, + event_time()); + id = sn_launcher_context_get_startup_id(sn_launcher); + + /* 20 second timeout for apps to start */ + sn_launcher_context_ref(sn_launcher); + g_timeout_add_full(G_PRIORITY_DEFAULT, + 20 * 1000, sn_launch_wait_timeout, sn_launcher, + (GDestroyNotify)sn_launcher_context_unref); + + g_setenv("DESKTOP_STARTUP_ID", id, TRUE); + + g_free(desc); +} + +void sn_spawn_cancel(void) +{ + sn_launcher_context_complete(sn_launcher); +} + +#endif diff --git a/openbox/startupnotify.h b/openbox/startupnotify.h new file mode 100644 index 0000000..d524bca --- /dev/null +++ b/openbox/startupnotify.h @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + startupnotify.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__startupnotify_h +#define ob__startupnotify_h + +#include <glib.h> +#include <X11/Xlib.h> + +void sn_startup(gboolean reconfig); +void sn_shutdown(gboolean reconfig); + +gboolean sn_app_starting(void); + +/*! Notify that an app has started + @param wmclass the WM_CLASS[1] hint + @param name the WM_CLASS[0] hint + */ +Time sn_app_started(const gchar *id, const gchar *wmclass, const gchar *name); + +/*! Get the desktop requested via the startup-notiication protocol if one + was requested */ +gboolean sn_get_desktop(gchar *id, guint *desktop); + +/* Get the environment to run the program in, with startup notification */ +void sn_setup_spawn_environment(const gchar *program, const gchar *name, + const gchar *icon_name, const gchar *wmclass, + gint desktop); + +/* Tell startup notification we're not actually running the program we + told it we were +*/ +void sn_spawn_cancel(void); + +#endif diff --git a/openbox/translate.c b/openbox/translate.c new file mode 100644 index 0000000..50d5000 --- /dev/null +++ b/openbox/translate.c @@ -0,0 +1,160 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + translate.c for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "openbox.h" +#include "mouse.h" +#include "gettext.h" +#include "obt/keyboard.h" + +#include <glib.h> +#include <string.h> +#include <stdlib.h> + +static guint translate_modifier(gchar *str) +{ + guint mask = 0; + + if (!g_ascii_strcasecmp("Mod1", str)) mask = Mod1Mask; + else if (!g_ascii_strcasecmp("Mod2", str)) mask = Mod2Mask; + else if (!g_ascii_strcasecmp("Mod3", str)) mask = Mod3Mask; + else if (!g_ascii_strcasecmp("Mod4", str)) mask = Mod4Mask; + else if (!g_ascii_strcasecmp("Mod5", str)) mask = Mod5Mask; + + else if (!g_ascii_strcasecmp("Control", str) || + !g_ascii_strcasecmp("C", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_CONTROL); + else if (!g_ascii_strcasecmp("Alt", str) || + !g_ascii_strcasecmp("A", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_ALT); + else if (!g_ascii_strcasecmp("Meta", str) || + !g_ascii_strcasecmp("M", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_META); + /* W = windows key, is linked to the Super_L/R buttons */ + else if (!g_ascii_strcasecmp("Super", str) || + !g_ascii_strcasecmp("W", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SUPER); + else if (!g_ascii_strcasecmp("Shift", str) || + !g_ascii_strcasecmp("S", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_SHIFT); + else if (!g_ascii_strcasecmp("Hyper", str) || + !g_ascii_strcasecmp("H", str)) + mask = obt_keyboard_modkey_to_modmask(OBT_KEYBOARD_MODKEY_HYPER); + else + g_message(_("Invalid modifier key \"%s\" in key/mouse binding"), str); + + return mask; +} + +gboolean translate_button(const gchar *str, guint *state, guint *button) +{ + gchar **parsed; + gchar *l; + gint i; + gboolean ret = FALSE; + + parsed = g_strsplit(str, "-", -1); + + /* first, find the button (last token) */ + l = NULL; + for (i = 0; parsed[i] != NULL; ++i) + l = parsed[i]; + if (l == NULL) + goto translation_fail; + + /* figure out the mod mask */ + *state = 0; + for (i = 0; parsed[i] != l; ++i) { + guint m = translate_modifier(parsed[i]); + if (!m) goto translation_fail; + *state |= m; + } + + /* figure out the button */ + if (!g_ascii_strcasecmp("Left", l)) *button = 1; + else if (!g_ascii_strcasecmp("Middle", l)) *button = 2; + else if (!g_ascii_strcasecmp("Right", l)) *button = 3; + else if (!g_ascii_strcasecmp("Up", l)) *button = 4; + else if (!g_ascii_strcasecmp("Down", l)) *button = 5; + else if (!g_ascii_strncasecmp("Button", l, 6)) *button = atoi(l+6); + if (!*button) + goto translation_fail; + + ret = TRUE; + +translation_fail: + g_strfreev(parsed); + return ret; +} + +gboolean translate_key(const gchar *str, guint *state, guint *keycode) +{ + gchar **parsed; + gchar *l; + gint i; + gboolean ret = FALSE; + KeySym sym; + + parsed = g_strsplit(str, "-", -1); + + *state = *keycode = 0; + + /* first, find the key (last token) */ + l = NULL; + for (i = 0; parsed[i] != NULL; ++i) + l = parsed[i]; + if (l == NULL) + goto translation_fail; + + /* figure out the mod mask */ + *state = 0; + for (i = 0; parsed[i] != l; ++i) { + guint m = translate_modifier(parsed[i]); + if (!m) goto translation_fail; + *state |= m; + } + + if (!g_ascii_strncasecmp("0x", l, 2)) { + gchar *end; + + /* take it directly */ + *keycode = strtol(l, &end, 16); + if (*l == '\0' || *end != '\0') { + g_message(_("Invalid key code \"%s\" in key binding"), l); + goto translation_fail; + } + } else { + /* figure out the keycode */ + sym = XStringToKeysym(l); + if (sym == NoSymbol) { + g_message(_("Invalid key name \"%s\" in key binding"), l); + goto translation_fail; + } + *keycode = XKeysymToKeycode(obt_display, sym); + } + if (!*keycode) { + g_message(_("Requested key \"%s\" does not exist on the display"), l); + goto translation_fail; + } + + ret = TRUE; + +translation_fail: + g_strfreev(parsed); + return ret; +} diff --git a/openbox/translate.h b/openbox/translate.h new file mode 100644 index 0000000..bffdc9b --- /dev/null +++ b/openbox/translate.h @@ -0,0 +1,29 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + translate.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef ob__translate_h +#define ob__translate_h + +#include <glib.h> + +gboolean translate_button(const gchar *str, guint *state, guint *keycode); +gboolean translate_key(const gchar *str, guint *state, guint *keycode); + +void RrImageSetDestroyFunc(RrImage *image, RrImageDestroyFunc func); + +#endif diff --git a/openbox/window.c b/openbox/window.c new file mode 100644 index 0000000..51806f9 --- /dev/null +++ b/openbox/window.c @@ -0,0 +1,229 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + window.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "window.h" +#include "menuframe.h" +#include "config.h" +#include "dock.h" +#include "client.h" +#include "frame.h" +#include "openbox.h" +#include "prompt.h" +#include "debug.h" +#include "grab.h" +#include "obt/prop.h" +#include "obt/xqueue.h" + +static GHashTable *window_map; + +static guint window_hash(Window *w) { return *w; } +static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; } + +void window_startup(gboolean reconfig) +{ + if (reconfig) return; + + window_map = g_hash_table_new((GHashFunc)window_hash, + (GEqualFunc)window_comp); +} + +void window_shutdown(gboolean reconfig) +{ + if (reconfig) return; + + g_hash_table_destroy(window_map); +} + +Window window_top(ObWindow *self) +{ + switch (self->type) { + case OB_WINDOW_CLASS_MENUFRAME: + return WINDOW_AS_MENUFRAME(self)->window; + case OB_WINDOW_CLASS_DOCK: + return WINDOW_AS_DOCK(self)->frame; + case OB_WINDOW_CLASS_CLIENT: + return WINDOW_AS_CLIENT(self)->frame->window; + case OB_WINDOW_CLASS_INTERNAL: + return WINDOW_AS_INTERNAL(self)->window; + case OB_WINDOW_CLASS_PROMPT: + return WINDOW_AS_PROMPT(self)->super.window; + } + g_assert_not_reached(); + return None; +} + +ObStackingLayer window_layer(ObWindow *self) +{ + switch (self->type) { + case OB_WINDOW_CLASS_DOCK: + return config_dock_layer; + case OB_WINDOW_CLASS_CLIENT: + return ((ObClient*)self)->layer; + case OB_WINDOW_CLASS_MENUFRAME: + case OB_WINDOW_CLASS_INTERNAL: + return OB_STACKING_LAYER_INTERNAL; + case OB_WINDOW_CLASS_PROMPT: + /* not used directly for stacking, prompts are managed as clients */ + g_assert_not_reached(); + break; + } + g_assert_not_reached(); + return None; +} + +ObWindow* window_find(Window xwin) +{ + return g_hash_table_lookup(window_map, &xwin); +} + +void window_add(Window *xwin, ObWindow *win) +{ + g_assert(xwin != NULL); + g_assert(win != NULL); + g_hash_table_insert(window_map, xwin, win); +} + +void window_remove(Window xwin) +{ + g_assert(xwin != None); + g_hash_table_remove(window_map, &xwin); +} + +void window_manage_all(void) +{ + guint i, j, nchild; + Window w, *children; + XWMHints *wmhints; + XWindowAttributes attrib; + + if (!XQueryTree(obt_display, RootWindow(obt_display, ob_screen), + &w, &w, &children, &nchild)) { + ob_debug("XQueryTree failed in window_manage_all"); + nchild = 0; + } + + /* remove all icon windows from the list */ + for (i = 0; i < nchild; i++) { + if (children[i] == None) continue; + wmhints = XGetWMHints(obt_display, children[i]); + if (wmhints) { + if ((wmhints->flags & IconWindowHint) && + (wmhints->icon_window != children[i])) + for (j = 0; j < nchild; j++) + if (children[j] == wmhints->icon_window) { + /* XXX watch the window though */ + children[j] = None; + break; + } + XFree(wmhints); + } + } + + for (i = 0; i < nchild; ++i) { + if (children[i] == None) continue; + if (window_find(children[i])) continue; /* skip our own windows */ + if (XGetWindowAttributes(obt_display, children[i], &attrib)) { + if (attrib.map_state == IsUnmapped) + ; + else + window_manage(children[i]); + } + } + + if (children) XFree(children); +} + +static gboolean check_unmap(XEvent *e, gpointer data) +{ + const Window win = *(Window*)data; + return ((e->type == DestroyNotify && e->xdestroywindow.window == win) || + (e->type == UnmapNotify && e->xunmap.window == win)); +} + +void window_manage(Window win) +{ + XWindowAttributes attrib; + gboolean no_manage = FALSE; + gboolean is_dockapp = FALSE; + Window icon_win = None; + + grab_server(TRUE); + + /* check if it has already been unmapped by the time we started + mapping. the grab does a sync so we don't have to here */ + if (xqueue_exists_local(check_unmap, &win)) { + ob_debug("Trying to manage unmapped window. Aborting that."); + no_manage = TRUE; + } + else if (!XGetWindowAttributes(obt_display, win, &attrib)) + no_manage = TRUE; + else { + XWMHints *wmhints; + + /* is the window a docking app */ + is_dockapp = FALSE; + if ((wmhints = XGetWMHints(obt_display, win))) { + if ((wmhints->flags & StateHint) && + wmhints->initial_state == WithdrawnState) + { + if (wmhints->flags & IconWindowHint) + icon_win = wmhints->icon_window; + is_dockapp = TRUE; + } + XFree(wmhints); + } + /* This is a new method to declare that a window is a dockapp, being + implemented by Windowmaker, to alleviate pain in writing GTK+ + dock apps. + http://thread.gmane.org/gmane.comp.window-managers.openbox/4881 + */ + if (!is_dockapp) { + gchar **ss; + if (OBT_PROP_GETSS_TYPE(win, WM_CLASS, STRING_NO_CC, &ss)) + { + if (ss[0] && ss[1] && strcmp(ss[1], "DockApp") == 0) + is_dockapp = TRUE; + g_strfreev(ss); + } + } + } + + if (!no_manage) { + if (attrib.override_redirect) { + ob_debug("not managing override redirect window 0x%x", win); + grab_server(FALSE); + } + else if (is_dockapp) { + if (!icon_win) + icon_win = win; + dock_manage(icon_win, win); + } + else + client_manage(win, NULL); + } + else { + grab_server(FALSE); + ob_debug("FAILED to manage window 0x%x", win); + } +} + +void window_unmanage_all(void) +{ + dock_unmanage_all(); + client_unmanage_all(); +} diff --git a/openbox/window.h b/openbox/window.h new file mode 100644 index 0000000..24a7d07 --- /dev/null +++ b/openbox/window.h @@ -0,0 +1,93 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + window.h for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __window_h +#define __window_h + +#include "stacking.h" + +#include <X11/Xlib.h> +#include <glib.h> + +typedef struct _ObWindow ObWindow; +typedef struct _ObInternalWindow ObInternalWindow; + +typedef enum { + OB_WINDOW_CLASS_MENUFRAME, + OB_WINDOW_CLASS_DOCK, + OB_WINDOW_CLASS_CLIENT, + OB_WINDOW_CLASS_INTERNAL, + OB_WINDOW_CLASS_PROMPT +} ObWindowClass; + +/* In order to be an ObWindow, you need to make this struct the top of your + struct */ +struct _ObWindow { + ObWindowClass type; +}; + +#define WINDOW_IS_MENUFRAME(win) \ + (((ObWindow*)win)->type == OB_WINDOW_CLASS_MENUFRAME) +#define WINDOW_IS_DOCK(win) \ + (((ObWindow*)win)->type == OB_WINDOW_CLASS_DOCK) +#define WINDOW_IS_CLIENT(win) \ + (((ObWindow*)win)->type == OB_WINDOW_CLASS_CLIENT) +#define WINDOW_IS_INTERNAL(win) \ + (((ObWindow*)win)->type == OB_WINDOW_CLASS_INTERNAL) +#define WINDOW_IS_PROMPT(win) \ + (((ObWindow*)win)->type == OB_WINDOW_CLASS_PROMPT) + +struct _ObMenu; +struct _ObDock; +struct _ObDockApp; +struct _ObClient; +struct _ObPrompt; + +#define WINDOW_AS_MENUFRAME(win) ((struct _ObMenuFrame*)win) +#define WINDOW_AS_DOCK(win) ((struct _ObDock*)win) +#define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win) +#define WINDOW_AS_INTERNAL(win) ((struct _ObInternalWindow*)win) +#define WINDOW_AS_PROMPT(win) ((struct _ObPrompt*)win) + +#define MENUFRAME_AS_WINDOW(menu) ((ObWindow*)menu) +#define DOCK_AS_WINDOW(dock) ((ObWindow*)dock) +#define CLIENT_AS_WINDOW(client) ((ObWindow*)client) +#define INTERNAL_AS_WINDOW(intern) ((ObWindow*)intern) +#define PROMPT_AS_WINDOW(prompt) ((ObWindow*)prompt) + +void window_startup (gboolean reconfig); +void window_shutdown(gboolean reconfig); + +Window window_top (ObWindow *self); +ObStackingLayer window_layer(ObWindow *self); + +ObWindow* window_find (Window xwin); +void window_add (Window *xwin, ObWindow *win); +void window_remove(Window xwin); + +/* Internal openbox-owned windows like the alt-tab popup */ +struct _ObInternalWindow { + ObWindowClass type; + Window window; +}; + +void window_manage_all(void); +void window_manage(Window win); +void window_unmanage_all(void); + +#endif diff --git a/packaging/openbox.changes b/packaging/openbox.changes new file mode 100644 index 0000000..8bcfb86 --- /dev/null +++ b/packaging/openbox.changes @@ -0,0 +1,7 @@ +* Tue Sep 04 2012 Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com> release-3.5.0@171e290 +- Added session management dependency (was missing) +- Added post and uninstall scripts to edit uxlaunch + +* Sat Sep 01 2012 Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com> release-3.5.0@a903d1c +- Initial submission to Tizen git/gerrit/OBS public infrastructure + diff --git a/packaging/openbox.spec b/packaging/openbox.spec new file mode 100644 index 0000000..f1b6d20 --- /dev/null +++ b/packaging/openbox.spec @@ -0,0 +1,145 @@ +Name: openbox +Summary: A highly configurable and standards-compliant X11 window manager +Version: 3.5.0 +Release: 1 +Group: System/X11 +License: GPLv2+ +URL: http://openbox.org +Source0: %{name}-%{version}.tar.bz2 +Requires: %{name}-libs = %{version}-%{release} +BuildRequires: pkgconfig(libxml-2.0) +BuildRequires: pkgconfig(xcursor) +BuildRequires: pkgconfig(xrandr) +BuildRequires: pkgconfig(xinerama) +BuildRequires: pkgconfig(xext) +BuildRequires: pkgconfig(xau) +BuildRequires: pkgconfig(pango) +BuildRequires: pkgconfig(sm) +BuildRequires: gettext-tools +BuildRequires: eglibc-devel +BuildRequires: desktop-file-utils + + +%description +Openbox is a window manager designed explicity for standards-compliance and +speed. It is fast, lightweight, and heavily configurable (using XML for its +configuration data). It has many features that make it unique among window +managers: window resistance, chainable key bindings, customizable mouse +actions, multi-head/Xinerama support, and dynamically generated "pipe menus." + +For a full list of the FreeDesktop.org standards with which it is compliant, +please see the COMPLIANCE file in the included documentation of this package. +For a graphical configuration editor, you'll need to install the obconf +package. For a graphical menu editor, you'll need to install the obmenu +package. + + + +%package libs +Summary: Shared libraries for %{name} +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires(post): /sbin/ldconfig +Requires(postun): /sbin/ldconfig + +%description libs +The %{name}-libs package contains shared libraries used by %{name}. + +%package devel +Summary: Development files for %{name} +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} +Requires: pkgconfig +Requires: pango-devel +Requires: libxml2-devel +Requires: glib2-devel + +%description devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}. + + + +%prep +%setup -q -n %{name}-%{version} + +%build +/bin/sh bootstrap +%configure --disable-static +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +# >> install post +rm -rf %{buildroot}%{_datadir}/doc/%{name} +#remove autostart stuff because uxlaunch allready starts stuff +rm %{buildroot}/%{_sysconfdir}/xdg/%{name}/autostart +# Remove the gnome-session stuff +rm -rf %{buildroot}%{_datadir}/gnome-session +# << install post +desktop-file-install --delete-original \ + --dir %{buildroot}%{_datadir}/applications \ + %{buildroot}%{_datadir}/applications/*.desktop +%find_lang %{name} + +%post libs +/sbin/ldconfig +# Set up uxlaunch to start openbox automatically +if [ -e /etc/sysconfig/uxlaunch ]; then + #Comment out current session + sed -i 's/session/#session/' /etc/sysconfig/uxlaunch + # Add new session line + echo 'session=/usr/bin/openbox-session' >> /etc/sysconfig/uxlaunch +fi + +%postun libs +/sbin/ldconfig +if [ -e /etc/sysconfig/uxlaunch ]; then + #Delete the openbox-session line + sed -i '/openbox-session/d' /etc/sysconfig/uxlaunch + # Restore previous session to whatever it was + sed -i 's/#session/session/' /etc/sysconfig/uxlaunch +fi + +%files -f %{name}.lang +%defattr(-,root,root,-) +%doc AUTHORS CHANGELOG COMPLIANCE COPYING README +%doc data/*.xsd data/menu.xml doc/rc-mouse-focus.xml +%dir %{_sysconfdir}/xdg/%{name}/ +%config(noreplace) %{_sysconfdir}/xdg/%{name}/* +%{_bindir}/gnome-panel-control +%{_bindir}/gdm-control +%{_bindir}/obxprop +%{_bindir}/openbox* +%{_libexecdir}/openbox-autostart +%{_libexecdir}/openbox-xdg-autostart +%{_datadir}/applications/%{name}.desktop +%{_datadir}/gnome/wm-properties/%{name}.desktop +%{_datadir}/themes/* +%{_datadir}/pixmaps/%{name}.png +%{_datadir}/xsessions/%{name}*.desktop +%{_mandir}/man1/%{name}*.1* +%{_mandir}/man1/obxprop.1* +# >> files +# << files + + +%files libs +%defattr(-,root,root,-) +%{_libdir}/libobrender.so.* +%{_libdir}/libobt.so.* +# >> files libs +# << files libs + +%files devel +%defattr(-,root,root,-) +%{_includedir}/%{name} +%{_libdir}/libobrender.so +%{_libdir}/libobt.so +%{_libdir}/pkgconfig/*.pc +# >> files devel +# << files devel + diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..0db7a85 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,32 @@ +sr sr@latin +zh_TW +zh_CN +de +es +eu +ca +sv +sk +no +fr +ru +pl +pt +pt_BR +fi +en@quot en@boldquot +et +cs +nl +ar +bn_IN +it +vi +ja +uk +hu +lt +lv +tr +da +hr diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 0000000..05231b8 --- /dev/null +++ b/po/Makevars @@ -0,0 +1,41 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Dana Jansens + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = http://bugzilla.icculus.org + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..4509714 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,18 @@ +# List of source files containing translatable strings. +openbox/actions.c +openbox/actions/execute.c +openbox/actions/exit.c +openbox/client.c +openbox/client_list_combined_menu.c +openbox/client_list_menu.c +openbox/client_menu.c +openbox/config.c +openbox/debug.c +openbox/keyboard.c +openbox/menu.c +openbox/mouse.c +openbox/openbox.c +openbox/screen.c +openbox/startupnotify.c +openbox/translate.c +openbox/prompt.c diff --git a/po/ar.po b/po/ar.po new file mode 100644 index 0000000..a9701b6 --- /dev/null +++ b/po/ar.po @@ -0,0 +1,476 @@ +# translation of openbox.pot to Arabic +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the Openbox package. +# +# Khaled Hosny <khaledhosny@eglug.org>, 2007. +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.3\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2007-07-21 14:43+0300\n" +"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n" +"Language-Team: Arabic <doc@arabeyes.org>\n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" +"Plural-Forms: nplurals=4; plural=n==1 ? 0 : n==2 ? 1 : n>=3 && n<=10 ? 2 : " +"3\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Ùشلت ÙÙŠ تØويل المسار \"%s\" من utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "" + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "اذهب هناك..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "أدÙر Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "أضÙÙ Ø³Ø·Ø Ù…ÙƒØªØ¨ جديد (_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "اØذ٠آخر Ø³Ø·Ø Ù…ÙƒØªØ¨ (_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "نواÙØ°" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Ø£Ø³Ø·Ø Ù…ÙƒØªØ¨" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "كل Ø£Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "طبقة (_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "دائما على Ø§Ù„Ø³Ø·Ø (_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "طبيعي (_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "دائما ÙÙŠ القاع (_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "أرسÙÙ„ إلى Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ (_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "قائمة العميل" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "استعÙد (_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "انقل (_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "ØجّÙÙ… (_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "صغّر (_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "كبّر (_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Ù„Ù٠لأعلى/لأسÙÙ„ (_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "ضع/أزل الØوا٠(_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "أغلق (_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "سياق غير صØÙŠØ \"%s\" ÙÙŠ ارتباط الÙأرة" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "زر غير صØÙŠØ \"%s\" Ù…Øدد ÙÙŠ مل٠الإعدادات" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "لم أستطÙع إنشاء الدليل '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "أغلق" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "يتعارض مع ارتباط المÙØ§ØªÙŠØ ÙÙŠ مل٠الإعدادات" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "لم أعثر على مل٠قائمة سليم \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ùشل تنÙيذ أمر Ù„ pipe-menu \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "خرج غير سليم من pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Øاولت الوصول إلى القائمة \"%s\" لكنها غير موجودة" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "المزيد..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "زر غير صØÙŠØ \"%s\" ÙÙŠ ارتباط الÙأرة" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "لم أستطع تغيير المجلد المنزلي \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "تعذّر ÙØªØ Ø§Ù„Ø¹Ø±Ø¶ من متغير البيئة DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "تعذّر بدأ مكتبة obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "خادم إكس لا يدعم المØليّة." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "لم أستطÙع ضبط Ù…ÙغيّÙرات المØليّة لخادم إكس." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "لم أعثر على مل٠إعدادات سليم، سأستخدم بعض الإÙتراضيات البسيطة" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "لم أستطÙع تØميل سÙمة." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Ùشلت إعادة التشغيل ÙÙŠ تنÙيذ Ù…ÙÙ†Ùّذ جديد \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Øقوق النسخ" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "الصيغة: openbox [options]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"الخيارات:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help اعرض هذه المساعدة ثم اخرج\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version اعرض النسخة ثم اخرج\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace استبدل مدير النواÙØ° الذي يعمل Øاليا\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable عطّÙÙ„ الإتصال بمدير الجلسة\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"تمرير رسائل لمرّة تعمل من أوبن‌بوكس:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure أعÙد تØميل إعدادات أوبن‌بوكس\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart أعÙد تشغيل أوبن‌بوكس\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr "" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"خيارات التنقيØ:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync شغّل ÙÙŠ النمط المزامن\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug اعرض خرْج التنقيØ\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus اعرض خرج Ø§Ù„ØªÙ†Ù‚ÙŠØ Ù„Ù„ØªØ¹Ø§Ù…Ù„ مع البؤرة\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama شق العرض إلى شاشات xinerama زائÙØ©\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"من Ùضلك أبلغ عن العلل إلى %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "معامل سطر أوامر غير سليم \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "يعمل مدير نواÙØ° بالÙعل على الشاشة %Id" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "تعذّر الØصول على انتقاء مدير النواÙØ° على الشاشة %Id" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "مدير النواÙØ° على الشاشة %Id لا وجود له" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "Ø³Ø·Ø Ø§Ù„Ù…ÙƒØªØ¨ %Ii" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "تشغيل %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ù…ÙØªØ§Ø Ù…ÙغيّÙر \"%s\" غير سليم ÙÙŠ ارتبط الÙأرة/لوØØ© المÙاتيØ" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "رمز Ù…ÙØªØ§Ø \"%s\" غير سليم ÙÙŠ ارتباط المÙتاØ" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "اسم Ù…ÙØªØ§Ø \"%s\" غير سليم ÙÙŠ ارتباط المÙتاØ" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "المÙØªØ§Ø Ø§Ù„Ù…Ø·Ù„ÙˆØ¨ \"%s\" لا وجود له ÙÙŠ العرض" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "لم أستطÙع ØÙظ الجلسة إلى \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "خطأ أثناء ØÙظ الجلسة إلى \"%s\": %s" + +#~ msgid "X Error: %s" +#~ msgstr "خطأ إكس: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Ùشلت ÙÙŠ تنÙيذ \"%s\": %s" diff --git a/po/bn_IN.po b/po/bn_IN.po new file mode 100644 index 0000000..b6a7d0a --- /dev/null +++ b/po/bn_IN.po @@ -0,0 +1,480 @@ +# translation of openbox to Bengali (India) +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the Openbox package. +# +# Runa Bhattacharjee <runabh@gmail.com>, 2007. +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2007-06-01 19:02+0530\n" +"Last-Translator: Runa Bhattacharjee <runabh@gmail.com>\n" +"Language-Team: Bengali (India) <en@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "" +"অবৈধ করà§à¦® \"%s\"-র অনà§à¦°à§‹à¦§ জানানো হয়েছে। à¦à¦‡ ধরনের কোনো করà§à¦® বরà§à¦¤à¦®à¦¾à¦¨à§‡ উপসà§à¦¥à¦¿à¦¤ নেই।" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "\"%s\" পাথটি utf8 থেকে রূপানà§à¦¤à¦° করতে বà§à¦¯à¦°à§à¦¥" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "" + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "চিহà§à¦¨à¦¿à¦¤ সà§à¦¥à¦¾à¦¨à§‡ চলà§à¦¨..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "উইনà§à¦¡à§‹" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "ডেসà§à¦•à¦Ÿà¦ª" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "সরà§à¦¬à¦ªà§à¦°à¦•à¦¾à¦° ডেসà§à¦•à¦Ÿà¦ª" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "সà§à¦¤à¦° (_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "সরà§à¦¬à¦¦à¦¾ উপরে (_t)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "সà§à¦¬à¦¾à¦à¦¾à¦¬à¦¿à¦• (_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "সরà§à¦¬à¦¦à¦¾ নীচে (_b)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "ডেসà§à¦•à¦Ÿà¦ªà§‡ পাঠানো হবে (_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "কà§à¦²à¦¾à§Ÿà§‡à¦¨à§à¦Ÿ মেনà§" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° (_e)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "সà§à¦¥à¦¾à¦¨à¦¾à¦¨à§à¦¤à¦°à¦£ (_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "মাপ পরিবরà§à¦¤à¦¨ (_z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "আইকন রূপে পà§à¦°à¦¦à¦°à§à¦¶à¦¨ (_n)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "বড় করà§à¦¨ (_x)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "উপরে/নীচে গà§à¦Ÿà¦¿à§Ÿà§‡ নিন (_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "বিনà§à¦¯à¦¾à¦¸ পরিবরà§à¦¤à¦¨ (_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "বনà§à¦§ করà§à¦¨ (_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "মাউস বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ অবৈধ কনটেকà§à¦¸à¦Ÿ \"%s\"" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "কনফিগ ফাইলে অবৈধ বাটন \"%s\" উলà§à¦²à¦¿à¦–িত হয়েছে" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "'%s' ডিরেকà§à¦Ÿà¦°à¦¿ নিরà§à¦®à¦¾à¦£ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "বনà§à¦§ করà§à¦¨" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "কনফিগ ফাইলে কি-বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ দà§à¦¬à¦¨à§à¦¦à§à¦¬" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "বৈধ মেনৠফাইল \"%s\" সনাকà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "পাইপ-মেনৠ\"%s\"-র জনà§à¦¯ কমানà§à¦¡ সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "পাইপ-মেনৠ\"%s\" থেকে অবৈধ ফলাফল পà§à¦°à¦¾à¦ªà§à¦¤ হয়েছে" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤ মেনৠ\"%s\" বà§à¦¯à¦¬à¦¹à¦¾à¦°à§‡à¦° পà§à¦°à¦šà§‡à¦·à§à¦Ÿà¦¾ হয়েছে" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "অতিরিকà§à¦¤..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "মাউস বাইনà§à¦¡à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ অবৈধ বাটন \"%s\"" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত ডিরেকà§à¦Ÿà¦°à¦¿ \"%s\"-তে পরিবরà§à¦¤à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "DISPLAY à¦à¦¨à¦à¦¾à¦¶à¦°à¦¨à¦®à§‡à¦¨à§à¦Ÿ à¦à§‡à¦°à¦¿à§Ÿà§‡à¦¬à¦²à§‡à¦° মান পà§à¦°à§Ÿà§‹à¦— করে পà§à¦°à¦¦à¦°à§à¦¶à¦¨ আরমà§à¦ করতে বà§à¦¯à¦°à§à¦¥à¥¤" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "obrender লাইবà§à¦°à§‡à¦°à¦¿ আরমà§à¦ করতে বà§à¦¯à¦°à§à¦¥à¥¤" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X সারà§à¦à¦¾à¦° দà§à¦¬à¦¾à¦°à¦¾ লোকেইল সমরà§à¦¥à¦¿à¦¤à¦¤ হয় না।" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "X সারà§à¦à¦¾à¦°à§‡à¦° জনà§à¦¯ লোকেইল মডিফায়ার নিরà§à¦§à¦¾à¦°à¦£ করতে বà§à¦¯à¦°à§à¦¥à¥¤" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "বৈধ কনফিগ ফাইল সনাকà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥, কয়েকটি সাধারণ ডিফলà§à¦Ÿ মান পà§à¦°à§Ÿà§‹à¦— করা হবে।" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "থিম লোড করতে বà§à¦¯à¦°à§à¦¥à¥¤" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "পà§à¦¨à¦°à¦¾à¦®à§à¦à§‡à¦° পরে নতà§à¦¨ à¦à¦•à§à¦¸à§‡à¦•à¦¿à¦‰à¦Ÿà§‡à¦¬à¦² \"%s\" সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "সà§à¦¬à¦¤à§à¦¬à¦¾à¦§à¦¿à¦•à¦¾à¦° (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦ªà§à¦°à¦£à¦¾à¦²à§€: openbox [বিকলà§à¦ª]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"বিবিধ বিকলà§à¦ª:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help সহায়তা বারà§à¦¤à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে পà§à¦°à¦¸à§à¦¥à¦¾à¦¨\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version সংসà§à¦•à¦°à¦£ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে পà§à¦°à¦¸à§à¦¥à¦¾à¦¨\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace বরà§à¦¤à¦®à¦¾à¦¨à§‡ চলমান উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾ পরিবরà§à¦¤à¦¨ করা হবে\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" +" --sm-disable সেশান পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦° সাথে সংযোগ নিষà§à¦•à§à¦°à¦¿à§Ÿ করা হবে\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"চলমান Openbox ইনসà§à¦Ÿà§à¦¯à¦¾à¦¨à§à¦¸à§‡ বারà§à¦¤à¦¾ পà§à¦°à§‡à¦°à¦£:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox-র কনফিগারেশন পà§à¦¨à¦°à¦¾à§Ÿ লোড করে\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox পà§à¦¨à¦°à¦¾à¦°à¦®à§à¦\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr "" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"ডিবাগ করার বিà¦à¦¿à¦¨à§à¦¨ বিকলà§à¦ª:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync সিঙà§à¦•à§à¦°à§‹à¦¨à¦¾à¦¸ মোডে সঞà§à¦šà¦¾à¦²à¦¿à¦¤ হবে\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug ডিবাগ-à¦à¦° ফলাফল পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus ফোকাস হà§à¦¯à¦¾à¦¨à§à¦¡à¦²à¦¿à¦‚ সংকà§à¦°à¦¾à¦¨à§à¦¤ ডিবাগের ফলাফল পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama পà§à¦°à¦¦à¦°à§à¦¶à¦¨ কà§à¦·à§‡à¦¤à§à¦°à¦Ÿà¦¿ নকল xinerama পরà§à¦¦à¦¾à§Ÿ à¦à¦¾à¦— করা হবে\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"অনà§à¦—à§à¦°à¦¹ করে %s-ঠবাগ সংকà§à¦°à¦¾à¦¨à§à¦¤ সূচনা দায়ের করà§à¦¨\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "অবৈধ কমানà§à¦¡-লাইন আরà§à¦—à§à¦®à§‡à¦¨à§à¦Ÿ \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "à¦à¦•à¦Ÿà¦¿ উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾ বরà§à¦¤à¦®à¦¾à¦¨à§‡ %d-ঠচলছে" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "পরà§à¦¦à¦¾ %d-ঠউইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦° নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ অংশ পà§à¦°à¦¾à¦ªà§à¦¤ করতে বà§à¦¯à¦°à§à¦¥" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "পরà§à¦¦à¦¾ %d-র উপর চলমান উইনà§à¦¡à§‹ পরিচালন বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦Ÿà¦¿ বনà§à¦§ করতে বà§à¦¯à¦°à§à¦¥" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +msgstr[1] "" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "desktop %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "%s সঞà§à¦šà¦¾à¦²à¦¿à¦¤ হচà§à¦›à§‡" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "কি/মাউস বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ মডিফায়ার-কি \"%s\"" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "কি-বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ কি-কোড \"%s\"" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "কি-বাইনà§à¦¡à¦¿à¦‚-র মধà§à¦¯à§‡ অবৈধ কি-র নাম \"%s\"" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "অনà§à¦°à§‹à¦§ করা কি \"%s\", পà§à¦°à¦¦à¦°à§à¦¶à¦¨ কà§à¦·à§‡à¦¤à§à¦°à§‡ উপসà§à¦¥à¦¿à¦¤ নেই" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "\"%s\"-র সেশান সংরকà§à¦·à¦£ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "\"%s\"-ঠসেশান সংরকà§à¦·à¦£à¦•à¦¾à¦²à§‡ সমসà§à¦¯à¦¾: %s" + +#~ msgid "X Error: %s" +#~ msgstr "X সংকà§à¦°à¦¾à¦¨à§à¦¤ তà§à¦°à§à¦Ÿà¦¿: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "\"%s\" সঞà§à¦šà¦¾à¦²à¦¨ করতে বà§à¦¯à¦°à§à¦¥: %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "\"%s\" করà§à¦®à§‡à¦° অবৈধ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¥¤ করà§à¦® উপেকà§à¦·à¦¾ করা হবে।" diff --git a/po/ca.po b/po/ca.po new file mode 100644 index 0000000..b892ebc --- /dev/null +++ b/po/ca.po @@ -0,0 +1,523 @@ +# Missatges en català per a openbox. +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# David Majà MartÃnez <davidmaja@gmail.com>, 2007, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-05-25 19:23+0200\n" +"Last-Translator: David Majà MartÃnez <davidmaja@gmail.com>\n" +"Language-Team: catalan\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "L'acció sollicitada \"%s\" no és và lida. Aquesta acció no existeix." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "No" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "SÃ" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Executa" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "No s'ha pogut convertir el camà \"%s\" des de utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancel·la" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Surt" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Esteu segur de voler sortir?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Surt" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Esteu segur de voler sortir de Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Surt de Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Finestra sense nom" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "S'està finalitzant..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "No està responent" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Sembla que la finestra \"%s\" no està responent. Voleu forçar-la a " +"finalitzar enviant el senyal %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Finalitza el procés" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Sembla que la finestra \"%s\" no està responent. Voleu desconnectar-la del " +"servidor d'X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Desconnecta" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Vés aquÃ..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Gestiona els escriptoris" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Afegeix un nou escriptori" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Suprimeix l'últim escriptori" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Finestres" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Escriptoris" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Tots els escriptoris" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Capa" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Sempre a so_bre" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Sempre a so_ta" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "A l'_escriptori" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menú del client" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Restaur_a" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Mou" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Redimen_siona" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimitza" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximitza" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "En/Desen_rotlla" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Sense/Amb _decoració" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Tanca" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "El context \"%s\" no és và lid en la vinculació del ratolÃ" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "El botó especificat al fitxer de configuració \"%s\" no és và lid." + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "No és pot crear el directori '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Tanca" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflicte amb la tecla vinculada en el fitxer de configuració" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "No s'ha pogut trobar un fitxer de menú \"%s\" và lid" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "" +"S'ha produït un error en executar l'ordre per al menú de conducte \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "La sortida del menú de conducte \"%s\" no és và lida" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "S'ha intentat accedir al menú \"%s\" ja que no existeix" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Més..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "El botó \"%s\" no és và lid en la vinculació del ratolÃ" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "No s'ha pogut canviar al directori de l'usuari \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "No s'ha pogut obrir la pantalla des de la variable d'entorn DISPLAY" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "S'ha produït un error en inicialitza la llibreria obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "El servidor X no te suport per a idiomes" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "No s'ha pogut assignar els modificadors del locale per al servidor X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"No s'ha pogut trobat un fitxer de configuració và lid, s'utilitzaran alguns " +"valors predeterminats" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "No s'ha pogut carregar el tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"S'han trobat un o mes errors de sintaxi XML en analitzar el fitxer de " +"configuració de Openbox. Per a més informació visualitza el stdout. L'últim " +"error trobat estava al fitxer \"%s\" lÃnia %d, amb el missatge: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Error de sintaxi de Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"S'ha produït un error en tornar a iniciar i executar el nou executable \"%s" +"\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaxis: openbox [opcions]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcions:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Visualitza aquesta ajuda i surt\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Visualitza la versió i surt\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Reemplaça el gestor de finestres que s'està executant " +"actualment\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FITXER\n" +" Especifica el camà del fitxer de configuració a " +"utilitzar\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Inhabilita la connexió amb el gestor de sessió\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"S'està transferint missatges a la instà ncia del Openbox que s'està " +"executant:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Torna a carregar la configuració de Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Torna a iniciar Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Surt de Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opcions de depuració:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Executa en mode sincronitzat\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Mostra la sortida de depuració\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Mostra la sortida de depuració per a la gestió del " +"focus\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Mostra la sortida de depuració per a la gestió del " +"session management\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama Divideix la visualització en pantalles xinerama " +"falses\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Informeu dels errors a %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s necessita un argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Opció \"%s\" no và lida a la lÃnia d'ordres\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Encara s'està executant un gestor de finestres a la pantalla %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "" +"No s'ha pogut adquirir la selecció del gestor de finestres en la pantalla %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "El gestor de finestres de la pantalla %d no està sortint" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"El Openbox està configurat per a %d escriptori, però la sessió actual en te " +"%d. S'està modificant la configuració del Openbox." +msgstr[1] "" +"El Openbox està configurat per a %d escriptoris, però la sessió actual en te " +"%d. S'està modificant la configuració del Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "escriptori %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Executant %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"La tecla modificadora \"%s\" no és và lida en la vinculació de tecles/ratolÃ" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "El codi de tecla \"%s\" no és và lid en la vinculació de tecles" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "El nom de la tecla \"%s\" no és và lid en la vinculació de tecles" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "La tecla seleccionada \"%s\" no existeix a la pantalla" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "D'acord" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Surt de Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file necessita un argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "L'acció SessionLogout no està disponible ja que el Openbox s'ha compilat " +#~ "sense suport per a la gestió de sessions" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "No s'ha pogut desar la sessió a \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "S'ha produït un error mentre es desava la sessió a \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "No esteu connectats al gestor de sessions" + +#~ msgid "X Error: %s" +#~ msgstr "Error d'X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "No s'ha pogut executar \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "L'ús de l'acció \"%s\" no és và lid. S'ignorarà aquesta acció." diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..79d84cf --- /dev/null +++ b/po/cs.po @@ -0,0 +1,501 @@ +# Czech translation for Openbox. +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the Openbox 3 package. +# tezlo <tezlo@gmx.net>, 2007 +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-17 17:00+0100\n" +"Last-Translator: tezlo <tezlo@gmx.net>\n" +"Language-Team: Czech <cs@li.org>\n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Požadována neplatná akce \"%s\". Žádná taková akce neexistuje." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ne" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ano" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Spustit" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "NepodaÅ™ilo se pÅ™evést cestu \"%s\" z utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "ZruÅ¡it" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Konec" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "UrÄitÄ› odhlásit?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Odhlásit" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "UrÄitÄ› chcete ukonÄit Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "UkonÄit Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Nepojmenované Okno" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "UkonÄuji..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "NeodpovÃdá" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "Okno \"%s\" nedpovÃdá. Chcete jej ukonÄit signálem %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "UkonÄit Proces" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Okno \"%s\" neodpovÃdá. Chcete jej odpojit od X serveru?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Odpojit" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Jdi tam..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Spravovat plochy" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_PÅ™idat novou plochu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Odstranit poslednà plochu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Okna" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Plochy" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "VÅ¡echny plochy" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Vrs_tva" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Vždy na_vrchu" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_NormálnÃ" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Vždy ve_spodu" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Poslat na plochu" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu klienta" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Obnovit" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "PÅ™es_unout" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Veli_kost" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimalizovat" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximalizovat" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "S_rolovat/Vyrolovat" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Oz_dobit/Odzdobit" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_ZavÅ™Ãt" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Neplatný kontext \"%s\" v nastavenà myÅ¡i" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Neplatné tlaÄÃtko \"%s\" v konfiguraÄnÃm souboru" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "NepodaÅ™ilo se vytvoÅ™it adresář '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "ZavÅ™Ãt" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt klávesových zkratek v konfiguraÄnÃm souboru" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "NepodaÅ™ilo se najÃt platný menu soubor \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "NepodaÅ™ilo se spustit pÅ™Ãkaz pro pipe-menu \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Neplatný výstup z pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Pokus o pÅ™Ãstup k menu \"%s\", ale ono neexistuje" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "VÃc..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Neplatné tlaÄÃtko \"%s\" v nastavenà myÅ¡i" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "NepodaÅ™ilo se pÅ™ejÃt do domácÃho adresáře \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "NepodaÅ™ilo se otevÅ™Ãt displej z promÄ›nné prostÅ™edà DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "NepodaÅ™ilo se inicializovat knihovnu obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server nepodporuje lokalizaci." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Nelze nastavit modifikátory lokalizace pro X server." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"NepodaÅ™ilo se najÃt platný konfiguraÄnà soubor, pokraÄuji s výchozÃm " +"nastavenÃm" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "NepodaÅ™ilo se naÄÃst motiv." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"PÅ™i naÄÃtánà konfiguraÄnÃch suborů nalezena jedna nebo vÃce syntaktických " +"chyb, vÃce informacà na standartnÃm výstupu. Poslednà zaznamenaná chyba je v " +"souboru \"%s\" na řádku %d, se zprávou: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Chyba Syntaxe" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "PÅ™i restartu se nepodaÅ™ilo spustit nový program \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntaxe: openbox [pÅ™epÃnaÄe]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"PÅ™epÃnaÄe:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Zobrazit tuto nápovÄ›du a skonÄit\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Zobrazit verzi a skonÄit\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Nahradit běžÃcà window manager\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Cesta ke konfiguraÄnÃmu souboru\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable NepÅ™ipojovat se k session manageru\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ZasÃlánà zpráv běžÃcà instanci Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Znovu naÄÃst konfiguraci Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Restartovat Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit UkonÄit Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"LadÃcà pÅ™epÃnaÄe:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Spustit v synchronnÃm módu\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Zobrazit ladÃcà výstup\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Zobrazit ladÃcà výstup pro správu oken\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama RozdÄ›lit displej na faleÅ¡né obrazovky xinerama\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"ProsÃm hlaÅ¡te chyby na %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s vyžaduje argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Neplatný argument pÅ™Ãkazové řádky \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Na obrazovce %d již nÄ›jaký window manager běžÃ" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "NepodaÅ™ilo se zÃskat výseÄ pro window manager na obrazovce %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Window manager na obrazovce %d ne a ne skonÄit" + +# TODO the rest of the file has plochy for plural and plochu for singular +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox je nakonfigurován pro %d ploch, ale souÄasná session má %d. " +"Konfigurace Openboxu bude zmÄ›nÄ›na." +msgstr[1] "" +"Openbox je nakonfigurován pro %d ploch, ale souÄasná session má %d. " +"Konfigurace Openboxu bude zmÄ›nÄ›na." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "plochu %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "SpouÅ¡tÃm %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Neplatný modifikátor \"%s\" v nastavenà klávesnice/myÅ¡i" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Neplatný kód klávesy \"%s\" v nastevenÃ" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Neplatné jméno klávesy \"%s\" v nastavenÃ" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Požadovaná klávesa \"%s\" na displeji neexistuje" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "UkonÄit Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file vyžaduje argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Akce SessionLogout nenà k dispozici jelikož byl Openbox zkompilován bez " +#~ "podpory session manageru" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "NepodaÅ™ilo se uložit session do \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Chyba bÄ›hem ukládánà session do \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "NepÅ™ipojen k session manageru" + +#~ msgid "X Error: %s" +#~ msgstr "X Chyba: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "NepodaÅ™ilo se spustit \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Neplatné užità akce \"%s\". Akce bude ignorována." diff --git a/po/da.po b/po/da.po new file mode 100644 index 0000000..9b5bc40 --- /dev/null +++ b/po/da.po @@ -0,0 +1,505 @@ +# Danish messages for openbox +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the openbox package. +# +# Jesper Sander <sander.contrib@gmail.com>, 2008 +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-08-19 16:50+0100\n" +"Last-Translator: Jesper Sander <sander.contrib@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Ugyldig operation \"%s\" anmodet. Operationen findes ikke." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nej" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ja" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Udfør" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Fejl ved konvertering af stien \"%s\" fra utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Afbryd" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Afslut" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Er du sikker pÃ¥ at du vil logge ud?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Log Ud" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Er du sikker pÃ¥ at du vil afslutte Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Afslut Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Unavngivet vindue" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Dræber..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Svarer Ikke" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Vinduet \"%s\" svarer ikke. Vil du udføre tvunget afslutning ved at sende %s " +"signalet?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Afslut proces" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Vinduet \"%s\" svarer ikke. Vil du frakoble vinduet fra X-serveren?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Frakoble" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "GÃ¥ der..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "HÃ¥ndter skrivebord" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Nyt skrivebord" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Fjern sidste skrivebord" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Vinduer" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Skrivebord" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Alle skriveborde" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "La_g" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Altid ø_verst" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "Nor_mal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Altid _nederst" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Send til skrivebord" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Klient-menu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "G_endan" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Flyt" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Ændre s_tørrelse" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "_Minimer" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximer" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Rul op/ned" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Fjern/tilføj _dekoration" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Luk" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Ugyldig indhold \"%s\" i muse-kombination" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ugyldig tast \"%s\" specificeret i konfigurationsfilen" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Kan ikke oprette mappe '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Luk" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt med taste-kombinationer i konfigurationsfilen" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Kan ikke finde en gyldig menufil \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Kunne ikke udføre kommando for pipe-menu \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ugyldig uddata fra pipe-menuen \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Forsøgte at Ã¥bne menuen \"%s\", men denne findes ikke" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mere..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Ugyldig knap \"%s\" i muse-kombination" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Kan ikke skifte til hjemmekataloget \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Kunne ikke Ã¥bne displayet fra DISPLAY-miljøvariablen" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Kunne ikke starte obrender-biblioteket." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-serveren understøtter ikke lokalisering." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Kan ikke indstille lokaliseringsmodifikatorene for X-serveren." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Kunne ikke finde en gyldig konfigurationsfil, bruger nogle simple " +"standardværdier" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Kan ikke hente et tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"En eller flere XML-syntaksfejl blev fundet ved læsning af " +"konfigurationsfilerne til Openbox. Se stdout for mere information. Den " +"sidste fejl som blev set var i fil \"%s\", linie %d, med beskeden: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox syntaksfejl" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Kunne ikke starte nyt program ved genstart: \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntaks: openbox [argumenter]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Tilvalg:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Vis denne hjælpetekst og afslut\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Vis versionsnummeret og afslut\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Erstat den kørende vinduesbehandler\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FIL Specificer stien til konfigurationsfilen du vil " +"benytte\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Deaktiver forbindelsen til sessionsbehandleren\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Sender beskeder til en kørende Openbox-instans:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Opdater Openbox' konfiguration\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Genstart Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Afslut Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Fejlsøgningsmuligheder:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Kør i synkron-modus\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Vis fejlsøgningsinformation\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Vis fejlsøgningsinformation for fokus-hÃ¥ndtering\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Vis fejlsøgningsinformation for session-hÃ¥ndtering\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Split displayet for \"falske\" xinerama-skærme\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Rapporter venligst fejl til %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s kræver et argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ugyldig kommandolinie-argument \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "En vindusbehandler kører allerede pÃ¥ skærm %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Kunne ikke hente vindusbehandlerens markering pÃ¥ skærm %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Vinduesbehandleren pÃ¥ skærm %d vil ikke afslutte" + +# TODO Figure out how to handle this case, the second number has the "desktop" +# on it here, but gettext looks at the first number only. If we split it up in +# two strings then we can't swap the order from the .po file... +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Aktiv session har %2$d skriveborde, mens Openbox er konfigureret til %1$d. " +"Benytter indstillingerne for den aktive session." +msgstr[1] "" +"Aktiv session har %2$d skriveborde, mens Openbox er konfigureret til %1$d. " +"Benytter indstillingerne for den aktive session." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "skrivebord %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Kører %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ugyldig modifikationstast \"%s\" i kombination for tast/mus" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ugyldig tastekode \"%s\" i tastekombination" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ugyldig tastenavn \"%s\" i tastekombination" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Ønsket tast \"%s\" eksisterer ikke i displayet" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Afslut Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file kræver et argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout er ikke tilgænglig, fordi Openbox blev kompileret uden " +#~ "understøttelse for sessionsbehandling" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Kan ikke gemme sessionen til \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Fejl mens session blev gemt til \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ikke forbundet til en sessionsbehandler" + +#~ msgid "X Error: %s" +#~ msgstr "Fejl i X: %s" diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..8d12a1f --- /dev/null +++ b/po/de.po @@ -0,0 +1,517 @@ +# German messages for openbox. +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Sebastian Vahl <svahl@web.de>, 2006. +# Simon A. Wilper <simonaw@openoffice.org>, Apr 2007 +# Peter Schwindt <schwindt@ba-loerrach.de> +# Finn Zirngibl <finn@s23.org>, 2008 +# Florian Walch <florian.walch@gmx.at>, 2008 +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-13 13:38+0100\n" +"Last-Translator: Florian Walch <florian.walch@gmx.at>\n" +"Language-Team: <de@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Unzulässige Aktion \"%s\" angefordert. Diese Aktion existiert nicht." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nein" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ja" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Ausführen" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Konnte Pfad \"%s\" nicht von UTF-8 konvertieren" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Abbrechen" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Beenden" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Sind Sie sicher, dass Sie sich abmelden wollen?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Abmelden" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Sind Sie sicher, dass Openbox beendet werden soll?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Beende Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Unbenanntes Fenster" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Wird beendet..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Reagiert nicht" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Das Fenster \"%s\" scheint nicht zu reagieren. Wollen Sie die Beendigung " +"durch das Senden des %s-Signals erzwingen?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Beende Prozess" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Das Fenster \"%s\" scheint nicht zu reagieren. Soll es vom X-Server getrennt " +"werden?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Trennen" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Hierher wechseln..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Desktops verwalten" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Neuen Desktop hinzufügen" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Letzten Desktop entfernen" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Fenster" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Desktops" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Alle Desktops" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Layer" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Immer im _Vordergrund" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Immer im _Hintergrund" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_An Desktop senden" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Client menu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Wi_ederherstellen" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Vers_chieben" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Größe ändern" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimieren" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximieren" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Auf/Ab_rollen" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Dekoration entfernen/_Dekorieren" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Schließen" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Maus-Binding enthält ungültigen Kontext \"%s\"" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Unzulässiger Button \"%s\" in der Konfigurationsdatei angegeben" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Das Verzeichnis '%s' konnte nicht angelegt werden: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Schließen" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt mit Tastenkombination in der Konfigurationsdatei" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Konnte keine gültige Menü-Datei \"%s\" finden" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Konnte Befehl \"%s\" für pipe-menu nicht ausführen: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ungültige Ausgabe vom pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "" +"Auf das Menü \"%s\" konnte nicht zugegriffen werden, da es nicht existiert" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mehr..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Maus-Binding enthält ungültigen Button \"%s\"" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Konnte nicht in das Heimatverzeichnis \"%s\" wechseln: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Konnte das Display aus der Umgebungsvariable DISPLAY nicht öffnen." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Konnte die obrender-Bibliothek nicht initialisieren." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Die gewählte Lokalisierung wird vom X-Server nicht unterstützt." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"Die Lokalisierungsmodifizierer für den X-Server konnten nicht gesetzt werden." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Es wurde keine gültige Konfigurationsdatei gefunden, benutze einfache " +"Standardwerte" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Konnte kein Thema laden." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Beim Parsen der Openbox-Konfigurationsdateien wurden ein oder mehrere XML-" +"Syntaxfehler gefunden. Die Standardausgabe enthält weitere Informationen. " +"Der letzte Fehler wurde in der Datei \"%s\" in Zeile %d festgestellt: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Syntax-Fehler" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"Neustart fehlgeschlagen, um die ausführbare Datei \"%s\" zu starten: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [Optionen]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Optionen:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Diese Hilfe anzeigen und beenden\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Version anzeigen und beenden\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Den aktuell laufenden Fenstermanager ersetzen\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file DATEI Pfad zur Konfigurationsdatei\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Keine Verbindung zum Sitzungsmanager aufbauen\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Nachrichten an eine laufende Openbox-Instanz weiterleiten:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox's Konfiguration neu laden\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox neu starten\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Beende Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debugging Optionen:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync im Synchronisierungsmodus starten\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Debugging-Informationen anzeigen\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Debugging-Informationen für's Fokus-Handling anzeigen\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Debugging-Informationen für's Session-Handling " +"anzeigen\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama Anzeige in künstliche Xinerama-Bildschirme aufteilen\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Bitte melden Sie Bugreports an: %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s erfordert einen Parameter\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ungültiges Kommandozeilen Argument \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Ein Fenstermanager läuft bereits auf Bildschirm %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Konnte die Fenstermanagerauswahl auf Bildschirm %d nicht reservieren" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Der Fenstermanager auf Bildschirm %d beendet sich nicht" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox wurde für %d Desktop konfiguriert, aber die aktuelle Sitzung hat %d. " +"Ãœberschreibe die Openbox-Konfiguration." +msgstr[1] "" +"Openbox wurde für %d Desktops konfiguriert, aber die aktuelle Sitzung hat " +"%d. Ãœberschreibe die Openbox-Konfiguration." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "Desktop %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Starte %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ungültige Modifier-Taste \"%s\" in Tastenbelegung/Maus-Binding" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ungültiger Tastencode \"%s\" in Tastenkombination" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ungültiger Tastenname \"%s\" in Tastenkombination" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Angeforderte Taste \"%s\" existiert nicht auf dem Display" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Beende Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file erfordert einen Parameter\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Die SessionLogout-Aktion ist nicht verfügbar, da Openbox ohne " +#~ "Unterstützung für Sitzungsmanagement kompiliert wurde" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Konnte die Sitzung \"%s\" nicht sichern: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Fehler beim Speichern der Sitzung nach \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nicht mit einem Sitzungsmanager verbunden" + +#~ msgid "X Error: %s" +#~ msgstr "X-Fehler: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Konnte \"%s\" nicht ausführen: %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Unzulässiger Einsatz der Aktion \"%s\". Aktion wird ignoriert." diff --git a/po/en@boldquot.po b/po/en@boldquot.po new file mode 100644 index 0000000..0df4f96 --- /dev/null +++ b/po/en@boldquot.po @@ -0,0 +1,499 @@ +# English translations for openbox package. +# Copyright (C) 2011 Dana Jansens +# This file is distributed under the same license as the openbox package. +# Automatically generated, 2011. +# +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# +# This catalog furthermore displays the text between the quotation marks in +# bold face, assuming the VT100/XTerm escape sequences. +# +msgid "" +msgstr "" +"Project-Id-Version: openbox 3.5.0-rc1\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2011-08-01 18:20+0200\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Invalid action “[1m%s[0m†requested. No such action exists." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "No" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Yes" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Execute" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Failed to convert the path “[1m%s[0m†from utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancel" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Exit" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Are you sure you want to log out?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Log Out" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Are you sure you want to exit Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Exit Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Unnamed Window" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Killing..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Not Responding" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"The window “[1m%s[0m†does not seem to be responding. Do you want to force " +"it to exit by sending the %s signal?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "End Process" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"The window “[1m%s[0m†does not seem to be responding. Do you want to " +"disconnect it from the X server?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Disconnect" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Go there..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Manage desktops" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Add new desktop" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Remove last desktop" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Windows" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Desktops" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "All desktops" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Layer" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Always on _top" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Always on _bottom" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Send to desktop" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Client menu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "R_estore" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Move" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Resi_ze" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Ico_nify" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximize" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Roll up/down" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Un/_Decorate" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Close" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Invalid context “[1m%s[0m†in mouse binding" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Invalid button “[1m%s[0m†specified in config file" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Unable to make directory '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Close" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflict with key binding in config file" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Unable to find a valid menu file “[1m%s[0mâ€" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Failed to execute command for pipe-menu “[1m%s[0mâ€: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Invalid output from pipe-menu “[1m%s[0mâ€" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Attempted to access menu “[1m%s[0m†but it does not exist" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "More..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Invalid button “[1m%s[0m†in mouse binding" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Unable to change to home directory “[1m%s[0mâ€: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Failed to open the display from the DISPLAY environment variable." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Failed to initialize the obrender library." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server does not support locale." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Cannot set locale modifiers for the X server." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "Unable to find a valid config file, using some simple defaults" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Unable to load a theme." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file “[1m%s[0m†line %d, with message: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Syntax Error" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Restart failed to execute new executable “[1m%s[0mâ€: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [options]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Options:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Display this help and exit\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Display the version and exit\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Replace the currently running window manager\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Specify the path to the config file to use\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Disable connection to the session manager\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Passing messages to a running Openbox instance:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Reload Openbox's configuration\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Restart Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Exit Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debugging options:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Run in synchronous mode\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr " --startup CMD Run CMD after starting\n" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Display debugging output\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Display debugging output for focus handling\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Display debugging output for session management\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Split the display into fake xinerama screens\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Please report bugs at %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requires an argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Invalid command line argument “[1m%s[0mâ€\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "A window manager is already running on screen %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Could not acquire window manager selection on screen %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "The WM on screen %d is not exiting" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[1] "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "desktop %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Running %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Invalid modifier key “[1m%s[0m†in key/mouse binding" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Invalid key code “[1m%s[0m†in key binding" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Invalid key name “[1m%s[0m†in key binding" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Requested key “[1m%s[0m†does not exist on the display" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" diff --git a/po/en@quot.po b/po/en@quot.po new file mode 100644 index 0000000..e9af2e4 --- /dev/null +++ b/po/en@quot.po @@ -0,0 +1,496 @@ +# English translations for openbox package. +# Copyright (C) 2011 Dana Jansens +# This file is distributed under the same license as the openbox package. +# Automatically generated, 2011. +# +# All this catalog "translates" are quotation characters. +# The msgids must be ASCII and therefore cannot contain real quotation +# characters, only substitutes like grave accent (0x60), apostrophe (0x27) +# and double quote (0x22). These substitutes look strange; see +# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html +# +# This catalog translates grave accent (0x60) and apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019). +# It also translates pairs of apostrophe (0x27) to +# left single quotation mark (U+2018) and right single quotation mark (U+2019) +# and pairs of quotation mark (0x22) to +# left double quotation mark (U+201C) and right double quotation mark (U+201D). +# +# When output to an UTF-8 terminal, the quotation characters appear perfectly. +# When output to an ISO-8859-1 terminal, the single quotation marks are +# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to +# grave/acute accent (by libiconv), and the double quotation marks are +# transliterated to 0x22. +# When output to an ASCII terminal, the single quotation marks are +# transliterated to apostrophes, and the double quotation marks are +# transliterated to 0x22. +# +msgid "" +msgstr "" +"Project-Id-Version: openbox 3.5.0-rc1\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2011-08-01 18:20+0200\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Invalid action “%s†requested. No such action exists." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "No" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Yes" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Execute" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Failed to convert the path “%s†from utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancel" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Exit" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Are you sure you want to log out?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Log Out" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Are you sure you want to exit Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Exit Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Unnamed Window" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Killing..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Not Responding" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"The window “%s†does not seem to be responding. Do you want to force it to " +"exit by sending the %s signal?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "End Process" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"The window “%s†does not seem to be responding. Do you want to disconnect " +"it from the X server?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Disconnect" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Go there..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Manage desktops" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Add new desktop" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Remove last desktop" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Windows" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Desktops" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "All desktops" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Layer" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Always on _top" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Always on _bottom" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Send to desktop" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Client menu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "R_estore" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Move" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Resi_ze" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Ico_nify" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximize" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Roll up/down" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Un/_Decorate" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Close" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Invalid context “%s†in mouse binding" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Invalid button “%s†specified in config file" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Unable to make directory '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Close" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflict with key binding in config file" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Unable to find a valid menu file “%sâ€" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Failed to execute command for pipe-menu “%sâ€: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Invalid output from pipe-menu “%sâ€" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Attempted to access menu “%s†but it does not exist" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "More..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Invalid button “%s†in mouse binding" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Unable to change to home directory “%sâ€: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Failed to open the display from the DISPLAY environment variable." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Failed to initialize the obrender library." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server does not support locale." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Cannot set locale modifiers for the X server." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "Unable to find a valid config file, using some simple defaults" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Unable to load a theme." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file “%s†line %d, with message: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Syntax Error" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Restart failed to execute new executable “%sâ€: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [options]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Options:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Display this help and exit\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Display the version and exit\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Replace the currently running window manager\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Specify the path to the config file to use\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Disable connection to the session manager\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Passing messages to a running Openbox instance:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Reload Openbox's configuration\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Restart Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Exit Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debugging options:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Run in synchronous mode\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr " --startup CMD Run CMD after starting\n" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Display debugging output\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Display debugging output for focus handling\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Display debugging output for session management\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Split the display into fake xinerama screens\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Please report bugs at %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requires an argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Invalid command line argument “%sâ€\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "A window manager is already running on screen %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Could not acquire window manager selection on screen %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "The WM on screen %d is not exiting" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[1] "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "desktop %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Running %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Invalid modifier key “%s†in key/mouse binding" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Invalid key code “%s†in key binding" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Invalid key name “%s†in key binding" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Requested key “%s†does not exist on the display" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..44c5e51 --- /dev/null +++ b/po/es.po @@ -0,0 +1,523 @@ +# Spanish messages for openbox. +# Copyright (C) 2005 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Miguel Calleja Gómez <mcg79@lycos.es>, 2005. +# Gustavo Varela <gustavo.varela [en] gmail [punto] com>, 2007 +# David Merino <rastiazul at yahoo . com>, 2007. +# Elián Hanisch <lambdae2@gmail.com>, 2008. +# Nicolás de la Torre <ndelatorre@gmail.com>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-05-04 16:39-0300\n" +"Last-Translator: Nicolás de la Torre <ndelatorre@gmail.com>\n" +"Language-Team: español <es@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "La acción \"%s\" solicitada es inválida. No existe tal acción." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "No" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "SÃ" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Ejecutar" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Falló al convertir la ruta \"%s\" desde utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancelar" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Salir" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "¿Está seguro que desea salir?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Salir" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "¿Está seguro que desea salir de Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Salir de Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Ventana sin nombre" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Terminando..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "No está respondiendo" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"La ventana \"%s\" no parece estar respondiendo. ¿Desea forzarla a salir " +"enviándole la señal %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Finalizar proceso" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"La ventana \"%s\" no parece estar respondiendo. ¿Desea desconectarla del " +"servidor X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Desconectar" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Ir ahÃ..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Administrar escritorios" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Añadir un nuevo escritorio" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Remover el último escritorio" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Ventanas" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Escritorios" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Todos los escritorios" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Capa" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Siempre _encima" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Siempre _debajo" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Enviar al escritorio" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menú del cliente" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Rest_aurar" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Mover" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Redimen_sionar" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizar" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximizar" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "En/Desen_rollar" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "_Decorar" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Cerrar" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Contexto inválido \"%s\" en mouse binding" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Botón inválido \"%s\" especificado en el archivo de configuración" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "No se puede crear el directorio '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Cerrar" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflicto con la combinación de teclas en el archivo de configuración" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "No es posible encontrar un archivo de menú \"%s\" válido" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Falló al ejecutar el comando para el pipe-menu \"%s\": \"%s\"" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Salida inválida del pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Intentó acceder al menú \"%s\" pero este no existe" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Más..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Botón inválido \"%s\" en mouse binding" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "No es posible cambiar al directorio home \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Falló abrir la pantalla desde la variable de entorno DISPLAY" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Falló la inicialización de la librerÃa obrender" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "El servidor X no soporta localizaciones." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"No se puede establecer los modificadores de localización para el servidor X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"No es posible encontrar un archivo de configuración válido, usando algunos " +"valores por defecto" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "No es posible cargar el tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Uno o más errores de sintaxis XML fueron encontrados leyendo los archivos de " +"configuración de Openbox. Ver salida (stdout) para mas información. El " +"último error viste estaba en el archivo \"%s\" linea %d, con el mensaje: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Error de Sintaxis" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "El reinicio falló en ejecutar el nuevo ejecutable \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaxis: openbox [opciones]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opciones:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Muestra esta ayuda y sale\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Muestra la versión y sale\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Remplaza el gestor de ventanas que esta corriendo " +"actualmente\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file ARCHIVO\n" +" Especifique la ruta del archivo de configuración a " +"usar\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" +" --sm-disable Deshabilita la conexión con el gestor de sesión\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Pasando mensajes a la instancia que esta corriendo de Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Recarga la configuración de Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Reinicia Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Cierra Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opciones de depuración:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Correr en modo sincrónico\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Mostrar salida del depurador\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Mostrar salida del depurador para el manejo del foco\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Mostrar salida del depurador para el manejo del " +"session\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama Separar la visualización en pantallas de xinerama " +"falsas\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Por favor reportar errores a %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requiere un argumento\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Argumento de la lÃnea de comando inválido \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Un gestor de ventanas ya esta corriendo en la pantalla %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "No se pudo obtener la selección del gestor de ventanas en pantalla %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "El WM en la pantalla %d no está saliendo" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox está configurado para escritorio %d, pero la sesión actual a %d. " +"Invalidando la configuración de Openbox." +msgstr[1] "" +"Openbox está configurado para escritorios %d, pero la sesión actual a %d. " +"Invalidando la configuración de Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "Escritorio %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Ejecutando %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Modificador de tecla \"%s\" inválido en combinaciones de teclas o ratón" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Código de tecla \"%s\" inválido en combinaciones de teclas" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Nombre de tecla \"%s\" inválido en combinaciones de teclas" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Tecla solicitada \"%s\" no existe en la pantalla" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Salir de Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file requiere un argumento\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "La acción SessionLogout no esta disponible ya que Openbox fue construido " +#~ "sin soporte de manejo de sesiones" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "No se puede salvar la sesión a \"%s\": \"%s\"" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Error mientras se salvaba la sesión a \"%s\": \"%s\"" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Sin conexión a un manejador de sesiones" + +#~ msgid "X Error: %s" +#~ msgstr "Error en X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Falló al ejecutar \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Uso inválido de la acción \"%s\". La acción sera ignorada." diff --git a/po/et.po b/po/et.po new file mode 100644 index 0000000..01330c2 --- /dev/null +++ b/po/et.po @@ -0,0 +1,500 @@ +# Openboxi eesti keele tõlge +# translation of openbox to Estonian +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the Openbox 3 package. +# +# Andres Järv <andresjarv@gmail.com>, 2007. +# Mihkel <turakas gmail com>, 2010 +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.11.1\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-04-21 21:40+0300\n" +"Last-Translator: mihkel <turakas@gmail.com>\n" +"Language-Team: Estonian <et@li.org>\n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Taotleti kehtetut käsklust \"%s\". Sellist käsklust pole olemas." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ei" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Jah" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Käivita" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Raja \"%s\" ümberkodeerimine UTF8-st ebaõnnestus" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Katkesta" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Välju" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Kas oled kindel, et soovid välja logida?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Logi välja" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Kas oled kindel, et soovid OpenBoxist väljuda?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Välju Openbox-st" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Nimetu aken" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Tapan..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ei vasta" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Paistab, et aken \"%s\" ei vasta enam. Kas soovid teda jõuga väljuma sundida " +"saates %s signaali?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Lõpeta protsess" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Paistab, et aken \"%s\" ei vasta enam. Kas soovid ta X serverist lahti " +"ühendada?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Ãœhenda lahti" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Mine sinna..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Halda töölaudu" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Lisa uus töölaud" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Eemalda viimane töölaud" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Aknad" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Töölauad" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Kõik töölauad" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Kiht" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Aken teiste _peal" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normaalne" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Aken teiste _all" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Saada töölauale" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Kliendi menüü" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Taasta" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Liiguta" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Muuda _suurust" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Muuda _ikooniks" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ksimeeri" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Rulli üles/alla" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Äär_ed sisse/välja" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "S_ulge" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Vigane kontekst \"%s\" hiire kiirklahvides" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Vigane nupp \"%s\" määratud seadistuste failis" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Kausta '%s' tegemine ebaõnnestus: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Sulge" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt kiirklahviga seadistuste failis" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Ei suudetud leida kehtivat menüüfaili \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ei suudetud käivitada torumenüü \"%s\" käsku: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Vigane väljund torumenüüst \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Ãœritati ligi pääseda menüüle \"%s\", aga seda pole olemas" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Rohkem..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Vigane nupp \"%s\" hiire kiirklahvides" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ei suudetud siseneda kodukataloogi \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "DISPLAY keskkonnamuutujas oleva ekraani avamine ebaõnnestus." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Obrender-damise teegi käivitamine ebaõnnestus." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server ei toeta lokaati." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ei suudetud sättida lokaadimuutujaid X serveri jaoks." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Kehtiva seadistuste faili leidmine ebaõnnestus, kasutatakse lihtsaid " +"vaikimisi seadeid" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Teema laadimine ebaõnnestus." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Ãœks või enam XML süntaki viga leiti Openboxi seadistuse faili parsides. " +"Rohkem infot leiad stdout-st. Viimane viga oli failis \"%s\", real %d ja " +"sõnum oli: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openboxi süntaksi viga" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Taaskäivitusel ebaõnnestus uue käivitusfaili \"%s\" käivitamine: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Autoriõigused (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Süntaks: openbox [seaded]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Seaded:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Selle abi kuvamine ja väljumine\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Versiooni kuvamine ja väljumine\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Hetkel töötava aknahalduri asendamine\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FAIL Määra kasutatava seadistuste faili teekond\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Seansihalduriga ühenduse keelamine\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Jooksvale Openboxi seansile sõnumite edastamine:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openboxi konfiguratsioon uuesti laadimine\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openboxi taaskäivitamine\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Välju Openbox-st\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Silumise seaded:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Sünkroonselt jooksutamine\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Silumisväljundi kuvamine\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Fookusekäsitluse siluriväljundi kuvamine\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Ekraani võlts-Xinerama ekraanideks jagamine\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Palun teata vigadest siia %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s nõuab argumenti\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Vigane käsurea argument \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Ekraanil %d juba jookseb aknahaldur" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Ei suuda hankida aknahaldurite loetelu ekraanil %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Aknahaldur ekraanil %d ei sulgu" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox on seadistatud %d töölauale, aga aktiivsel seansil on %d. Tühistan " +"Openboxi seadistuse." +msgstr[1] "" +"Openbox on seadistatud %d töölauale, aga aktiivsel seansil on %d. Tühistan " +"Openboxi seadistuse." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "töölaud %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Jooksev %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Vigane muutujaklahv \"%s\" hiire/klaviatuuri kiirklahvides" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Vigane klahvikood \"%s\" kiirklahvil" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Vigane klahvinimi \"%s\" kiirklahvil" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Soovitud klahvi \"%s\" ei ole sellel ekraanil" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Sobib" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Seansi \"%s\" salvestamine ebaõnnestus: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Seansi \"%s\" salvestamisel ilmnes viga: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Pole ühendatud seansihalduriga" + +#~ msgid "X Error: %s" +#~ msgstr "X-i viga: %s" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout tegevust pole saadaval, kuna Openbox on kompileeritud " +#~ "seansi haldamise toeta" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "\"%s\" käivitamine ebaõnnestus: %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Käskluse \"%s\" kasutus on kehtetu. Käsklust ignoreeritakse." diff --git a/po/eu.po b/po/eu.po new file mode 100644 index 0000000..01501e7 --- /dev/null +++ b/po/eu.po @@ -0,0 +1,506 @@ +# Basque translation for openbox. +# Copyright (C) 2008 Inko Illarramendi Arancibia +# This file is distributed under the same license as the openbox package. +# Inko Illarramendi Arancibia <inkoia@gmail.com>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-10-22 18:06+0100\n" +"Last-Translator: Inko I. A. <inkoia@gmail.com>\n" +"Language-Team: Inko I. A. <inkoia@gmail.com>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Eskatutako \"%s\" ekintza baliogabea. Ez da ekintza hori existitzen." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ez" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Bai" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Exekutatu" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Hutsegitea \"%s\" helbidea utf8-tik bihurtzean" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Ezeztatu" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Irten" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Ziur al zaude saioa itxi nahi duzula?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Saioa Itxi" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Ziur al zaude Openbox-etik irten nahi duzula?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Openbox-etik Irten" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Izenik gabeko leihoa" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Akabatzen..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Erantzunik Ez" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Badirudi \"%s\" leihoak ez duela erantzuten. Nahi al duzu istea behartu %s " +"seinalea bidaliz?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Prozesua Amaitu" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Badirudi \"%s\" leihoak ez duela erantzuten. Nahi al duzu leihoa X " +"zerbitzaritik deskonektatu?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Deskonektatu" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Hona joan..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Idazmahaiak kudeatu" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Idazmahai berria _gehitu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Azken idazmahaia _ezabatu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Leihoak" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Idazmahaiak" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Idazmahai guztiak" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Geruza" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Beti _gainean" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Ohikoa" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Beti _azpian" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Bidali idazmahaira" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Bezero menua" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Berr_ezarri" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Mugitu" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Tamaina aldatu" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Iko_notu" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximizatu" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Bildu/_Zabaldu" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Des/_Dekoratu" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Itxi" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Baliogabeko \"%s\" testuingurua sagu elkarketan" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Konfigurazio fitxategian zehaztutako \"%s\" botoia baliogabea" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ezin da '%s' direktorioa sortu: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Itxi" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Gatazka konfigurazio fitxategiko tekla elkarketarekin" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Ezin da \"%s\" baliozko menu fitxategi bat aurkitu" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Hutsegitea \"%s\" pipe-menuarentzat komandoa exekutatzean: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Baliogabeko irteera \"%s\" pipe-menutik" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "\"%s\" menua atzitzen saiatu da baina ez da existitzen" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Gehiago..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Baliogabeko \"%s\" botoia sagu elkarketan" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ezin da \"%s\" hasiera direktoriora aldatu: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Hutsegitea pantaila irekitzean DISPLAY ingurune aldagaitik." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Hutsegitea obrender liburutegia hasieratzean." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X zerbitzariak ez du locale euskarririk." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ezin da locale modifikatzailerik ezarri X zerbitzariarentzat." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ezin da baliozko konfigurazio fitxategirik aurkitu, hainbat aukera lehenetsi " +"sinple erabiltzen" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ezin da gai bat kargatu." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"XML sintaxi errore bat edo gehiago aurkitu dira Openbox konfigurazio " +"fitxategiak interpretatzerakoan. Ikusi stdout informazio gehiago jasotzeko. " +"Azken errorea \"%s\" fitxategian %d lerroan izan da, mezu honekin: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox sintaxi errorea" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Berrabiarazteak hutsegitea \"%s\" exekutagarri berria exekutatzean: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaxia: openbox [aukerak]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Aukerak:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Mezu hau erakutsi eta irten\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Bertsioa bistarazi eta irten\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Ordezkatu exekutatzen ari den leiho-kudeatzailea\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +"--config-file FILE Zehaztu erabiltzeko konfigurazio fitxategirako bidea\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Ezgaitu saio kudeatzailearekiko konexioa\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Exekutatzen ari den Openbox instantzia bati mezuak pasatzen:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Birkargatu Openbox-en konfigurazioa\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Berrabiarazi Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Itxi Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Arazketa aukerak:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Modu sinkronoan exekutatu\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Arazketa irteera erakutsi\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Erakutsi arazketa irteera foku maneiurako\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Zatitu pantaila xinerama pantaila faltsuetan\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"%s helbidean erroreen berri eman mesedez\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s argumentu bat behar du\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "\"%s\" komando lerro argumentu baliogabea\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "" +"Bistaratzeko %d pantailan aurretik leiho-kudeatzaile bat exekutatzen ari da" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "" +"Ezin izan da eskuratu leiho-kudeatzailearen hautapena bistaratzeko %d " +"pantailan" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "%d bistaratze pantailako leiho-kudeatzailea ez da irteten" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox %d idazmahaientzat konfiguratua dago, baina uneko saioak %d dauzka. " +"Openbox konfigurazioa gainjartzen." +msgstr[1] "" +"Openbox %d idazmahaientzat konfiguratua dago, baina uneko saioak %d dauzka. " +"Openbox konfigurazioa gainjartzen." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "%i Idazmahaia" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Egikaritzen %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr " tekla/sagu elkarketan \"%s\" modifikatzaile tekla baliogabea" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr " tekla elkarketan \"%s\" tekla kode baliogabea" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr " tekla elkarketan \"%s\" tekla izen baliogabea" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Eskatutako \"%s\" tekla ez da pantaila existitzen" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Ados" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Openbox-etik Irten" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file argumentu bat behar du\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout ekintza ez dago eskuragarri, Openbox saio kudetzaile " +#~ "gaitasun gabe konpilatua izan baitzen" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ezin da saioa \"%s\"-n gorde: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Errorea saioa \"%s\"-n gordetzean: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Saio kudeatzaile batera ez konektatua" + +#~ msgid "X Error: %s" +#~ msgstr "X errorea: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Hutsegitea \"%s\" exekutatzean: %s" diff --git a/po/fi.po b/po/fi.po new file mode 100644 index 0000000..7cc1e93 --- /dev/null +++ b/po/fi.po @@ -0,0 +1,499 @@ +# Openbox Finnish translation. +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Pauli Virtanen <pauli.virtanen@hut.fi>, 2005. +# Lauri Hakko <aperculum@gmail.com>, 2008. +# Elias Julkunen <elias.julkunen@gmail.com>, 2008. +# Jarkko Piiroinen <jarkkop@iki.fi>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.11\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-03-13 21:56+0100\n" +"Last-Translator: Lauri Hakko <aperculum@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Pyydettiin virheellinen toiminto \"%s\". Toimintoa ei ole olemassa." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ei" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Kyllä" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Suorita" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Polun \"%s\" muuntaminen utf8:sta epäonnistui" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Peruuta" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Sulje" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Haluatko varmasti kirjautua ulos?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Kirjaudu ulos" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Haluatko varmasti sulkea Openboxin" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Sulje Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Nimetön ikkuna" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Tapetaan..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ei vastaa" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Ikkuna \"%s\" ei näytä vastaavan. Haluatko sulkea sen lähettämällä sille " +"singaalin %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Lopeta prosessi" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Ikkuna \"%s\" ei näytä vastaavan. Haluatko katkaista sen yhteyden X-" +"palvelimeen?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Katkaise yhteys" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Näytä tämä..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Työtilojen hallinta" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Lisää uusi työtila" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Poista viimeisin työtila" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Ikkunat" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Työtilat" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Kaikkiin työtiloihin" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Kerros" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Aina _päällimmäisenä" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Tavallinen" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Aina _alimmaisena" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Lähetä työtilaan" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Ikkunan valikko" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Palauta" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "S_iirrä" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Muuta kokoa" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Pie_nennä" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Suurenn_a" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Rullaa _ylös/alas" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "(Epä)_reunusta" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Sulje" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Virheellinen asiayhteys \"%s\" hiirisidonnoissa" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Asetustiedostossa määritelty painike \"%s\" on virheellinen" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Hakemiston '%s' luonti epäonnistui: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Sulje" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Päällekäisiä näppäinsidontoja asetustiedostossa" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Toimivaa valikkotiedostoa ei löytynyt \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Putkivalikon suorittaminen epäonnistui \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Virheellinen tulos putkivalikosta \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Valikon \"%s\" lukemista yritettiin, mutta sitä ei ole olemassa" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Lisää..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Virheellinen painike \"%s\" hiirisidonnoissa" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Kotihakemistoon \"%s\" vaihtaminen epäonnistui: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Näytön avaaminen DISPLAY-muuttujasta epäonnistui." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Obrender-kirjaston käynnistäminen epäonnistui." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-palvelin ei tue maa-asetusta." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Maa-asetusmuuttujia ei voitu tehdä X-palvelimelle." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Kelvollista asetustiedostoa ei löytynyt, käytetään yksinkertaisia " +"oletusarvoja" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Teeman lataaminen epäonnistui." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Yksi tai useampi XML-syntaksivirhe löytyi asetustiedostoa käsiteltäessä. Lue " +"stdout saadaksesi lisätietoja. Viimeisin virhe oli tiedostossa \"%s\" " +"rivillä %d: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox syntaksivirhe" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"Uudelleenkäynnistys ei onnistunut käynnistämään uutta ohjelmaa \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Tekijänoikeudet (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntaksi: openbox [valitsin]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Käyttö:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Näytä tämä ohje ja poistu\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Näytä version tiedot ja poistu\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Korvaa käynnissä oleva ikkunointiohjelma\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Määritä käytettävän asetustiedoston polku\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Estä yhteys istuntojen hallintaan\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Komentojen antaminen käynnissä olevalle Openboxille:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Lataa Openboxin asetustiedosto uudelleen\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Käynnistä Openbox uudelleen\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Sulje Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Vianjäljityksen asetukset:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Aja synkronointi-tilassa\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Näytä vianjäljitystuloste\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Näytä vianjäljitystuloste ikkunavalitsimelle\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Jaa näyttö kahteen vale-xinerama-ruutuun\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Ilmoita virheistä: %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s tarvitsee argumentin\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Virheellinen valitsin \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Ikkunointiohjelma on jo käynnissä näytöllä %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Ikkunointiohjelman valinta ruudulla %d ei onnistunut" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Ikkunointiohjelma ruudulla %d ei sulkeudu" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox on asetettu käyttämään %d työtilaa, mutta nykyisessä istunnossa " +"työtiloja on %d. Ohitetaan Openboxin asetus." +msgstr[1] "" +"Openbox on asetettu käyttämään %d työtilaa, mutta nykyisessä istunnossa " +"työtiloja on %d. Ohitetaan Openboxin asetus." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "työtila %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Suoritetaan %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Virheellinen valintanäppäin \"%s\" näppäin- tai hiirisidonnoissa" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Virheellinen näppäinkoodi \"%s\" pikanäppäimissä" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Virheellinen näppäin \"%s\" pikanäppäimissä" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Pyydettyä näppäintä \"%s\" ei ole olemassa näytöllä" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Istuntoa ei voitu tallentaa hakemistoon \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Virhe tallennettaessa istuntoa hakemistoon \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ei yhteyttä istunnon hallintaan" + +#~ msgid "X Error: %s" +#~ msgstr "X-virhe: %s" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout tapahtuma ei ole suoritettavissa, koska Openbox käännettiin " +#~ "ilman istunnon hallinnan tukea" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Ohjelman \"%s\" suorittaminen epäonnistui: %s" diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..36a2908 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,527 @@ +# French translation of Openbox. +# Copyright (C) 2004 Mikael Magnusson +# This file is distributed under the same license as the Openbox package. +# +# tioui <leonptitlouis@wanadoo.fr>, 2004. +# Cyrille Bagard <nocbos@gmail.com>, 2007-2008. +# Jacques BON <jbon@cafcom.net>, 2007. +# Éric Lassauge <lassauge@users.sf.net>, 2008 +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-02 02:06+0100\n" +"Last-Translator: Cyrille Bagard <nocbos@gmail.com>\n" +"Language-Team: français <fr@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Action demandée invalide \"%s\". Une telle action n'existe pas." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Non" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Oui" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Exécuter" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Échec de la conversion du chemin « %s » depuis l'UTF-8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Annuler" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Quitter" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Etes vous certain de vouloir vous déconnecter ?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Déconnexion" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Etes vous certain de vouloir quitter Openbox ?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Quitter Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Fenêtre sans nom" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Tue..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ne répond pas" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"La fenêtre \"%s\" semble ne pas répondre. Voulez vous la forcer à se " +"terminer en envoyant un signal %s ?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Fin de processus" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"La fenêtre \"%s\" semble ne pas répondre. Voulez vous la déconnecter du " +"serveur X ?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Déconnexion" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Aller là..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Gérer les bureaux" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Ajouter un bureau" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Supprimer le dernier bureau" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Fenêtres" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Bureaux" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Tous les bureaux" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Disposition" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "_Toujours au premier plan" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Toujours en _arrière plan" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "En_voyer vers le bureau" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu de la fenêtre" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "R_estaurer" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Dé_placer" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Red_imensionner" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Ico_nifier" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximiser" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "En/Dé_rouler" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Ne pas/Dé_corer" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Fermer" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Contexte « %s » invalide dans le paramétrage de la souris" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Bouton « %s » indiqué dans le fichier de configuration invalide" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Impossible de créer le répertoire « %s » : %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Fermer" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflit entre les raccourcis clavier dans le fichier de configuration" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Impossible de trouver un fichier de menus valide « %s »" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Échec lors de l'exécution de la commande pour un pipe-menu « %s » : %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Sortie du pipe-menu invalide « %s »" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Tentative d'accès au menu « %s » qui n'existe pas" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Plus..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Bouton « %s » invalide dans le paramétrage de la souris" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Impossible de changer vers le répertoire de l'utilisateur « %s » : %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "" +"Échec de l'ouverture de l'affichage depuis la variable d'environnement " +"DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Échec de l'initialisation de la bibliothèque obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Le serveur X ne supporte pas la localisation." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"Impossible d'appliquer les modifications de localisation pour le serveur X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Impossible de trouver un fichier de configuration valide, utilisation de " +"défauts simples" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Impossible de charger un thème." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Une ou plusieurs erreurs de syntaxe XML ont été trouvées lors de l'analyse " +"des fichiers de configuration d'Openbox. Voir stdout pour plus " +"d'information. La dernière erreur vue était dans le fichier \"%s\", ligne " +"%d, avec le message : %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Erreur de syntaxe Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"Le redémarrage n'a pas réussi à exécuter le nouvel exécutable « %s » : %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntaxe : openbox [options]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Options :\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Affiche cette aide et quitte\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Affiche la version et quitte\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Remplace le gestionnaire de fenêtres actuellement en " +"usage\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Spécifie le chemin du fichier de configuration à " +"utiliser\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" +" --sm-disable Désactive la connexion au gestionnaire de sessions\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Passage de messages à l'instance d'Openbox en cours :\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Recharge la configuration d'Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Redémarre Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Sortir d'Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Options de déboguage :\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Exécute en mode synchrone\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Affiche la sortie de déboguage\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Affiche la sortie de déboguage pour la gestion du " +"focus\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Affiche la sortie de déboguage pour la gestion du " +"session\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama Découpe l'affichage en écrans xinerama factices\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Veuillez soumettre les rapports de bogues à %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requiert un argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Argument de la ligne de commande invalide « %s »\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Un gestionnaire de fenêtres est déjà lancé sur l'écran %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "" +"Impossible d'acquérir la sélection du gestionnaire de fenêtres pour l'écran " +"%d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "" +"Le gestionnaire de fenêtres sur l'écran %d n'est pas en train de se terminer" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox est configuré pour %d bureau, mais la session en a %d. Ceci " +"supplante la configuration d'Openbox." +msgstr[1] "" +"Openbox est configuré pour %d bureaux, mais la session en a %d. Ceci " +"supplante la configuration d'Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "bureau %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Exécution de %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Touche de modification « %s » invalide dans le paramétrage du clavier / de " +"la souris" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Code de touche « %s » invalide dans le raccourci clavier" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Nom de touche « %s » invalide dans le raccourci clavier" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "La touche demandée « %s » n'existe pas pour l'affichage" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Quitter Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file requiert un argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "L'action SessionLogout n'est pas disponible comme Openbox a été construit " +#~ "sans support de gestion de session" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Impossible de sauvegarder la session dans « %s » : %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Erreur lors de la sauvegarde de la session depuis « %s » : %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Non connecté à un gestionnaire de session" + +#~ msgid "X Error: %s" +#~ msgstr "Erreur X : %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Échec de l'exécution de « %s » : %s" diff --git a/po/hr.po b/po/hr.po new file mode 100644 index 0000000..dacabfb --- /dev/null +++ b/po/hr.po @@ -0,0 +1,503 @@ +# Croatian messages for openbox. +# Copyright (C) 2009 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2009-04-05 16:53+0200\n" +"Last-Translator: boljsa <asjlob AT vip.hr>\n" +"Language-Team: <asjlob AT vip.hr>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Nevažeća akcija \"%s\" zatražena. Takva akcija ne postoji." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ne" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Da" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "IzvrÅ¡i" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Neuspio pokuÅ¡aj pretvorbe putanje \"%s\" iz utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Odustani" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "IzaÄ‘i" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Jeste li sigurni da se želite odjaviti?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Odjava" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Jeste li sigurni da želite zatvoriti Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Zatvori Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Neimenovan Prozor" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Ubijanje..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ne Odgovara" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Prozor \"%s\" ne reagira. Želite li forsirati izlaženje Å¡aljući %s signal?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "ZavrÅ¡etak Procesa" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Prozor \"%s\" ne reagira. Želite li prekinuti njegovu vezu sa X serverom?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Prekid veze" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Idi tamo..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Upravljanje radnim povrÅ¡inama" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Dodaj novu radnu povrÅ¡inu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Ukloni zadnju radnu povrÅ¡inu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Prozori" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Radne PovrÅ¡ine" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Sve radne povrÅ¡ine" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Sloj" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Uvijek na _vrhu" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normalno" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Uvijek na _dnu" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "PoÅ¡alji na _radnu povrÅ¡inu" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Izbornik klijenta" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "O_bnovi" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Pomicanje" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Prom_jena veliÄine" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizacija" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "M_aksimizacija" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Okretanje gore/dolje" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Ne/_Dekoriranje" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Zatvori" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Nevažeći kontekst \"%s\" u povezivanju miÅ¡a" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Nevažeće dugme \"%s\" specificirano u konfiguracijskoj datoteci" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ne mogu stvoriti direktorij '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Zatvori" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt sa povezivanjem tipki u konfiguracijskoj datoteci" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Ne mogu pronaći važeću datoteku izbornika \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Neuspio pokuÅ¡aj izvrÅ¡avanja naredbe za cijev-izbornik \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Nevažeći izlaz za cijev-izbornik \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "PokuÅ¡avam pristupiti izborniku \"%s\" ali on ne postoji" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "ViÅ¡e..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Nevažeće dugme \"%s\" u povezivanju miÅ¡a" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ne mogu doći u home direktorij \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Neuspio pokuÅ¡aj otvaranja zaslona iz DISPLAY varijable okruženja." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Neuspio pokuÅ¡aj inicijalizacije obrender biblioteke." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server ne podržava lokalno." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ne mogu postaviti lokalne modifikatore za X server." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ne mogu pronaći važeću konfiguracijsku datoteku, koriteći neke jednostavne " +"standarde" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ne mogu pokrenuti temu." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Jedna ili viÅ¡e XML pogreÅ¡ki u sintaksi su pronaÄ‘ene prilikom analize Openbox " +"konfiguracijskih datoteka. Pogledajte stdout za viÅ¡e informacija. Zadnja " +"pogreÅ¡ka je u datoteci \"%s\" u liniji %d, sa porukom: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox PogreÅ¡ka u Sintaksi" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Restart je bio neusjeÅ¡an za izvrÅ¡enje novog izvrÅ¡nog \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaksa: openbox [opcije]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcije:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Prikazuje ovu pomoć i izlazi\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Prikazuje verziju i izlazi\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Zamjenjuje trenutno pokrenut upravitelj prozora\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Specificira putanju do konfiguracijske datoteke koja " +"se koristi\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Onemogućuje vezu sa upraviteljom sesija\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ProsljeÄ‘uje poruke pokrenutoj Openbox instanci:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Osvježava Openbox konfiguraciju\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Restartira Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Izlazi iz Openbox-a\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opcije traženja pogreÅ¡aka:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Pokretanje u sinkronizacijskom modu\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Prikazuje izlaz traženja pogreÅ¡aka\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Prikazuje izlaz traženja pogreÅ¡aka za rukovanje " +"fokusom\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Prikazuje izlaz traženja pogreÅ¡aka za rukovanje " +"sessionom\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Podijeli zaslon u lažne xinerama zaslone\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Molimo prijavite pogreÅ¡ku na %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s zahtjeva argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Nevažeći argument komandne linije \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Upravitelj prozora je već pokrenut na zaslonu %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Ne mogu ostvariti odabir upravitelja prozora na zaslonu %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Upravitelj prozora na zaslonu %d ne izlazi" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox je konfiguriran za %d radnu povrÅ¡inu, ali trenutna sesija ima %d. " +"Prepisujem preko Openbox konfiguracije." +msgstr[1] "" +"Openbox je konfiguriran za %d radnu povrÅ¡inu, ali trenutna sesija ima %d. " +"Prepisujem preko Openbox konfiguracije." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "radna povrÅ¡ina %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Pokrenuto %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Nevažeći modifikacijski kljuÄ \"%s\" u povezivanju tipke/miÅ¡" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Nevažeći kod kljuÄa \"%s\" u povezivanju tipki" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Nevažeće ime tipke \"%s\" u povezivanju tipki" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Traženi kljuÄ \"%s\" ne postoji na zaslonu" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Zatvori Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file zahtjeva argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Akcija SessionLogout nije dostupna otkad je Openbox izgraÄ‘en bez podrÅ¡ke " +#~ "upravljanja sesijama" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ne mogu spremiti sesiju u \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "PogreÅ¡ka prilokom spremanja sesije u \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nije povezan sa upraviteljem sesija" + +#~ msgid "X Error: %s" +#~ msgstr "X PogreÅ¡ka: %s" diff --git a/po/hu.po b/po/hu.po new file mode 100644 index 0000000..52ba588 --- /dev/null +++ b/po/hu.po @@ -0,0 +1,496 @@ +# Hungarian translation of openbox. +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the openbox package. +# Robert Kuszinger <hiding@freemail.hu>, 2007. +# Laszlo Dvornik <dvornik@gnome.hu>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: openbox 3.4.10\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-01-14 11:04+0100\n" +"Last-Translator: Laszlo Dvornik <dvornik@gnome.hu>\n" +"Language-Team: Hungarian <translation-team-hu@lists.sourceforge.net>\n" +"Language: hu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Érvénytelen művelet \"%s\". Nem létezik ilyen művelet." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nem" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Igen" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Végrehajtás" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Az útvonalat nem sikerült átalakÃtani utf8-ból: \"%s\"" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Mégsem" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Kilépés" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Biztos ki akar jelentkezni?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Kijelentkezés" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Biztos ki akar lépni az Openboxból?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Kilépés az Openboxból" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Névtelen ablak" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Kilövés..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Nem válaszol" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"A(z) \"%s\" ablak nem válaszol. ErÅ‘ltessük a kilépést a %s jelzés küldésével?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Folyamat vége" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "A(z) \"%s\" ablak nem válaszol. Lekapcsoljuk az X kiszolgálóról?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Lekapcsolódás" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Ugrás..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Munkaasztal-kezelés" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Új munk_aasztal" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Utolsó munkaasztal _eltávolÃtása" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Ablakok" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Munkaasztalok" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Összes munkaasztal" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Réteg" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Mindig _felül" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normál" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Mindig _alul" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Munkaasztalra _küldés" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Kliens menü" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_VisszaállÃtás" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Ã_thelyezés" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Ãtmérete_zés" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Iko_nizálás" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximalizálás" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Görgetés fel/le" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "_Dekoráció eltávolÃtása" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Bezárás" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Érvénytelen környezet az egér hozzárendeléseknél: \"%s\"" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Érvénytelen gomb a konfigurációs fájlban \"%s\"" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Nem lehet létrehozni a könyvtárat '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Bezárás" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "ÃœtközÅ‘ billentyű hozzárendelések a konfigurációs fájlban" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Nem található ilyen érvényes menüfájl: \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Nem sikerült végrehajtani a parancsot a csÅ‘vezeték-menüben \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Érvnytelen kimenet a csÅ‘vezeték-menübÅ‘l \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "\"%s\" menü elérésére történt kÃsérlet, de az nem létezik" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Tovább..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Érvénytelen gomb \"%s\" az egér hozzárendeléseknél" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Nem lehet a saját könyvtárba váltani \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "" +"Nem sikerült megnyitni a DISPLAY környezeti változóban beállÃtott képernyÅ‘t." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Nem sikerült elÅ‘készÃteni az obrender programkönyvtárat." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Az X-kiszolgáló nem támogatja ezt a nemzetközi beállÃtást." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"Nem lehet beállÃtani a nemzetközi beállÃtás-módosÃtókat az X-kiszolgálón." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Nem található érvényes konfigurációs fájl, ezért egyszerű alapértelmezés " +"lesznek használva" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Nem lehet betölteni témát." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Egy vagy több XML szintaktikai hiba található az Openbox konfigurációs fájl " +"feldolgozásakor. További információkért tekintse meg a szabványos kimenetet. " +"Az utolsó hiba ebben a fájlban volt: \"%s\" (%d sor). A hibaüzenet: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox szintaktikai hiba" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Az újraindÃtás során ez az új program nem volt indÃtható \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Használat: openbox [opciók]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opciók:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Súgó megjelenÃtése és kilépés\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Verzió kiÃrása és kilépés\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Jelenleg futó ablakkezelÅ‘ cseréje\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FÃJL A használandó konfigurációs fájl útvonalának megadása\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Ne csatlakozzon a munkamenet-kezelÅ‘höz\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Ãœzenet küldése a futó Openbox példánynak:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox beállÃtásának újratöltése\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox újraindÃtása\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Kilépés az Openboxból\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Hibakeresési opciók:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Futtatás szinkron módban\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Hibakeresési kimenet megjelenÃtése\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Fókuszkezelésre vonatkozó hibakeresési kimenetek " +"megjelenÃtése\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama KépernyÅ‘ felosztása két ál-xinerama képernyÅ‘re\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Kérjük a hibákat itt jelentse: %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s használatakor paraméter megadása kötelezÅ‘\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Érvénytelen parancssori opció: \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Már fut egy ablakkezelÅ‘ ezen a képernyÅ‘n: %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Nem lehet ablakkezelÅ‘t váltani ezen a képernyÅ‘n: %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Nem lép ki az ablakkezelÅ‘ ezen a képernyÅ‘n: %d" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Az Openbox %d munkaasztal használatára lett beállÃtva, de a jelenlegi " +"munkamenetnek %d van. FelülbÃráljuk az Openbox beállÃtását." +msgstr[1] "" +"Az Openbox %d munkaasztal használatára lett beállÃtva, de a jelenlegi " +"munkamenetnek %d van. FelülbÃráljuk az Openbox beállÃtását." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "%i. munkaasztal" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "%s futtatása" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Érvénytelen módosÃtó billentyű \"%s\" billentyű vagy egér hozzárendelésnél" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Érvénytelen billentyűkód \"%s\" billentyű hozzárendelésnél" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Érvénytelen billentyűnév \"%s\" billentyű hozzárendelésnél" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "A kért billentyű \"%s\" nem létezik a képernyÅ‘n" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Kilépés az Openboxból" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file használatakor paraméter megadása kötelezÅ‘\n" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Nem lehet menteni ide a futó munkamenetet \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Hiba a munkamenet mentése közben \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nincs kapcsolódva a munkamenet-kezelÅ‘höz" + +#~ msgid "X Error: %s" +#~ msgstr "X-hiba: %s" diff --git a/po/it.po b/po/it.po new file mode 100644 index 0000000..a8fce8c --- /dev/null +++ b/po/it.po @@ -0,0 +1,520 @@ +# Italian translation for Openbox +# Copyright (C) 2007-2009 Davide Truffa +# Copyright (C) 2008 Andrea Scarpino +# This file is distributed under the same license as the openbox package. +# Davide Truffa <davide@catoblepa.org>, 2007-2009. +# Andrea Scarpino <bash.lnx@gmail.com>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2009-02-25 11:29+0100\n" +"Last-Translator: Davide Truffa <davide@catoblepa.org>\n" +"Language-Team: Italian <tp@lists.linux.it>\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "L'operazione \"%s\" non è valida. L'operazione non esiste." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "No" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Si" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Esegui" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Impossibile convertire il percorso utf8 \"%s\"" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Annulla" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Esci" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Sei sicuro di voler uscire?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Esci" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Sei sicuro di voler uscire da Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Chiudi Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Finestra senza nome" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Termino..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Non Risponde" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"La finestra \"%s\" sembra non rispondere. Vuoi terminarne l'esecuzione " +"inviando il segnale %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Termina Processo" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"La finestra \"%s\" non sembra rispondere. Vuoi terminarne l'esecuzione " +"tramite il server X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Disconnesso" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Vai a..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Gestisci i desktop" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Aggiungi un nuovo desktop" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Rimuovi l'ultimo desktop" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Finestre" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Desktop" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Tutti i desktop" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Livello" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Sempre _sopra" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normale" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Sempre s_otto" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Invia al _desktop" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menù della finestra" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Ripristina" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Muovi" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "R_idimensiona" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizza" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ssimizza" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "A_rrotola" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Si/No _Decorazioni" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Chiudi" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Il contesto \"%s\" specificato nelle associazioni mouse non è valido" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Il pulsante \"%s\" specificato nel file di configurazione non è valido" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Impossibile creare la directory '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Chiudi" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "" +"Conflitto con la scorciatoia da tastiera specificata nel file di " +"configurazione" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Impossibile trovare il file di menù \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Impossibile eseguire il comando nel pipe-menù \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Output del pipe-menù \"%s\" non valido" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Tentativo di accedere al menù \"%s\". Il menù non esiste" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Altri..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Il pulsante \"%s\" specificato nelle associazioni mouse non è valido" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Impossibile accedere alla directory home \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Impossibile accedere al display specificato nella variabile DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Impossibile inizializzare la libreria obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Il server X non ha il supporto per la localizzazione." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"Impossibile impostare i tasti modificatori localizzati per il server X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Impossibile trovare un file di configurazione valido, verranno utilizzate le " +"impostazioni predefinite" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Impossibile caricare un tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Sono stati trovati uno o più errori nel file di configurazione di Openbox. " +"Vedi stdout per ulteriori informazioni. L'ultimo errore era in \"%s\" alla " +"linea %d, con il messaggio: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Errore di sintassi" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Non è stato possibile riavviare il nuovo eseguibile \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintassi: openbox [opzioni]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opzioni:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Mostra questo messaggio di aiuto ed esce\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Mostra il numero di versione ed esce\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Sostituisce l'attuale window manager attivo\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Specifica il percorso del file di configurazione\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Disabilita la connessione al session manager\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Inviare messaggi ad un'istanza di Openbox attiva:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Ricarica la configurazione di Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Riavvia Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Termina Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opzioni di debug:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Esegue in modalità sincrona\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Mostra le informazioni di debug\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Mostra le informazioni di debug sulla gestione del " +"focus\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Mostra le informazioni di debug sulla gestione del " +"session\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Divide lo schermo per simulare xinerama\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Segnalate eventuali bug a %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s richiede un argomento\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Argomento da linea di comando non valido \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Un window manager è già attivo sullo schermo %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Impossibile acquisire la selezione del window manager sullo schermo %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Il WM sullo schermo %d non è terminato" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox è configurato per %d desktop, ma la sessione attuale ne ha %d. " +"Ignoro la configurazione di Openbox." +msgstr[1] "" +"Openbox è configurato per %d desktop, ma la sessione attuale ne ha %d. " +"Ignoro la configurazione di Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "desktop %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Sto eseguendo %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Il nome del tasto \"%s\" specificato nelle associazioni di mouse/tastiera " +"non è valido" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "" +"Il codice tastiera \"%s\" specificato nelle associazioni di mouse/tastiera " +"non è valido" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "" +"Il nome del tasto \"%s\" specificato nelle associazioni di mouse/tastiera " +"non è valido" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Il tasto richiesto \"%s\" non esiste sul display" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Ok" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Chiudi Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file richiede un argomento\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "L'azione SessionLogout non è disponibile se Openbox è compilato senza il " +#~ "supporto del gestore delle sessioni." + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Impossibile salvare la sessione in \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Errore durante il salvataggio della sessione in \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Non connesso al gestore di sessioni" + +#~ msgid "X Error: %s" +#~ msgstr "Errore del server X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Impossibile eseguire il comando \"%s\": %s" diff --git a/po/ja.po b/po/ja.po new file mode 100644 index 0000000..132b887 --- /dev/null +++ b/po/ja.po @@ -0,0 +1,502 @@ +# Japanese messages for openbox. +# Copyright (C) 2004 Mikael Magnusson +# This file is distributed under the same license as the Openbox package. +# Yukihiro Nakai <nakai@gnome.gr.jp>, 2003. +# Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>, 2007, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-04 16:32+0100\n" +"Last-Translator: Ryoichiro Suzuki <ryoichiro.suzuki@gmail.com>\n" +"Language-Team: Japanese <ja@li.org>\n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "" +"ä¸æ£ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³\"%s\"ãŒè¦æ±‚ã•ã‚Œã¾ã—ãŸã€‚ãã®ã‚ˆã†ãªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯å˜åœ¨ã—ã¾ã›ã‚“。" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "ã„ã„ãˆ" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "ã¯ã„" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "実行ã™ã‚‹" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "パス\"%s\"ã‚’ utf8 ã‹ã‚‰å¤‰æ›ã™ã‚‹ã®ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "ã‚ャンセル" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "終了" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "ãƒã‚°ã‚¢ã‚¦ãƒˆ" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Openbox を終了ã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Openbox を終了ã™ã‚‹" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "å称未è¨å®š" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "強制終了ä¸..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "å¿œç”ãªã—" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"ウィンドウ \"%s\" ã¯å¿œç”ã—ã¦ã„ãªã„よã†ã§ã™ã€‚%s ä¿¡å·ã‚’é€ã‚Šå¼·åˆ¶çµ‚了ã—ã¾ã™ã‹ï¼Ÿ" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "プãƒã‚»ã‚¹ã‚’終了ã™ã‚‹" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "ウィンドウ \"%s\" ã¯å¿œç”ã—ã¦ã„ãªã„よã†ã§ã™ã€‚Xサーãƒã‹ã‚‰åˆ‡æ–ã—ã¾ã™ã‹ï¼Ÿ" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "切æ–ã™ã‚‹" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "移動ã™ã‚‹..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "デスクトップを管ç†" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "æ–°ã—ããƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã‚’è¿½åŠ (_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "最後ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—を削除(_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "ウィンドウ" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "デスクトップ" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "ã™ã¹ã¦ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—(_A)" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "階層(_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "常ã«æœ€ä¸Šå±¤ã«ã™ã‚‹(_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "通常(_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "常ã«æœ€ä¸‹å±¤ã«ã™ã‚‹(_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "デスクトップã«é€ã‚‹(_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "クライアントメニュー" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "復元(_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "移動(_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "サイズã®å¤‰æ›´(_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "最å°åŒ–(_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "最大化(_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "å·»ã上ã’/展開(_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "éž/装飾(_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "é–‰ã˜ã‚‹(_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "マウス割り当ã¦ã«æ–¼ã„ã¦ä¸æ£ãªã‚³ãƒ³ãƒ†ã‚¯ã‚¹ãƒˆ \"%s\"" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "ä¸æ£ãªãƒœã‚¿ãƒ³\"%s\"ãŒè¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã§æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "ディレクトリ'%s'を作れã¾ã›ã‚“: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "é–‰ã˜ã‚‹" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã«ã‚ー割り当ã¦ã®è¡çªãŒã‚ã‚Šã¾ã™ã€‚" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "æ£å½“ãªãƒ¡ãƒ‹ãƒ¥ãƒ¼ãƒ•ã‚¡ã‚¤ãƒ«\"%s\"を見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "パイプメニューã®ç‚ºã®ã‚³ãƒžãƒ³ãƒ‰\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "パイプメニュー\"%s\"ã‹ã‚‰ã®ä¸æ£ãªå‡ºåŠ›ã§ã™ã€‚" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "メニュー\"%s\"ã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã‚’試ã¿ã¾ã—ãŸãŒã€ãã‚Œã¯å˜åœ¨ã—ã¾ã›ã‚“。" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "ã‚‚ã£ã¨..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "マウス割り当ã¦ã«æ–¼ã„ã¦ä¸æ£ãªãƒœã‚¿ãƒ³ \"%s\"" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "ホームディレクトリ\"%s\"ã«ç§»å‹•ã§ãã¾ã›ã‚“: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "環境変数 DISPLAY ã‹ã‚‰ãƒ‡ã‚£ã‚¹ãƒ—レイを開ãã®ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "obrender ライブラリã®åˆæœŸåŒ–ã«å¤±æ•—ã—ã¾ã—ãŸã€‚" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Xサーãƒã¯ãƒã‚±ãƒ¼ãƒ«ã‚’サãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Xサーãƒã®ç‚ºã®ãƒã‚±ãƒ¼ãƒ«ä¿®é£¾åã‚’è¨å®šã§ãã¾ã›ã‚“。" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "æ£å½“ãªè¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’見ã¤ã‘られã¾ã›ã‚“。å˜ç´”ãªåˆæœŸè¨å®šã‚’使ã„ã¾ã™ã€‚" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "テーマをèªã¿è¾¼ã‚ã¾ã›ã‚“。" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Openbox ã®è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’解æžä¸ã« XML ã®æ§‹æ–‡ã‚¨ãƒ©ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚詳ã—ã„æƒ…å ±" +"ã¯æ¨™æº–出力を見ã¦ä¸‹ã•ã„。最後ã«è¦‹ã¤ã‹ã£ãŸã‚¨ãƒ©ãƒ¼ã¯\"%s\"ファイルã®%d 行目ã§ã€èª¬" +"明ã¯ã“ã†ã§ã™ï¼š%s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox 構文エラー" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "å†èµ·å‹•ã®éš›æ–°ã—ã„実行ファイル\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "著作権 (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "用法: openbox [オプション]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"オプション:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help ã“ã®ä½¿ã„方を表示ã—ã¦çµ‚了ã—ã¾ã™\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示ã—ã¦çµ‚了ã—ã¾ã™\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace ç¾åœ¨å®Ÿè¡Œä¸ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ã‚’ç½®ãæ›ãˆã¾ã™\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE 使用ã™ã‚‹è¨å®šãƒ•ã‚¡ã‚¤ãƒ«ã®ãƒ‘スを指定ã—ã¾ã™\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable セッションマãƒãƒ¼ã‚¸ãƒ£ã¸ã®æŽ¥ç¶šã‚’æ¢ã‚ã¾ã™\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"実行ä¸ã® Openbox ã«å‘½ä»¤ã‚’é€ã‚Šã¾ã™:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox ã®è¨å®šã‚’å†èªã¿è¾¼ã¿ã—ã¾ã™\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox ã‚’å†èµ·å‹•ã—ã¾ã™\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Openbox を終了ã—ã¾ã™\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"デãƒãƒƒã‚°ã‚ªãƒ—ション:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync åŒæœŸãƒ¢ãƒ¼ãƒ‰ã§å®Ÿè¡Œã—ã¾ã™\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug デãƒãƒƒã‚°æƒ…å ±ã‚’è¡¨ç¤ºã—ã¾ã™\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus フォーカスã®æ‰±ã„ã«é–¢ã™ã‚‹ãƒ‡ãƒãƒƒã‚°æƒ…å ±ã‚’è¡¨ç¤ºã—ã¾ã™\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session セッションã®æ‰±ã„ã«é–¢ã™ã‚‹ãƒ‡ãƒãƒƒã‚°æƒ…å ±ã‚’è¡¨ç¤ºã—ã¾ã™\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama å½ã® xinerama スクリーンã«åˆ†å‰²è¡¨ç¤ºã—ã¾ã™\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"ãƒã‚°ã¯ %s å®›ã¸å ±å‘Šã—ã¦ä¸‹ã•ã„\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requires an argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "ä¸æ£ãªã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³å¼•æ•° \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "スクリーン%dã§ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ãŒæ—¢ã«èµ·å‹•ã—ã¦ã„ã¾ã™ã€‚" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "スクリーン%dã§ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ãƒžãƒãƒ¼ã‚¸ãƒ£ã®é¸æŠžã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "スクリーン%dã®WMãŒçµ‚了ã—ã¾ã›ã‚“。" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox 㯠%d 個ã®ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã‚’è¨å®šã•ã‚Œã¾ã—ãŸãŒ, ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã¯ %d 個" +"æŒã£ã¦ã„ã¾ã™ã€‚ Openbox ã®è¨å®šã‚’無視ã—ã¾ã™ã€‚" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "デスクトップ%i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "èµ·å‹•ä¸ %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "ã‚ー/マウス割り当ã¦ã®ä¸ã®ä¸æ£ãªä¿®é£¾ã‚ー \"%s\"" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "ã‚ー割り当ã¦ã®ä¸ã®ä¸æ£ãªã‚ーコード \"%s\"" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "ã‚ー割り当ã¦ã®ä¸ã®ä¸æ£ãªã‚ーå称 \"%s\"" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "è¦æ±‚ã•ã‚ŒãŸã‚ー\"%s\"ã¯ãã®ãƒ‡ã‚£ã‚¹ãƒ—レイã«å˜åœ¨ã—ã¾ã›ã‚“。" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Openbox を終了ã™ã‚‹" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file requires an argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Openbox ãŒã‚»ãƒƒã‚·ãƒ§ãƒ³ç®¡ç†ã®æ©Ÿèƒ½ãªã—ã«ä½œã‚‰ã‚ŒãŸã®ã§ SessionLogout アクション" +#~ "ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "セッションを\"%s\"ã«ä¿å˜ã§ãã¾ã›ã‚“: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "セッションを\"%s\"ã«ä¿å˜ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒèµ·ãã¾ã—ãŸ: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "セッションマãƒãƒ¼ã‚¸ãƒ£ã«æŽ¥ç¶šã•ã‚Œã¦ã„ã¾ã›ã‚“。" + +#~ msgid "X Error: %s" +#~ msgstr "Xエラー: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "\"%s\"ã®å®Ÿè¡Œã«å¤±æ•—ã—ã¾ã—ãŸ: %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "アクション\"%s\"ã®ä¸æ£ãªä½¿ç”¨ã§ã™ã€‚ã“ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã¯ç„¡è¦–ã•ã‚Œã¾ã™ã€‚" diff --git a/po/lt.po b/po/lt.po new file mode 100644 index 0000000..289f220 --- /dev/null +++ b/po/lt.po @@ -0,0 +1,496 @@ +# Lithuanian messages for openbox +# Copyright (C) 2008 Dana Jansens +# This file is distributed under the same license as the openbox package. +# Vytautas (GODhack) <vytautas1987@yahoo.com>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-05-04 21:16+0200\n" +"Last-Translator: Vytautas <vytautas1987@yahoo.com>\n" +"Language-Team: Lithuanian <lt@li.ourg>\n" +"Language: lt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Neteisingo veiksmo \"%s\" pareikalauta. Toks veiksmas neegzistuoja." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ne" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Taip" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Vygdyti" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Nepavyko iÅ¡versti \"%s\" iÅ¡ utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "AtÅ¡aukti" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "IÅ¡eiti" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Ar tikrai norite iÅ¡siregistruoti?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "IÅ¡siregistruoti" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Ar tikrai norite iÅ¡jungti Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "IÅ¡jungti Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Bevardis Langas" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Naikinama..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Neatsako" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Langas \"%s\" neatsako. Ar nori jį priverstinai iÅ¡jungti nusiÅ«sdamas %s " +"signalÄ…?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Baigti procesÄ…" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Langas \"%s\" neatsako. Ar nori jį atjungti nuo X serverio?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Atsijungti" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Eiti ten..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Valdyti darbastalius" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_PridÄ—ti darbastalį" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "P_aÅ¡alinti paskutinį darbastalį" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Langai" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Darbastaliai" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Visi darbastaliai" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Sluoksnis" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Visada _virÅ¡uje" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normalus" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Visada _apaÄioje" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "SiÅ«sti į _darbastalį" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Kliento menu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Atstatyti" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Judinti" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Pakeisti dydį" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "_Mažinti" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "D_idinti" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Apvers_ti aukÅ¡tyn/žemyn" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Ne/D_ekoruoti" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Uždaryti" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Neteisingas kontekstas \"%s\" pelÄ—s nustatymuose" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Neteisingas mygtuskas \"%s\" nurodytas nustatymų byloje" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Negalima padaryti direktorijos '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Uždaryti" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konfliktas tarp mygtukų nustatymų byloje" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Nerasta gera menu byla \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Nepavyko pipe-menu komandos vygdyti \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Neteisinga iÅ¡vestis iÅ¡ pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Bandoma įeiti į menu \"%s\", bet jis neegzistuoja" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Dar..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Neteisingas mygtukas \"%s\" pelÄ—s nustatymuose" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Negalima patekti į home direktorijÄ… \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Nepavyko atidaryti ekrano pagal DISPLAY aplinkos kintamÄ…jį." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Nepavyko inicijuoti obrender bibliotekos." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X serveris nepalaiko lokalÄ—s." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Negalima nustatyti lokalÄ—s keitinių X serveriui." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Nepavyksta rasti geros nustatymų bylos, naudojami standartiniai nustatymai" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Nepavyksta įjunggti temos." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"XML sintaksÄ—s klaidos rastos Openbox nustatymų bylose. ŽiurÄ—kite stdout dÄ—l " +"daugiau informacijos. PaskutinÄ— klaida byloje \"%s\" eilutÄ—je %d, su " +"praneÅ¡imu: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox SintaksÄ—s Klaida" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Restartui nepavyko įjungti \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "SintaksÄ—: openbox [nustatymai]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Nustatymai:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Parodyti tai ir iÅ¡eiti\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Parodyti versijÄ… ir iÅ¡eiti\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Pakeisti jau veikiantį VM\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Nurodyti kur yra nustatymų byla\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Atsijungti nuo sesijos tvarkyklÄ—s\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Perduodamos žinutÄ—s į veikiantį Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Perkrauti Openbox nustatymus\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Perkrauti Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit IÅ¡eiti iÅ¡ Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Klaidų Å¡alinimo nustatymai:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Paleisti synchronous mode\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Rodyti debugging output\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Rodyti debugging output dÄ—l focus handling\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr " --debug-session Rodyti debugging output dÄ—l session management\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Skirstyt ekranÄ… į netikrus xinerama ekranus\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"PraneÅ¡kite apie vabaliukus %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s reikalauja argumento\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Blogas komandinÄ—s eilutÄ—s argumentas \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "WM jau veikia ekrane %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Nepavyksta gauti langų tvarkyklÄ—s pasirinkimo ekrane %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "WM ekrane %d neiÅ¡sijungia" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox yra suderintas %d darbastaliams, bet dabartinÄ— sesija turi %d. Mes " +"naudosime kitÄ…, ne Openbox, konfigÅ«racijÄ…." +msgstr[1] "" +"Openbox yra suderintas %d darbastaliams, bet dabartinÄ— sesija turi %d. Mes " +"naudosime kitÄ…, ne Openbox, konfigÅ«racijÄ…." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "darbastalis %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Veikia %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Neteisingas modifikavimų klaviÅ¡as \"%s\" klaviatÅ«ros/pelÄ—s priskyrimuose" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Neteisingas klaviÅ¡o kodas \"%s\" klavišų priskyrimuose" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Neteisingas klaviÅ¡o vardas \"%s\" klavišų priskyrimuose" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Norimas klaviÅ¡as \"%s\" neegzistuoja ekrane" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "IÅ¡jungti Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file reikalauja argumento\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout action negalimas, nes Openbox buvo pastatytas be sesijos " +#~ "valdymo palaikymo" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Negalima iÅ¡saugoti sesijos į \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Klaida bandant iÅ¡saugot sesijÄ… į \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Neprisijungta prie sesijų sesijos tvarkyklÄ—" + +#~ msgid "X Error: %s" +#~ msgstr "X Klaida: %s" diff --git a/po/lv.po b/po/lv.po new file mode 100644 index 0000000..6c434c2 --- /dev/null +++ b/po/lv.po @@ -0,0 +1,503 @@ +# Latvian translations for openbox. +# Copyright (C) 2010 Dana Jansens +# This file is distributed under the same license as the openbox package. +# +# Einars Sprugis <einars8@gmail.com>, 2010. +msgid "" +msgstr "" +"Project-Id-Version: 3.4.10\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-01-08 21:11+0200\n" +"Last-Translator: Einars Sprugis <einars8@gmail.com>\n" +"Language-Team: Latvian <locale@laka.lv>\n" +"Language: lv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " +"2);\n" +"X-Generator: Lokalize 1.0\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "PrasÄ«ta neatļauta darbÄ«ba \"%s\". Å Äda darbÄ«ba neeksistÄ“." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "NÄ“" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "JÄ" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "IzpildÄ«t" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "NeizdevÄs pÄrveidot ceļu \"%s\" no utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Atcelt" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Iziet" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Vai tieÅ¡Äm vÄ“laties atteikties?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Atteikties" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Vai tieÅ¡Äm vÄ“laties iziet no Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Iziet no Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Logs bez nosaukuma" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Nogalina..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Neatbild" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Logs \"%s\" neatbild. Vai vÄ“lieties to aizvÄ“rt piespiedu kÄrtÄ, nosÅ«tot " +"signÄlu %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Nobeigt procesu" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Logs \"%s\" neatbild. Vai vÄ“laties to atvienot no X servera?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Atvienot" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Iet uz turieni..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "PÄrvaldÄ«t darbvirsmas" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Pievienot j_aunu darbvirsmu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Noņemt pÄ“dÄ“jo da_rbvirsmu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Logi" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Darbvirsmas" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Visas darbvirsmas" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "S_lÄnis" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "VienmÄ“r augÅ¡Ä" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_NormÄls" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "VienmÄ“r a_pakÅ¡Ä" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "No_sÅ«tÄ«t uz darbvirsmu" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Klienta izvÄ“lne" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Atja_unot" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "PÄrviet_ot" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "MainÄ«t i_zmÄ“ru" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizÄ“t" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "MaksimizÄ“_t" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Sa_ritinÄt/AtritinÄt" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Bez/Ar _dekorÄcijÄm" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Ai_zvÄ“rt" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Neatļauts konteksts \"%s\" peles saÄ«snÄ“" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "KonfigurÄcijas failÄ \"%s\" norÄdÄ«ts neatļauts taustiņš" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "NevarÄ“ja izveidot mapi '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "AizvÄ“rt" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "KonfliktÄ“ ar tastatÅ«ras saÄ«snÄ“m konfigurÄcijas failÄ" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Nav atrasts atļauts izvÄ“lnes fails \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "NevarÄ“ja izpildÄ«t skriptÄ“tÄs izvÄ“lnes komandu \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Neatļauta izvade no skriptÄ“tÄs izvÄ“lnes \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "MÄ“Ä£inÄja piekļūt izvÄ“lnei \"%s\", bet tÄ neeksistÄ“" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "VairÄk..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Neatļauts taustiņš \"%s\" peles saÄ«snÄ“" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "NevarÄ“ja pÄriet uz mÄjas mapi \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "NeizdevÄs atvÄ“rt displeju no DISPLAY vides mainÄ«gÄ." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "NeizdevÄs inicializÄ“t obrender bibliotÄ“ku." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X serveris neatbalsta lokÄli." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Nevar uzstÄdÄ«t lokÄles modificÄ“tÄjus X serverim." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"NevarÄ“ja atrast atļautu konfigurÄcijas failu, tiek izmantoti noklusÄ“jumi" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Nebija iespÄ“jams ielÄdÄ“t tÄ“mu." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"AnalizÄ“jot Openbox konfigurÄcijas failus, tika atrastas viena vai vairÄkas " +"XML sintakses kļūdas. AplÅ«kojiet standarta izvadi, lai noskaidrotu vairÄk. " +"PÄ“dÄ“jÄ kļūda bija failÄ \"%s\" - %d rinda, kļūdas ziņojums: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox sintakses kļūda" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "PÄrstartÄ“tÄjam neizdevÄs palaist jauno izpildÄmo \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "AutortiesÄ«bas (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintakse: openbox [opcijas]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcijas:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help ParÄda Å¡o palÄ«dzÄ«bas tekstu un iziet\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version ParÄda versiju un iziet\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Aizvieto paÅ¡reiz palaisto logu pÄrvaldnieku\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FAILS NorÄda ceļu uz izmantojamo konfigurÄcijas failu\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable PÄrtrauc savienojumu ar sesiju pÄrvaldnieku\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Nodod ziņojumus esoÅ¡ai Openbox instancei:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure PÄrlÄdÄ“ Openbox konfigurÄcijas failus\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart PÄrstartÄ“ Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Iziet no Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"AtkļūdoÅ¡anas iespÄ“jas:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Palaist sinhronajÄ režīmÄ\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug RÄdÄ«t atkļūdoÅ¡anas izvadi\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus RÄdÄ«t atkļūdoÅ¡anas izvadi fokusÄ“Å¡anas darbÄ«bÄm\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama SadalÄ«t displeju vairÄkos viltus xinerama ekrÄnos\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"LÅ«dzu, ziņojiet kļūdas %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s vajadzÄ«gs arguments\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Neatļauts komandrindas arguments \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Logu pÄrvaldnieks jau palaists uz %d. ekrÄna" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "NevarÄ“ja iegÅ«t logu pÄrvaldnieka izvÄ“li uz %d. ekrÄna" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Logu pÄrvaldnieks uz %d. ekrÄna nebeidz darbÄ«bu" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox ir konfigurÄ“ts %d darbvirsmai, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. " +"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta." +msgstr[1] "" +"Openbox ir konfigurÄ“ts %d darbvirsmÄm, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. " +"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta." +msgstr[2] "" +"Openbox ir konfigurÄ“ts %d darbvirsmÄm, bet paÅ¡reizÄ“jai sesijai tÄdu ir %d. " +"Å Ä« Openbox konfigurÄcijas opcija tiks ignorÄ“ta." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "darbvirsma %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Palaiž %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Neatļauts modificÄ“tÄjtaustiņš \"%s\" tastatÅ«ras/peles saÄ«snÄ“" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Neatļauts taustiņa kods \"%s\" tastatÅ«ras saÄ«snÄ“" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Neatļauts taustiņa nosaukums \"%s\" tastatÅ«ras saÄ«snÄ“" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "PieprasÄ«tais taustiņš \"%s\" uz displeja neeksistÄ“" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Labi" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Iziet no Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file vajadzÄ«gs arguments\n" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "NeizdevÄs saglabÄt sesiju \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Kļūda saglabÄjot sesiju \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nav savienots ar sesiju pÄrvaldnieku" + +#~ msgid "X Error: %s" +#~ msgstr "X kļūda: %s" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "DarbÄ«ba 'SessionLogout' nav pieejama, jo Openbox ir kompilÄ“ts bez sesiju " +#~ "pÄrvaldÄ«bas atbalsta" diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..b869348 --- /dev/null +++ b/po/nl.po @@ -0,0 +1,504 @@ +# Dutch translations for openbox. +# This file is distributed under the same license as the openbox package. +# +# Mark Pustjens <pustjens@dds.nl>, 2007. +# Jochem Kossen <jkossen@xs4all.nl>, 2007. +# Marvin Vek <laen@onedot.nl>, 2008. +# Benno Schulenberg <benno@vertaalt.nl>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-18 23:30+0100\n" +"Last-Translator: Benno Schulenberg <benno@vertaalt.nl>\n" +"Language-Team: Dutch <vertaling@vrijschrift.org>\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Ongeldige actie \"%s\" werd gevraagd. Deze actie bestaat niet." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nee" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ja" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Uitvoeren" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Converteren van het pad \"%s\" vanuit UTF-8 is mislukt" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Annuleren" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Afsluiten" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Weet u zeker dat u wilt uitloggen?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Uitloggen" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Weet u zeker dat u Openbox wilt afsluiten?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Openbox afsluiten" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Naamloos venster" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Termineren..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Reageert niet" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Het venster \"%s\" reageert niet. Wilt u het afsluiten forceren door het " +"signaal %s te sturen?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Beëindig proces" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Het venster \"%s\" reageert niet. Wilt u de verbinding van het venster met " +"de X-server verbreken?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Verbreek verbinding" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Ga hierheen..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Bureaubladen beheren" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Voeg nieuw bureaublad toe" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "V_erwijder laatste bureaublad" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Vensters" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Bureaubladen" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Alle bureaubladen" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Laag" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Altijd _bovenop" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normaal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Altijd _onderop" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Verplaats _naar bureaublad" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Venstermenu" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Herstellen" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Verplaatsen" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Grootte aanpassen" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "_Iconificeren" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "_Maximaliseren" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Op/neerklappen" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "_Vensterrand weghalen/toevoegen" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Sluiten" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Ongeldige context \"%s\" in muisbinding" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ongeldige knop \"%s\" opgegeven in het configuratiebestand" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Kan map '%s' niet aanmaken: %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Sluiten" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflict in toetsbindingen in het configuratiebestand" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Kan geen geldig menubestand \"%s\" vinden" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Uitvoeren van commando voor pipe-menu \"%s\" is mislukt: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ongeldige uitvoer van pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Toegang tot niet-bestaand menu \"%s\" werd gevraagd" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Meer..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Ongeldige knop \"%s\" in muisbinding" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Kan thuismap \"%s\" niet de huidige map maken: %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Kan scherm genoemd in omgevingsvariabele DISPLAY niet openen." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Initialiseren van de 'obrender'-bibliotheek is mislukt." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-server ondersteunt lokalisatie niet." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Kan lokalisatie voor de X-server niet instellen." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Kan geen geldig configuratiebestand vinden; simpele standaardinstellingen " +"worden gebruikt" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Kan geen thema laden." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Een of meer XML-syntaxfouten zijn gevonden tijdens het inlezen van de " +"Openbox-configuratiebestanden. Zie standaarduitvoer voor meer informatie. " +"De laatste fout werd gevonden in bestand \"%s\" regel %d, met bericht: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox-syntaxfout" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Uitvoeren van nieuw programma \"%s\" bij herstart is mislukt: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Gebruik: openbox [opties]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opties:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Deze hulptekst tonen en stoppen\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Versie tonen en stoppen\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace De draaiende vensterbeheerder vervangen\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file BESTAND\n" +" Dit configuratiebestand gebruiken\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Verbinding met de sessiebeheerder uitschakelen\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Berichten worden naar een draaiende Openbox-instantie gestuurd:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox-configuratie opnieuw laden\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox herstarten\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Openbox afsluiten\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debugging-opties:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync In synchrone modus starten\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Debuguitvoer weergeven\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Debuguitvoer voor focusafhandeling weergeven\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Het scherm in nep Xinerama-schermen splitsen\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Rapporteer programmafouten aan %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s vereist een argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Onbekende optie \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Er draait al een vensterbeheerder op scherm %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Kan op scherm %d geen vensterbeheerderselectie verkrijgen" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "De vensterbeheerder op scherm %d sluit zichzelf niet af" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox is geconfigureerd voor %d bureaublad, maar de huidige sessie heeft " +"er %d. De Openbox-instelling wordt genegeerd." +msgstr[1] "" +"Openbox is geconfigureerd voor %d bureaubladen, maar de huidige sessie heeft " +"er %d. De Openbox-instelling wordt genegeerd." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "bureaublad %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Starten van %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ongeldige modificatietoets \"%s\" in toetsen-/muisbinding" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ongeldige toetscode \"%s\" in toetsenbinding" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ongeldige toetsnaam \"%s\" in toetsenbinding" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Gevraagde toets \"%s\" bestaat niet op het scherm" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Openbox afsluiten" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "'--config-file' vereist een argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "De actie 'SessionLogout' is niet beschikbaar omdat Openbox gecompileerd " +#~ "is zonder ondersteuning voor sessiebeheer." + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Kan de sessie niet opslaan in \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Fout tijdens het opslaan van de sessie in \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Niet met een sessiebeheerder verbonden" + +#~ msgid "X Error: %s" +#~ msgstr "X-fout: %s" diff --git a/po/no.po b/po/no.po new file mode 100644 index 0000000..0d01353 --- /dev/null +++ b/po/no.po @@ -0,0 +1,506 @@ +# Norwegian messages for openbox +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the openbox package. +# +# Michael Kjelbergvik Thung <postlogic@gmail.com>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-13 13:37+0100\n" +"Last-Translator: Michael Kjelbergvik Thung <postlogic@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Ugyldig operasjon \"%s\" etterspurt. Operasjonen finnes ikke." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nei" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ja" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Utfør" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Feil ved konvertering av \"%s\" fra utf8 " + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Avbryt" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Avslutt" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Er du sikker pÃ¥ at du vil logge ut?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Logg Ut" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Er du sikker pÃ¥ at du vil avslutte Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Avslutt Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Ukjent Vindu" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Dreper..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Svarer Ikke" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Vinduet \"%s\" svarer ikke. Vil du utføre tvunget avslutning ved Ã¥ sende " +"signalet %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Avslutt Prosess" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Vinduet \"%s\" svarer ikke. Vil du fjerne tilknytning av vinduet til X-" +"serveren?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Fjern tilknytning" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "GÃ¥ dit..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Behandle skrivebord" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Nytt skrivebord" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Fjern siste skrivebord" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Vinduer" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Skrivebord" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Alle skrivebord" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "La_g" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Alltid ø_verst" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "Nor_mal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Alltid _nederst" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Send til" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Klient-meny" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Tilbak_estill" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Flytt" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Endre s_tørrelse" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "_Minimer" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximer" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Rull opp/ned" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Fjern/Legg til _dekorasjon" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Lukk" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Ugyldig innhold \"%s\" i binding for mus" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ugyldig tast \"%s\" spesifisert i konfigurasjonsfilen" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Kan ikke lage katalog '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Lukk" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt med hurtigtastbinding i konfigurasjonsfilen" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Kan ikke finne en gyldig menyfil \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Kunne ikke kjøre kommando for pipe-meny \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ugyldig utdata fra pipe-menyen \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Forsøkte Ã¥ Ã¥pne menyen \"%s\", men denne finnes ikke" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mer..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Ugyldig knapp \"%s\" i binding for mus" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Kan ikke endre til hjemmekatalogen \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Kunne ikke Ã¥pne displayet fra DISPLAY-miljøvariabelen" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Kunne ikke starte obrender-biblioteket." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-serveren støtter ikke lokalisering." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Kan ikke stille inn lokaliseringsmodifikatorene for X-serveren." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "Kunne ikke finne en gyldig konfigurasjonsfil, bruker standardverdier" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Kan ikke laste et tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"En eller flere XML-syntaksfeil ble funnet ved lesing av konfigurasjonsfilene " +"til Openbox. Se stdout for mer informasjon. Forrige feil funnet var i filen " +"\"%s\", linje %d, med beskjeden: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Syntaksfeil" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Kunne ikke starte nytt program ved omstart: \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [alternativer\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Alternativ:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Vise denne hjelpeteksten og avslutt\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Vis versjonsnummeret og avslutt\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Erstatt den kjørende vindusbehandleren\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FIL Spesifisér filbane til konfigurasjonsfilen du vil ta i " +"bruk\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Deaktiver tilknytning til sesjonsbehandleren\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Sender beskjeder til en kjørende Openbox-instans:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Oppdater Openbox' konfigurasjon\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Start Openbox pÃ¥ nytt\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Avslutt Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debug-alternativ:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Kjør i synkron-modus\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Vis debuggingsinformasjon\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Vis debuggingsinformasjon for fokus-hÃ¥ndtering\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Vis debuggingsinformasjon for session-hÃ¥ndtering\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " -debug-xinerama Splitt displayet for falske xinerama-skjermer\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Vennligst rapporter bugs til %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s krever et argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ugyldig kommandolinje-argument \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "En vindusbehandler kjører allerede pÃ¥ skjerm %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Kunne ikke hendte vindusbehandlerens markering pÃ¥ skjerm %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Vindusbehandleren pÃ¥ skjerm %d vil ikke avslutte" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Aktiv sesjon har %2$d skrivebord, mens Openbox er konfigurert til %1$d. " +"Benytter innstillingene for den aktive sesjonen." +msgstr[1] "" +"Aktiv sesjon har %2$d skrivebord, mens Openbox er konfigurert til %1$d. " +"Benytter innstillingene for den aktive sesjonen." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "skrivebord %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Kjører %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ugyldig modifikasjonsknapp \"%s\" i binding for tast/mus" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ugyldig tastekode \"%s\" i hurtigtastbinding" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ugyldig tastenavn \"%s\" i hurtigtastbinding" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Ønsket tast \"%s\" eksisterer ikke i displayet" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Avslutt Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file krever et argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout er ikke tilgjengelig fordi Openbox ble kompilert uten " +#~ "støtte for sesjonsbehandling" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Kan ikke lagre sesjon til \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Feil ved lagring av sesjon til \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ikke tilknyttet en sesjonsbehandler" + +#~ msgid "X Error: %s" +#~ msgstr "Feil i X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Kunne ikke kjøre \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Ugyldig bruk av aksjonen \"%s\". Aksjonen vil bli ignorert." diff --git a/po/openbox.pot b/po/openbox.pot new file mode 100644 index 0000000..785d49e --- /dev/null +++ b/po/openbox.pot @@ -0,0 +1,453 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Dana Jansens +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "" + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "" + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "" + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr "" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr "" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr "" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr "" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr "" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr "" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr "" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +msgstr[1] "" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "" diff --git a/po/pl.po b/po/pl.po new file mode 100644 index 0000000..794d0f5 --- /dev/null +++ b/po/pl.po @@ -0,0 +1,496 @@ +# Polish translation of Openbox 3. +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the Openbox 3 package. +# Madej <madej@afn.no-ip.org>, 2004. +# PaweÅ‚ Rusinek <p.rusinek@gmail.com>, 2007. +# Piotr DrÄ…g <raven@pmail.pl>, 2007. +# Jakub Åojewski <lojewski@ovi.com>, 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.3\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-03-11 13:55+0100\n" +"Last-Translator: Jakub Åojewski <lojewski@ovi.com>\n" +"Language-Team: Polish <pl@li.org>\n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "WywoÅ‚ana akcja \"%s\" nie istnieje." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nie" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Tak" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Wykonaj" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Nie można przekonwertować Å›cieżki \"%s\" z UTF-8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Anuluj" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "WyjÅ›cie" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Czy na pewno chcesz siÄ™ wylogować?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Wyloguj" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Czy na pewno chcesz opuÅ›cić Openboksa?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Opuść Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Okno bez nazwy" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "KoÅ„czenie..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Nie odpowiada" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Okno \"%s\" nie odpowiada. Czy wymusić zakoÅ„czenie poprzez wysÅ‚anie sygnaÅ‚u " +"%s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "ZakoÅ„cz proces" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Okno \"%s\" nie odpowiada. OdÅ‚Ä…czyć je od serwera X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "OdÅ‚Ä…cz" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Przejdź..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "ZarzÄ…dzaj pulpitami" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Dod_aj nowy pulpit" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_UsuÅ„ ostatni pulpit" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Okna" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Pulpity" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Wszystkie pulpity" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Warstwa" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Zawsze na _wierzchu" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normalnie" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Zawsze pod _spodem" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "WyÅ›lij na p_ulpit" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu klienta" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "P_rzywróć" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_PrzesuÅ„" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "ZmieÅ„ _rozmiar" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Zmi_nimalizuj" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Zma_ksymalizuj" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_ZwiÅ„/RozwiÅ„" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "WyÅ›wietl/ukryj _dekoracje" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Z_amknij" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "NieprawidÅ‚owy kontekst \"%s\" w skrócie myszy" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "NieprawidÅ‚owy klawisz \"%s\" okreÅ›lony w pliku konfiguracyjnym" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Nie można utworzyć katalogu '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Zamknij" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt skrótów klawiszowych w pliku konfiguracyjnym" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Nie można znaleźć prawidÅ‚owego pliku menu \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Wykonanie polecenia dla pipe-menu \"%s\" nie powiodÅ‚o siÄ™: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "NieprawidÅ‚owe wyjÅ›cie z pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Spróbowano uzyskać dostÄ™p do menu \"%s\", ale ono nie istnieje" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "WiÄ™cej..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "NieprawidÅ‚owy klawisz \"%s\" w skrócie myszy" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Nie można przejść do katalogu domowego \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Otwarcie ekranu ze zmiennej Å›rodowiskowej DISPLAY nie powiodÅ‚o siÄ™." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Zainicjowanie biblioteki obrender nie powiodÅ‚o siÄ™." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Serwer X nie obsÅ‚uguje ustawieÅ„ lokalnych." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Nie można ustawić modyfikatorów lokalnych dla serwera X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Nie można znaleźć prawidÅ‚owego pliku konfiguracyjnego, używanie " +"domyÅ›lnychwartoÅ›ci" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Nie można wczytać motywu." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Jeden lub wiÄ™cej bÅ‚Ä™dów skÅ‚adniowych XML zostaÅ‚o znalezionych podczas " +"sprawdzania plików konfiguracyjnych. Zobacz stdout aby uzyskać wiÄ™cej " +"informacji. Ostatnio bÅ‚Ä…d znaleziono w pliku \"%s\" linia %d, z wiadomoÅ›ciÄ…: " +"%s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "BÅ‚Ä…d skÅ‚adniowy Openboksa" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"Wykonanie nowego pliku wykonywalnego \"%s\" podczas ponownego " +"uruchomienianie powiodÅ‚o siÄ™: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "SkÅ‚adnia: openbox [opcje]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcje:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help WyÅ›wietla tÄ™ pomoc i koÅ„czy\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version WyÅ›wietla wersjÄ™ i koÅ„czy\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace ZastÄ™puje aktualnie dziaÅ‚ajÄ…cy menedżer okien\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Podaj Å›cieżkÄ™ do pliku konfiguracji\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Nie tworzy poÅ‚Ä…czenia z menedżerem sesji\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Przekazywanie komunikatów do dziaÅ‚ajÄ…cej instancji Openboksa:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Ponownie wczytuje pliki konfiguracyjne\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Ponownie uruchamia Openboksa\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Opuść Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opcje debugowania:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Uruchamia w trybie synchronicznym\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug WyÅ›wietla informacje o debugowaniu\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus WyÅ›wietla wyjÅ›cie debugowania obsÅ‚ugi aktywacji\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Dzieli ekran na sztuczne ekrany xineramy\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"ProszÄ™ zgÅ‚aszać bÅ‚Ä™dy (w jÄ™zyku angielskim) pod adresem %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s wymaga argumentu\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "NieprawidÅ‚owy argument wiersza poleceÅ„ \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Menedżer okien jest już uruchomiony na ekranie %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Nie można uzyskać wyboru menedżera okien na ekranie %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Menedżer okien na ekranie %d nie koÅ„czy dziaÅ‚ania" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +msgstr[1] "" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "pulpit %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Uruchamianie %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"NieprawidÅ‚owy klawisz modyfikatora \"%s\" w skrócie klawiszowym lub myszy" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "NieprawidÅ‚owy kod \"%s\" w skrócie klawiszowym" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "NieprawidÅ‚owa nazwa \"%s\" w skrócie klawiszowym" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Żądany klawisz \"%s\" nie istnieje na ekranie" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Nie można zapisać sesji do \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "WystÄ…piÅ‚ bÅ‚Ä…d podczas zapisywania sesji do \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nie podÅ‚Ä…czono do menedżera sesji" + +#~ msgid "X Error: %s" +#~ msgstr "BÅ‚Ä…d X: %s" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout jest niedostÄ™pne, ponieważ Openbox zostaÅ‚ stworzony bez " +#~ "wsparcia dla zarzÄ…dzania sesjÄ…" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Wykonanie \"%s\" nie powiodÅ‚o siÄ™: %s" diff --git a/po/pt.po b/po/pt.po new file mode 100644 index 0000000..390963f --- /dev/null +++ b/po/pt.po @@ -0,0 +1,503 @@ +# Portuguese messages for openbox +# Copyright (C) 2010 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Gonçalo Ferreira <gonsas@gmail.com>, 2006. +# Pedro Beja <althaser@gmail.com>, 2007 2008 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.11.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2010-05-14 15:51+0100\n" +"Last-Translator: Pedro Beja <althaser@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Pedido de acção \"%s\" inválido. Não existem quaisquer acções." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Não" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Sim" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Executar" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Falha a converter o caminho \"%s\" do utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancelar" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Sair" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Tem a certeza que pretende fazer log out?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Log Out" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Tem a certeza que pretende sair do Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Sair do Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Janela sem nome" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Terminando..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Não está a responder" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"A janela \"%s\" parece não estar a responder. Quer forçá-la a sair enviando " +"o sinal %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Terminar Processo" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"A janela \"%s\" parece não estar a responder. Quer desligá-la do servidor X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Desligar" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Ir para..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Gerir áreas de trabalho" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Adicionar nova área de trabalho" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Remover última área de trabalho" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Janelas" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Ãreas de trabalho" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Todas as áreas de trabalho" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Camada" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Sempre no _topo" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Sempre no _fundo" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Enviar para área de _trabalho" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu cliente" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "R_estaurar" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Mover" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Redimen_sionar" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizar" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximizar" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Des/en_rolar" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Des/_Decorar" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Fechar" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Contexto inválido \"%s\" no atalho do rato" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Botão inválido \"%s\" especificado no ficheiro de configuração" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Incapaz de criar o directório '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Fechar" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflito com tecla de atalho no ficheiro de configuração" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Incapaz de encontrar um ficheiro de menu válido \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Falha a executar comando para o menu de processamento \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Resultado inválido do menu de processamento \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Tentou aceder ao menu \"%s\" mas ele não existe" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mais..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Botão inválido \"%s\" no atalho do rato" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Incapaz de mudar para o directório home \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Falha ao abrir o ecrã da variável de ambiente DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Falha ao inicializar a biblioteca obrender" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "O servidor X não suporta o locale." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Não é possÃvel configurar modificadores de locale para o servidor X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Incapaz de encontrar um ficheiro de configuração válido, usando alguns " +"valores simples de omissão" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Incapaz de carregar o tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Um ou mais erros de sintaxe do XML foram encontrados enquanto analizava os " +"ficheiros de configuração do Openbox. Veja o stdout para mais informações. " +"O último erro visto foi no ficheiro \"%s\" linha %d, com a mensagem: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Erro de Sintaxe do Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Falha a reiniciar a execução de um novo executável \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Direitos de autor (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaxe: openbox [opções]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opções:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Mostra esta ajuda e sai\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Mostra a versão e sai\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Substitui o corrente gestor de janelas\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file ficheiro\n" +" Especifica o caminho do ficheiro de configuração a " +"usar\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Desactiva a ligação com o gestor de sessões\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Passando mensagens para uma instância do Openbox em execução:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Recarrega a configuração do Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Reinicia o Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --sair Sai do Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opções de depuração:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Executa em modo sincronizado\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Mostra o resultado da depuração\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Mostra o resultado de depuração para manipulação de " +"foco\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Divide o ecrã em falsos ecrãs xinerama\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Por favor reporte os erros em %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requer um argumento\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Argumento inválido na linha de comandos \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Um gestor de janelas já está em execução no ecrã %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Não consegui adequirir o gestor de janelas selecionado no ecrã %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "O gestor de janelas no ecrã %d não está a fechar" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"O Openbox está configurado para %d área de trabalho, mas a sessão corrente " +"tem %d. Sobrescrevendo a configuração do Openbox." +msgstr[1] "" +"O Openbox está configurado para %d áreas de trabalho, mas a sessão corrente " +"tem %d. Sobrescrevendo a configuração do Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "área de trabalho %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Executando %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Chave modificadora inválida \"%s\" no atalho de tecla/rato" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Código chave inválido \"%s\" na tecla de atalho" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Nome de chave inválido \"%s\" na tecla de atalho" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Chave requerida \"%s\" não existe no ecrã" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Incapaz de guardar a sessão em \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Erro enquanto guardo a sessão em \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Não está ligado a um gestor de sessões" + +#~ msgid "X Error: %s" +#~ msgstr "Erro no X: %s" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "A acção SessionLogout não está disponÃvel visto que o Openbox foi " +#~ "construÃdo sem suporte à gestão de sessões" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Falha a executar \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Uso inválido da acção \"%s\". A acção será ignorada." diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100644 index 0000000..b0c2294 --- /dev/null +++ b/po/pt_BR.po @@ -0,0 +1,517 @@ +# Portuguese Brazil (pt_BR) messages for openbox +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# crimeboy, 2007. +# Og Maciel <ogmaciel@gnome.org>, 2007, 2008, 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.5.0\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2011-08-01 15:26-0400\n" +"Last-Translator: Og Maciel <ogmaciel@gnome.org>\n" +"Language-Team: Brazilian Portuguese <gnome-l10n-br@listas.cipsga.org.br>\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Ação inválida \"%s\" requisitada. Ação não existe." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Não" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Sim" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Executar" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Falha ao converter o caminho \"%s\" do utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Cancelar" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Sair" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Você tem certeza que deseja sair?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Sair" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Você tem certeza que deseja sair do Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Sair do Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Janela sem nome" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Terminando..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Não Responsivo" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"A janela \"%s\" não está responsiva. Você deseja forçá-la a sair enviando o " +"sinal %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Terminar Processo" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"A janela \"%s\" não está responsiva. Você deseja desconectá-la do servidor X?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Desconectar" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Ir lá..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Gerenciar áreas de trabalho" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Adicionar nova área de trabalho" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Remover última área de trabalho" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Janelas" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Ãreas de trabalho" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Todas as áreas de trabalho" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Camada" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Sempre no _topo" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Sempre no _fundo" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Enviar para área de _trabalho" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu do cliente" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "R_estaurar" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Mover" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Redimen_sionar" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimizar" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximizar" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "(Des)en_rolar" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "(Não) _Decorar" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Fechar" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Contexto \"%s\" inválido na associação do mouse" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Botão inválido \"%s\" especificado no arquivo de configuração" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" +"Openbox foi compilado sem suporte para carregamento de imagens Imlib2. " +"Ãcones em menus não serão carregados." + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Não foi possÃvel criar o diretório '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Fechar" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Conflito com associação de chave no arquivo de configuração" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Não foi possÃvel encontrar um arquivo de menu \"%s\" válido" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Falha ao executar comando para menu de processamento \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "SaÃda inválida do menu de processamento \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Tentou acessar menu \"%s\" mas ele não existe" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mais.." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Botão inválido \"%s\" na associação do mouse" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Não foi possÃvel mudar para o diretório pessoal \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Falha ao abrir a tela da variavel de ambiente DISPLAY" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Falha ao iniciar a biblioteca obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Servidor X não suporta localização." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "" +"Não foi possÃvel configurar modificadores de localização para o servidor X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Não foi possÃvel encontrar um arquivo de configuração válido, usando alguns " +"valores padrão simples." + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Não foi possÃvel carregar um tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Um ou mais erros de sintaxe de XML foram encontrados ao analisar os arquivos " +"de configuração do Openbox. Veja a saÃda padrão para mais informação. O " +"último erro relatado foi no arquivo \"%s\" linha %d, com a mensagem: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Erro de Sintaxe do Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "O comando de reiniciar falhou ao executar novo executável \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaxe: openbox [opções]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opções:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Mostra esta ajuda e sai\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Mostra a versão e sai\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Substitui o gerenciador de janelas ativo\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file ARQUIVO\n" +" Especifica o caminho do arquivo de configuração para " +"usar\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" +" --sm-disable Desabilita conexão com o gerenciador de sessões\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Passando mensagens para uma instância do Openbox em execução:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Recarrega a configuração do Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Reinicia o Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Sai do Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opções de depuração:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Executa em modo sincronizado\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr " --startup CMD Executa CMD depois de iniciar\n" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Mostra saida de depuração\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Mostra saÃda de depuração para manipulação de foco\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Mostra saÃda de depuração do gerenciamento da sessão\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr "" +" --debug-xinerama Divide a exibição de telas em telas de xinerama " +"falsas\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Por favor reporte erros em %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s requere um argumento\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Argumento de linha de comando inválido \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Um gerenciador de janelas já está em execução na tela %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "" +"Não foi possÃvel adquirir a seleção do gerenciador de janelas na tela %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "O gerenciador de janelas na tela %d não está saindo" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"O Openbox está configurado para %d área de trabalho, mas a sessão atual " +"contém %d. Sobrescrevendo a configuração do Openbox." +msgstr[1] "" +"O Openbox está configurado para %d áreas de trabalho, mas a sessão atual " +"contém %d. Sobrescrevendo a configuração do Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "área de trabalho %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Executando %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Chave modificadora \"%s\" inválida na associação de tecla/mouse" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Código chave \"%s\" inválido na associação de chave" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Nome de chave \"%s\" inválido na associação de chave" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Chave requerida \"%s\" não existe na tela" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Sair do Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file requere um argumento\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "A ação SessionLogout não está disponÃvel já que o Openbox foi compilado " +#~ "sem suporte de gerenciamento de sessões" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Não foi possÃvel salvar a sessão em \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Erro enquanto salvando a sessão em \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Não está conectado à um gerente de sessões" + +#~ msgid "X Error: %s" +#~ msgstr "Erro no X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Falha ao executar \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Uso inválido da ação \"%s\". Ação será ignorada." diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..8fa7e1e --- /dev/null +++ b/po/ru.po @@ -0,0 +1,501 @@ +# Russian translation of Openbox +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Alexey Remizov <alexey@remizov.pp.ru>, 2004. +# Nikita Bukhvostov <dragon.djanic@gmail.com>, 2007. +# Moroz Sergey L. <se.seam@gmail.com>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-05-02 10:25+0200\n" +"Last-Translator: Moroz Sergey L. <se.seam@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Запрошено неверное дейÑтвие \"%s\". Такого дейÑÑ‚Ð²Ð¸Ñ Ð½ÐµÑ‚." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ðет" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Да" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "ЗапуÑтить" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "ÐÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ \"%s\" из utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Отменить" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Выйти" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Ð’Ñ‹ дейÑтвительно хотите выйти?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Выход" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Ð’Ñ‹ дейÑтвительно хотите выйти из Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Выйти из Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "БезымÑнное окно" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Завершение..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ðет ответа" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Похоже, окно \"%s\" не отвечает. Хотите принудительно поÑлать Ñигнал выхода " +"%s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Закончить процеÑÑ" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Похоже, окно \"%s\" не отвечает. Хотите отключить его от Ð¥-Ñервера?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Отключить" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Перейти..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Управление рабочими Ñтолами" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Добавить новый рабочий Ñтол(_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Удалить поÑледний рабочий Ñтол(_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Окна" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Рабочие Ñтолы" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Ð’Ñе рабочие Ñтолы" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Положение(_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Ð’Ñегда наверху(_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "Обычное(_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Ð’Ñегда внизу(_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Отправить на рабочий Ñтол(_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "КлиентÑкое меню" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "ВоÑÑтановить(_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "ПеремеÑтить(_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Изменить размер(_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Свернуть в значок(_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "РаÑпахнуть(_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "РаÑ/Ñкрутить(_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "РаÑ/Декорировать(_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Закрыть(_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÑвÑзь \"%s\" в комбинации мыши" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ð’ файле конфигурации определена Ð½ÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° \"%s\"" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ðевозможно Ñоздать директорию '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Закрыть" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Конфликтует Ñ ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸ÐµÐ¹ клавиш из файла конфигурации" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Ðевозможно найти ÑоответÑтвующий файл меню \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ðеудачное выполнение команды Ð´Ð»Ñ Ð¼ÐµÐ½ÑŽ канала \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ðеверный выход меню канала \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Попытка доÑтупа к меню \"%s\", которого не ÑущеÑтвует" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Еще..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° \"%s\" в комбинации мыши" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ðевозможно изменить на домашнюю директорию \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Ðевозможно открыть Ñкран из переменной Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Ðевозможно запуÑтить библиотеку obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X Ñервер не поддерживает локаль." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ðевозможно уÑтановить модификаторы локали Ð´Ð»Ñ X Ñервера." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ðевозможно найти правильный файл наÑтройки, иÑпользуетÑÑ Ð¿Ñ€Ð¾Ñтой по " +"умолчанию." + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ðевозможно загрузить тему." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"При обработке файлов конфигурации Openbox найдена одна или более " +"ÑинтакÑичеÑких ошибок XML. Подробную информацию проÑмотрите в выводе " +"stdout. ПоÑледнÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° замечена в файле \"%s\" Ñтроке %d, Ñ Ñообщением: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Ошибка ÑинтакÑиÑа Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "При перезапуÑке не удалоÑÑŒ выполнить новую команду \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "СинтакÑиÑ: openbox [options]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Опции:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Показать Ñту Ñправку и выйти\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Показать верÑию и выйти\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Заменить текущий запущенный менеджер окон\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Указать путь к иÑпользуемому файлу наÑтройки\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Разорвать Ñоединение Ñ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑ€Ð¾Ð¼ ÑеÑÑии\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Отправка Ñообщений запущенному ÑкземплÑру Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Перезагрузить конфигурацию Openbox\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart ПерезапуÑтить Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Выйти из Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"ÐаÑтройки отладки:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync ЗапуÑтить в режиме Ñинхронизации\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Показать вывод отладки\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Показать вывод отладки Ð´Ð»Ñ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð½Ð¾Ð³Ð¾ фокуÑом\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Разделить диÑплей на фальшивые Ñкраны xinerama\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"ПожалуйÑта, Ñообщайте об ошибках на %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ðеверный аргумент командной Ñтроки \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Оконный менеджер уже запущен на Ñкране %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Ðевозможно получить выбранный менеджер окон на Ñкране %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Менеджер окон на Ñкране %d еще запущен" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox Ñконфигурирован Ð´Ð»Ñ %d рабочих Ñтолов, а в текущей ÑеÑÑии имеетÑÑ " +"%d. Изменены наÑтройки Openbox." +msgstr[1] "" +"Openbox Ñконфигурирован Ð´Ð»Ñ %d рабочих Ñтолов, а в текущей ÑеÑÑии имеетÑÑ " +"%d. Изменены наÑтройки Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "рабочий Ñтол %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "ЗапуÑк %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ðеверный модификатор \"%s\" в комбинации клавиши/мыши" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ðеверный код ключа \"%s\" в комбинации клавиш" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ðеверное Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° \"%s\" в комбинации клавиш" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Запрошенного ключа \"%s\" на Ñкране не ÑущеÑтвует" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Выйти из Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file требует ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ð°\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "ДейÑтвие 'SessionLogout' недоÑтупно так как Openbox был Ñобран без " +#~ "поддержки ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑеÑÑиÑми" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ðевозможно Ñохранить ÑеÑÑию в \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Ошибка при Ñохранении ÑеÑÑии в \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ðе подключен к менеджеру ÑеÑÑии" + +#~ msgid "X Error: %s" +#~ msgstr "Ошибка X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Ðе удалоÑÑŒ запуÑтить \"%s\": %s" diff --git a/po/sk.po b/po/sk.po new file mode 100644 index 0000000..c126c8e --- /dev/null +++ b/po/sk.po @@ -0,0 +1,487 @@ +# Translation of openbox.pot to Slovak. +# Copyright (C) 2006 Mikael Magnusson +# This file is distributed under the same license as the Openbox 3 package. +# Jozef ŘÃha <jose1711@gmail.com>, 2006, 2007. +# FrantiÅ¡ek Eliáš <elias.frantisek@gmail.com>, 2009. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox-3.4.8\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2009-07-16 17:30+0200\n" +"Last-Translator: Frantisek Elias <elias.frantisek@gmail.com>\n" +"Language-Team: Slovak <sk@sk.org>\n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Vyžiadaná neplatná akcia \"%s\". Takáto akcia neexistuje." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nie" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ãno" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "SpustiÅ¥" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Nepodarilo sa skonvertovaÅ¥ cestu \"%s\" z utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "ZruÅ¡iÅ¥" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "UkonÄiÅ¥" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Naozaj sa chcete odhlásiÅ¥?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "OdhlásiÅ¥ sa" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "UrÄite chcete ukonÄiÅ¥ Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "UkonÄiÅ¥ Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Nepomenované okno" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "UkonÄujem proces..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Neodpovedá" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "UkonÄiÅ¥ proces" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Zdá sa, že okno \"%s\" neodpovedá. Chcete ho odpojiÅ¥ z X serveru?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "OdpojiÅ¥" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "PrejsÅ¥ na..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Správa plôch" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_PridaÅ¥ novú plochu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_OdstrániÅ¥ poslednú plochu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Okná" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Plochy" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "VÅ¡etky plochy" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Vrstva" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Vždy _navrchu" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "Nor_málna" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Vždy _dole" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_PoslaÅ¥ na plochu" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Menu klienta" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_ObnoviÅ¥" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Pre_sunúť" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Z_mena veľkosti" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Do iko_ny" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximalizovaÅ¥" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Ro/_Zvinúť" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "(Ne)_DekorovaÅ¥" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Z_avrieÅ¥" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Neplatný kontext \"%s\" v priradenà myÅ¡i" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Neplatné tlaÄidlo \"%s\" Å¡pecifikované v konfiguraÄnom súbore" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Nebolo možné vytvoriÅ¥ adresár '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "ZavrieÅ¥" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt priradenie klávesov v konfiguraÄnom súbore" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Nepodarilo sa nájsÅ¥ platný súbor menu \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Nepodarilo sa spustiÅ¥ prÃkaz pre pipe-menu \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Neplatný výstup z pipe-menu \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Pokus o sprÃstupnenie menu \"%s\", ale to neexistuje" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Viac..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Neplatné tlaÄidlo \"%s\" v priradenà myÅ¡i" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Nepodarilo sa prejsÅ¥ do domovského adresára \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Nepodarilo sa otvoriÅ¥ displej z premennej prostredia DISPLAY" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Nepodarilo sa inicializovaÅ¥ knižnicu obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X server nepodporuje locale." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Nemôžem nastaviÅ¥ locale pre X server." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Nepodarilo sa nájsÅ¥ platný konfiguraÄný súbor, použijem jednoduché " +"implicitné nastavenia" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Nepodarilo sa nahraÅ¥ tému." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "ReÅ¡tart zlyhal pri spúšťanà novej binárky \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [volby]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Volby:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Zobrazi tuto napovedu a skonci\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Zobrazi cislo verzie a skonci\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Nahradi momentalne beziace sedenie window manazera\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Vypne spojenie k manazerovi sedenia\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Predavanie sprav beziacej instancii Openboxu:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Opatovne nacita konfiguraciu Openboxu\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Restartuje Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr "" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Volby ladenia:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Spustit v synchronnom mode\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Zobrazit ladiaci vystup\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Zobrazit ladiaci vystup pre manipulaciu s fokusom\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Rozdelit displej na neprave obrazovky xineramy\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Prosim hlaste chyby na %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s vyzaduje parameter\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Neplatny parameter prikazoveho riadku \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Okenny manazer uz bezi na obrazovke %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Nepodarilo sa zÃskaÅ¥ výber okenného manažéra na obrazovke %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Okenný manažér na obrazovke %d sa neukonÄuje" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +msgstr[1] "" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "plocha %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Spúšťam %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Neplatný kláves pre modifikátor \"%s\" v priradenà klávesov/myÅ¡i" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Neplatný kód klávesu \"%s\" v priradenà klávesov" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Neplatný názov klávesu \"%s\" v priradenà klávesov" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Požadovaný kláves \"%s\" na displeji neexistuje" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "UkonÄiÅ¥ Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file vyzaduje parameter\n" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Nepodarilo sa uložiÅ¥ sedenie \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Chyba pri ukladanà sedenia do \"%s\": %s" + +#~ msgid "X Error: %s" +#~ msgstr "Chyba X: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Nepodarilo sa spustiÅ¥ \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Neplatné použitie akcie \"%s\". Akcia bude ignorovaná." diff --git a/po/sr.po b/po/sr.po new file mode 100644 index 0000000..f20be6d --- /dev/null +++ b/po/sr.po @@ -0,0 +1,508 @@ +# Serbian translations for Openbox package +# Copyright (C) 2008 Dana Jansens +# This file is distributed under the same license as the Openbox 3 package. +# Jay A. Fleming <tito.nehru.naser@gmail.com>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-11-11 14:52+0100\n" +"Last-Translator: Jay A. Fleming <tito.nehru.naser@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Захтевана је непоÑтојећа акција „%s“." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ðе" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Да" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Изврши" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Претварање путање „%s“ из УТФ-8 није уÑпело" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Поништи" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Излаз" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "ЗаиÑта желите да Ñе одјавите?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Одјављивање" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "ЗаиÑта желите да изађете из ОпенбокÑа?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Излаз из ОпенбокÑа" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Безимени прозор" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Убијање..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Програм Ñе не одазива" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Изгледа да Ñе прозор „%s“ не одазива. Желите ли да га приморате на излаз " +"Ñлањем Ñигнала %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Завршетак процеÑа" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Изгледа да Ñе прозор „%s“ не одазива. Желите ли да га одÑпојите од графичког " +"Ñервера?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Прекид везе" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Иди овде..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Управљање радним површинама" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Додајте нову радну површину" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Уклоните поÑледњу радну површину" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Прозори" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Радне површине" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Све радне површине" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Слој" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Увек изнад оÑталих" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Ðомално" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Увек на дну" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "ПремеÑти на радну површину" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "КориÑнички мени" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Врати" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Помери" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Промени величину" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Умањи" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Увећај" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Замотај/Одмотај прозор" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Ðе/УкраÑи" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Затвори" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Погрешан Ñадржај „%s“ у Ñпајању миша" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Погрешно дугме „%s“ наведено у датотеци за подешавање" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ðе могу да направим директоријум '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Затвори" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Сукоб у комбинацији таÑтера у датотеци за подешавање" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Датотека за подешавање менија („%s“) није пронађена" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ðије Ñе могла извршити команда за цевни-мени „%s“: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Погрешан излаз из цевног-менија „%s“" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Покушај приÑтупа менију „%s“ није уÑпео јер он не поÑтоји" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Више..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Погрешан таÑтер „%s“ у Ñпајању миша" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ðе могу Ñе премеÑтити у Личну фаÑциклу „%s“:%s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Ðије уÑпео приÑтуп екрану из променљиве окружења „DISPLAY“" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Ðије уÑпела иницијализација „obrender“ библиотеке." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "Графички Ñервер не подржава локалитет." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ðе може Ñе поÑтавити измењивач локалитета за графички Ñервер" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ðе могу наћи иÑправне датотеке подешавања. КориÑтиће Ñе Ñамо оÑновна " +"подешавања." + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ðе могу учитати тему." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Код обраде датотека за подешавање ОпенбокÑа пронађено је једна или више " +"ÑинтакÑних грешака (XML). ПоÑледња је била у датотеци „%s“, у линији %d, Ñа " +"поруком: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "СинтакÑна грешка у ОпенбокÑу" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Поновно покретање није могло извршити нови програм „%s“: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "ÐуторÑка права (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "СинтакÑа: openbox [опције]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Опције:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Прикажи ову помоћ и изађи\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Прикажи верзију и изађи\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Замени тренутно покренут управник прозора\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Ðаведите путању до датотеке Ñа подешавањима која ће Ñе " +"кориÑтити\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Онемогући везу Ñа управљачем ÑеÑија\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ПроÑлеђујем поруке покренутом примерку ОпенбокÑа:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Поново учитај подешавања за ОпенбокÑ\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Покрени опет ОпенбокÑ\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Изађи из ОпенбокÑа\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Опције отклањања грешака:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Изврши у иÑтовременом режиму\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Прикажи излаз код отклањања грешака\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Прикажи излаз код отклањања грешака за руковање " +"фокуÑом\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Подели екран на имитације „xinerama“ екрана\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Пријавите грешке на %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s захтева одговарајући аргумент\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "ÐеиÑправан аргумент командне линије „%s“\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Управвник прозора је већ покренут на екрану %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "ÐиÑам могао да добијем избор управника прозора на екрану %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Управвник прозора на екрану %d није завршио Ñа радом" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радну површину, а тренутна ÑеÑија их има %d. " +"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°." +msgstr[1] "" +"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радне површине, а тренутна ÑеÑија их има %d. " +"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°." +msgstr[2] "" +"ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ñ˜Ðµ подешен за %d радних површина, а тренутна ÑеÑија их има %d. " +"Преклапање ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ð¾Ð´ÐµÑˆÐ°Ð²Ð°ÑšÐ°." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "радна површина %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Извршавам %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "ÐеиÑправан измењивач таÑтера „%s“ у комбинацији таÑтер/миш" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "ÐеиÑправан код таÑтера „%s“ у комбинацији таÑтера" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "ÐеиÑправно име таÑтера „%s“ у комбинацији таÑтера" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Захтевани таÑтер „%s“ не поÑтоји на екрану" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "У реду" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Излаз из ОпенбокÑа" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file захтева одговарајући аргумент\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Ðкција „SessionLogout“ није доÑтупна јер је ÐžÐ¿ÐµÐ½Ð±Ð¾ÐºÑ Ð¿Ñ€ÐµÐ²ÐµÐ´ÐµÐ½ без подршке " +#~ "за управљање ÑеÑијама" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ðе могу Ñачувати ÑеÑију у „%s“: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Грешка приликом упиÑа у датотеку ÑеÑије „%s“: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ðије повезан Ñа управником ÑеÑија" + +#~ msgid "X Error: %s" +#~ msgstr "Грешка графичког Ñервера: %s" diff --git a/po/sr@latin.po b/po/sr@latin.po new file mode 100644 index 0000000..9997185 --- /dev/null +++ b/po/sr@latin.po @@ -0,0 +1,511 @@ +# Serbian translations for Openbox package +# Copyright (C) 2008 Dana Jansens +# This file is distributed under the same license as the Openbox 3 package. +# Jay A. Fleming <tito.nehru.naser@gmail.com>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-11-11 14:52+0100\n" +"Last-Translator: Jay A. Fleming <tito.nehru.naser@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Zahtevana je nepostojeća akcija „%s“." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Ne" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Da" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "IzvrÅ¡i" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Pretvaranje putanje „%s“ iz UTF-8 nije uspelo" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "PoniÅ¡ti" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Izlaz" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Zaista želite da se odjavite?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Odjavljivanje" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Zaista želite da izaÄ‘ete iz Openboksa?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Izlaz iz Openboksa" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Bezimeni prozor" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Ubijanje..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Program se ne odaziva" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Izgleda da se prozor „%s“ ne odaziva. Želite li da ga primorate na izlaz " +"slanjem signala %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "ZavrÅ¡etak procesa" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Izgleda da se prozor „%s“ ne odaziva. Želite li da ga odspojite od grafiÄkog " +"servera?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Prekid veze" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Idi ovde..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Upravljanje radnim povrÅ¡inama" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Dodajte novu radnu povrÅ¡inu" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Uklonite poslednju radnu povrÅ¡inu" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Prozori" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Radne povrÅ¡ine" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Sve radne povrÅ¡ine" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Sloj" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Uvek iznad ostalih" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Nomalno" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Uvek na dnu" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Premesti na radnu povrÅ¡inu" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "KorisniÄki meni" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Vrati" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Pomeri" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Promeni veliÄinu" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Umanji" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Uvećaj" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Zamotaj/Odmotaj prozor" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Ne/Ukrasi" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Zatvori" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "PogreÅ¡an sadržaj „%s“ u spajanju miÅ¡a" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "PogreÅ¡no dugme „%s“ navedeno u datoteci za podeÅ¡avanje" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ne mogu da napravim direktorijum '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Zatvori" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Sukob u kombinaciji tastera u datoteci za podeÅ¡avanje" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Datoteka za podeÅ¡avanje menija („%s“) nije pronaÄ‘ena" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Nije se mogla izvrÅ¡iti komanda za cevni-meni „%s“: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "PogreÅ¡an izlaz iz cevnog-menija „%s“" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "PokuÅ¡aj pristupa meniju „%s“ nije uspeo jer on ne postoji" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "ViÅ¡e..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "PogreÅ¡an taster „%s“ u spajanju miÅ¡a" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ne mogu se premestiti u LiÄnu fasciklu „%s“:%s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Nije uspeo pristup ekranu iz promenljive okruženja „DISPLAY“" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Nije uspela inicijalizacija „obrender“ biblioteke." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "GrafiÄki server ne podržava lokalitet." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ne može se postaviti izmenjivaÄ lokaliteta za grafiÄki server" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ne mogu naći ispravne datoteke podeÅ¡avanja. Koristiće se samo osnovna " +"podeÅ¡avanja." + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ne mogu uÄitati temu." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Kod obrade datoteka za podeÅ¡avanje Openboksa pronaÄ‘eno je jedna ili viÅ¡e " +"sintaksnih greÅ¡aka (XML). Poslednja je bila u datoteci „%s“, u liniji %d, " +"sa porukom: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Sintaksna greÅ¡ka u Openboksu" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Ponovno pokretanje nije moglo izvrÅ¡iti novi program „%s“: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Autorska prava (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sintaksa: openbox [opcije]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcije:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Prikaži ovu pomoć i izaÄ‘i\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Prikaži verziju i izaÄ‘i\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Zameni trenutno pokrenut upravnik prozora\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Navedite putanju do datoteke sa podeÅ¡avanjima koja će " +"se koristiti\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Onemogući vezu sa upravljaÄem sesija\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ProsleÄ‘ujem poruke pokrenutom primerku Openboksa:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Ponovo uÄitaj podeÅ¡avanja za Openboks\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Pokreni opet Openboks\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit IzaÄ‘i iz Openboksa\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Opcije otklanjanja greÅ¡aka:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync IzvrÅ¡i u istovremenom režimu\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Prikaži izlaz kod otklanjanja greÅ¡aka\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Prikaži izlaz kod otklanjanja greÅ¡aka za rukovanje " +"fokusom\n" + +#: openbox/openbox.c:547 +#, fuzzy +msgid " --debug-session Display debugging output for session management\n" +msgstr "" +" --debug-session Prikaži izlaz kod otklanjanja greÅ¡aka za rukovanje " +"sessionom\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Podeli ekran na imitacije „xinerama“ ekrana\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Prijavite greÅ¡ke na %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s zahteva odgovarajući argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Neispravan argument komandne linije „%s“\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Upravvnik prozora je već pokrenut na ekranu %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Nisam mogao da dobijem izbor upravnika prozora na ekranu %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Upravvnik prozora na ekranu %d nije zavrÅ¡io sa radom" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openboks je podeÅ¡en za %d radnu povrÅ¡inu, a trenutna sesija ih ima %d. " +"Preklapanje Openboks podeÅ¡avanja." +msgstr[1] "" +"Openboks je podeÅ¡en za %d radne povrÅ¡ine, a trenutna sesija ih ima %d. " +"Preklapanje Openboks podeÅ¡avanja." +msgstr[2] "" +"Openboks je podeÅ¡en za %d radnih povrÅ¡ina, a trenutna sesija ih ima %d. " +"Preklapanje Openboks podeÅ¡avanja." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "radna povrÅ¡ina %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "IzvrÅ¡avam %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Neispravan izmenjivaÄ tastera „%s“ u kombinaciji taster/miÅ¡" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Neispravan kod tastera „%s“ u kombinaciji tastera" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Neispravno ime tastera „%s“ u kombinaciji tastera" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Zahtevani taster „%s“ ne postoji na ekranu" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "U redu" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Izlaz iz Openboksa" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file zahteva odgovarajući argument\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Akcija „SessionLogout“ nije dostupna jer je Openboks preveden bez podrÅ¡ke " +#~ "za upravljanje sesijama" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ne mogu saÄuvati sesiju u „%s“: %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "GreÅ¡ka prilikom upisa u datoteku sesije „%s“: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Nije povezan sa upravnikom sesija" + +#~ msgid "X Error: %s" +#~ msgstr "GreÅ¡ka grafiÄkog servera: %s" diff --git a/po/sv.po b/po/sv.po new file mode 100644 index 0000000..33952a4 --- /dev/null +++ b/po/sv.po @@ -0,0 +1,503 @@ +# Swedish messages for openbox +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Mikael Magnusson <mikachu@icculus.org>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.5.0\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2011-08-01 18:11+0100\n" +"Last-Translator: Mikael Magnusson <mikachu@icculus.org>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Ogiltig action \"%s\" efterfrågades, men den finns inte." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Nej" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Ja" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Kör" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Lyckades inte konvertera sökvägen \"%s\" från utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Avbryt" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Avsluta" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Är du säker på att du vill logga ut?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Logga ut" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Är du säker på att du vill avsluta Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Avsluta Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Namnlöst fönster" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Dödar..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Svarar inte" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Fönstret \"%s\" verkar inte svara. Vill du tvinga det att avslutas genom " +"att skicka signalen %s?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Avsluta process" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Fönstret \"%s\" verkar inte svara. Vill du stänga dess anslutning till X-" +"servern?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Stäng anslutning" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Gå dit..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Hantera skrivbord" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Lägg till nytt skrivbord" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Ta bort sista skrivbordet" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Fönster" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Skrivbord" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Alla skrivbord" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Lager" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Alltid ö_verst" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Alltid _underst" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Skicka till skrivbord" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Klientmeny" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Åt_erställ" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Flytta" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Ändra s_torlek" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Mi_nimera" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Ma_ximera" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Rulla upp/ner" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "_Dekorationer" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Stän_g" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Ogiltig kontext \"%s\" i musbindning" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ogiltig knapp \"%s\" angiven i konfigurationsfilen" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" +"Openbox kompilerades utan stöd för att ladda bilder via Imlib2. Ikoner i " +"menyer kommer inte att laddas." + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Kunde inte skapa katalogen '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Stäng" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Konflikt med annan tangentbindning i konfigurationsfilen" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Kunde inte hitta en giltig menyfil \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Misslyckades att köra kommando för pipe-menyn \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ogiltig utdata från pipe-menyn \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Försökte öppna menyn \"%s\", men den finns inte" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Mer..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Ogiltig knapp \"%s\" i musbindning" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Kunde inte gå till hemkatalogen \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Kunde inte öppna en display från miljövariabeln DISPLAY." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Kunde inte initialisera obrender-biblioteket." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-servern stödjer inte lokalisering." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Kan inte sätta lokaliseringsmodifierare för X-servern." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Kunde inte hitta en giltig konfigurationsfil, använder enkla standardvärden" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Kunde inte ladda ett tema." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Ett eller flera fel påträffades medan konfigurationsfilerna för Openbox " +"lästes in. Se stdout för mer information. Det sista felet var i filen \"%s" +"\" rad %d, med meddelandet: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox syntaxfel" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Restart misslyckades att starta nytt program \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Copyright (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Syntax: openbox [alternativ]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Alternativ:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Visa den här hjälpen och avsluta\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Visa versionen och avsluta\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Ersätt den befintliga fönsterhanteraren\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FIL Ange sökvägen till konfigurationsfil att använda\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Avaktivera anslutning till sessionshanteraren\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Skicka meddelanden till en exekverande instans av Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Ladda om Openbox konfiguration\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Starta om Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Avsluta Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debug-alternativ:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Kör i synkroniserat läge\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr " --startup CMD Kör CMD efter uppstart\n" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Visa debuginformation\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus Visa debuginformation för fokushantering\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr " --debug-session Visa debuginformation för sessionshantering\n" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Dela skärmen i simulerade xinerama-skärmar\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Rapportera buggar till %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s kräver ett argument\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ogiltigt kommandoradsargument \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "En fönsterhanterare körs redan på skärm %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Kunde inte erhålla fönsterhanterarmarkeringen på skärm %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Fönsterhanteraren på skärm %d avslutar inte" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox är inställt på %d skrivbord, men nuvarande session har %d. Använder " +"sessionens inställning." +msgstr[1] "" +"Openbox är inställt på %d skrivbord, men nuvarande session har %d. Använder " +"sessionens inställning." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "skrivbord %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Kör %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Ogiltig modifikationstangent \"%s\" i tangent-/musbindning" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ogiltig tangentkod \"%s\" i tantentbindning" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ogiltigt tangentnamn \"%s\" i tangentbindning" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Efterfrågad tangent \"%s\" finns inte på displayen" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "OK" + +#~ msgid "Openbox" +#~ msgstr "Openbox" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Kommandot SessionLogout är inte tillgängligt eftersom Openbox " +#~ "kompilerades utan stöd för sessionshantering" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Kunde inte spara sessionen till \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Ett fel inträffade när sessionen skulle sparas till \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Inte ansluten till en sessionshanterare" + +#~ msgid "X Error: %s" +#~ msgstr "X-fel: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Kunde inte exekvera \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Ogiltigt användande av action \"%s\", den kommer ignoreras." diff --git a/po/tr.po b/po/tr.po new file mode 100644 index 0000000..0f03dec --- /dev/null +++ b/po/tr.po @@ -0,0 +1,503 @@ +# Turkish translation of openbox. +# Copyright (C) 2008 Dana Jansens +# This file is distributed under the same license as the openbox package. +# Tutku Dalmaz <mektup@tutkudalmaz.org>, 2008. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-05-24 15:08+0300\n" +"Last-Translator: Tutku Dalmaz <mektup@tutkudalmaz.org>\n" +"Language-Team: Turkish\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "\"%s\" geçersiz eylem isteÄŸi. Böyle bir eylem yok." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Hayır" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Evet" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Çalıştır" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "\"%s\" yolu utf8'e çevrilmesi baÅŸarısız oldu" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Ä°ptal" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Çık" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Oturumu kapatmak istediÄŸinizden emin misiniz?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Oturumu Kapat" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Openbox'tan çıkmak istediÄŸinize emin misiniz?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Openbox'tan Çık" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Ä°simsiz Pencere" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Sonlandırılıyor..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Cevap Vermiyor" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"\"%s\" penceresi cevap veriyor gibi görünmüyor. %s sinyali göndererek zorla " +"sonlandırmak ister misiniz?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Süreci Sonlandır" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"\"%s\" penceresi cevap veriyor gibi görünmüyor. X sunucusu ile baÄŸlantısını " +"sonlandırmak ister misiniz?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "BaÄŸlantıyı Kes" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Oraya git..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Masaüstlerini yönet" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Yeni masaüstü ekle" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Son masaüstünü kaldır" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Pencereler" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Masaüstleri" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Tüm masaüstleri" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "_Katman" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "_Her zaman üstte" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Normal" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "_Her zaman altta" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "_Masaüstüne gönder" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Ä°stemci menüsü" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Eski durumuna getir" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "_Taşı" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "_Yeniden boyutlandır" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "_Simge durumuna küçült" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "_Ekranı kapla" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Dürele/Aç" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Geri Al/Kapla" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "_Kapat" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Fare bağında geçersinz \"%s\" içeriÄŸi" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Yapılandırılma dosyasında belirtilmiÅŸ geçersiz \"%s\" düğmesi" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "'%s': %s dizini oluÅŸturulamadı" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Kapat" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Yapılandırma dosyasındaki tuÅŸ baÄŸlantısında çakışma" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "\"%s\" geçerli menü dosyası bulunamadı" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ä°letim menüsü için \"%s\": %s komutunun çalıştırılması baÅŸarısız oldu" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "\"%s\" iletim menüsü için geçersiz çıkış" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "\"%s\" menüsüne eriÅŸilmeye çalışıldı fakat bu menü yok" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Daha..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Fare bağında geçersiz \"%s\" tuÅŸu" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "\"%s\": %s ev dizini deÄŸiÅŸtirilemedi" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "DISPLAY çevre deÄŸiÅŸkeninde görüntünün açılması baÅŸarısız oldu." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "ObRender kitaplığının sıfırlanması baÅŸarısız oldu." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X sunucusu dil ayarlarını desteklemiyor." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "X sunucu için dil ayarları deÄŸiÅŸtiricisi ayarlanamadı." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Geçerli yapılandırma dosyası bulunamadı, bazı basit öntanımlı ayarlar " +"kullanılıyor" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Tema yüklenemedi." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Openbox yapılandırma dosyası ayrıştırılırken bir yada daha fazla XML " +"sözdizimi hatası bulundu. Daha fazla bilgi için stdout çıktısına bakınız. " +"Son hata \"%s\" dosyası içerisindeki %d satırında %s hata iletisi ile görüldü" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox Sözdizimi Hatası" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"Yeniden baÅŸlatmadaki \"%s\": %s çalıştırılabilir dosyalarının baÅŸlatılması " +"baÅŸarısız oldu" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Telif Hakkı (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Sözdizimi: openbox [seçenekler]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Seçenekler:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Yardımı görüntüle ve çık\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version Sürüm bilgisini görüntüle ve çık\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Güncel pencere yöneticisini deÄŸiÅŸtir\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr "" +" --config-file FILE Kullanılacak yapılandırma dosyasının yolunu belirtir\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr "" +" --sm-disable Oturum yöneticisiyle olan baÄŸlanıyı etkisiz kıl\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Ä°letiler çalışan bir Openbox örneÄŸine aktarılıyor:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Openbox yapılandırmasını yeniden yükle\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Openbox'ı yeniden baÅŸlat\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Openbox'tan çık\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Hata ayıklama seçenekleri:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync EÅŸ zamanlı kipte çalış\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Hata ayıklama çıktısını göster\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus ÖzelleÅŸmiÅŸ durum için hata ayıklama çıktısını göster\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Görüntü gerçek olmayan ekranlara bölünür\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Lütfen hataları %s adresine bildiriniz\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s dosyası bir deÄŸiÅŸkene ihtiyaç duyuyor\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "\"%s\" geçersiz komut satırı deÄŸiÅŸkeni\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "%d ekranınz zaten bir pencere yöneticixi çalışıyor" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Seçilen %d ekranında pencere yöneticisi bulunamadı" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "%d ekranındaki pencere yöneticisinden çıkılamıyor" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox %d masaüstleri için yapılandırılmıştır fakat güncel oturum %d dir. " +"Openbox yapılandırmasının üzerien yazılıyor." +msgstr[1] "" +"Openbox %d masaüstleri için yapılandırılmıştır fakat güncel oturum %d dir. " +"Openbox yapılandırmasının üzerien yazılıyor." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "%i masaüstü" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "%s çalışıyor" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Klavye/fare bağında geçersiz \"%s\" tuÅŸ deÄŸiÅŸtirici" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "TuÅŸ bağında \"%s\" geçersiz tuÅŸ kodu" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "TuÅŸ bağında \"%s\" geçersiz tuÅŸ adı" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Ä°stenilen \"%s\" tuÅŸu görüntüde yok" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Tamam" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Openbox'tan Çık" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file dosyası bir deÄŸiÅŸkene ihtiyaç duyuyor\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Openbox oturum yönetim desteÄŸi olmaksızın yapılandırıldığı için " +#~ "SessionLogout eylemi geçerli deÄŸildir." + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "\"%s\": %s oturumu kaydedilemedi" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Oturum \"%s\": %s'e kaydedilirken hata" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Oturum yöneticisine baÄŸlı deÄŸil" + +#~ msgid "X Error: %s" +#~ msgstr "%s X Hatası" diff --git a/po/uk.po b/po/uk.po new file mode 100644 index 0000000..a108e37 --- /dev/null +++ b/po/uk.po @@ -0,0 +1,500 @@ +# Ukrainian translation for Openbox. +# Copyright (C) 2007 Dmitriy Moroz +# Copyright (C) 2008 Serhiy Lysovenko +# This file is distributed under the same license as the openbox package. +# Dmitriy Moroz <zux@dimaka.org.ua>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.2\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-12-09 20:12+0200\n" +"Last-Translator: Serhiy Lysovenko <lisovenko.s[at]gmail[dot]com>\n" +"Language-Team: Ukrainian <linux.org.ua>\n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "ЗдійÑнено запит на неіÑнуючу дію \"%s\"." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "ÐÑ–" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Так" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Виконати" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Ðе вдалоÑÑ ÐºÐ¾Ð½Ð²ÐµÑ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ шлÑÑ… \"%s\" з utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "СкаÑувати" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Вихід" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Ви дійÑно бажаєте завершити ÑеанÑ?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Вийти" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Ви дійÑно хочете вийти з Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Вийти з Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Ðеназване вікно" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "ЗнищеннÑ..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Ðе відповідає" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Схоже, вікно \"%s\" не відповідає. Чи бажаєте примуÑово завершити програму, " +"поÑлавши Ñигнал \"%s\"?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "ПримуÑове завершеннÑ" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "Вікно \"%s\" не відповідає. Чи бажаєте його від'єднати від X Ñервера?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Від'єднати" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Перейти..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑтільницÑми" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "Додати нову Ñтільницю (_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "Видалити оÑтанню Ñтільницю (_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Вікна" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Стільниці" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Ðа вÑÑ–Ñ… ÑтільницÑÑ…" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Шар (_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Ðад уÑіма вікнами (_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "Звичайне Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ (_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Під вікнами (_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Відправити на Ñтільницю (_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Меню клієнта" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "Відновити (_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "ПереміÑтити (_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Змінити розмір (_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Згорнути (_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Розгорнути (_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "Скрутити/Розкрутити (_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "Перемкнути декорацію (_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Закрити (_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Ðекоректний контекÑÑ‚ \"%s\" в прив'Ñзці клавіш мишки" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Ðекоректна кнопка \"%s\" вказана у файлі конфігурації" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Ðе вдалоÑÑ Ñтворити каталог '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Закрити" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Конфлікт прив'Ñзки клавіш у файлі конфігурації" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ коректний файл меню \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ команду Ð´Ð»Ñ pipe-меню \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Ðекоректний вивід з pipe-меню \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Спроба доÑтупу до неіÑнуючого меню \"%s\"" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Більше..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Ðекоректна клавіша \"%s\" в прив'Ñзці клавіш мишки" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¹Ñ‚Ð¸ до домашнього каталогу \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ диÑплей зі змінної Ñередовища DISPLAY" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ð°Ñ–Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ бібліотеку obrender" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X-Ñервер не підтримує локалі" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Ðе можу вÑтановити модифікатори локалі Ð´Ð»Ñ X-Ñервера" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "" +"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ коректний файл конфігурації, викориÑтовую Ñтандартні " +"налаштуваннÑ" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ тему" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"ВиÑвлено одну або більше ÑинтакÑичних помилок XML під Ñ‡Ð°Ñ ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ " +"конфігураційних файлів Openbox. Щоб дізнатиÑÑŒ більше - переглÑньте stdout. " +"ОÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð¾Ð¼Ñ–Ñ‡ÐµÐ½Ð° помилка була в файлі \"%s\", Ñтрічка %d, повідомленнÑ: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "ÑинтакÑична помилка Openbox" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "" +"При перезавантаженні не вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ новий виконуваний файл \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "ÐвторÑькі права (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "СинтакÑ: openbox [параметри]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Параметри:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help Показати цю довідку Ñ– вийти\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --vesrion Показати верÑÑ–ÑŽ Ñ– вийти\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace Замінити запущений менеджер вікон\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file ФÐЙЛ Вказати шлÑÑ… до конфігураційного файлу\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Вимкнути з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· менеджером ÑеанÑу\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"Передача повідомлень процеÑу Openbox, що виконуєтьÑÑ\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Перезавантажити конфігурацію Openbox'у\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart ПерезапуÑтити Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Вийти з Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Ðалагоджувальні параметри\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync ЗапуÑтити в Ñинхронному режимі\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug Показувати інформацію налагоджуваннÑ\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus Показувати відлагоджувальний вивід Ð´Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ " +"фокуÑом\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Розбити екран на фальшиві екрани xinerama\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Будь-лаÑка, повідомлÑйте про помилки на %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s потребує аргументу\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Ðекоректний аргумент \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "Ðа диÑплеї %d вже запущений менеджер вікон" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Ðе можу запуÑтити менеджера вікон на диÑплеї %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "Менеджер вікон на диÑплеї %d не завершаєтьÑÑ" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox Ñконфігуровано на %d диÑплеїв, але в поточній ÑеÑÑ–Ñ— викориÑтовуєтьÑÑ " +"%d. ÐŸÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— Openbox." +msgstr[1] "" +"Openbox Ñконфігуровано на %d диÑплеїв, але в поточній ÑеÑÑ–Ñ— викориÑтовуєтьÑÑ " +"%d. ÐŸÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— Openbox." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "ÑÑ‚Ñ–Ð»ÑŒÐ½Ð¸Ñ†Ñ %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "ВиконуєтьÑÑ %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "" +"Ðекоректна назва модифікатору \"%s\" у прив'Ñзці клавіш клавіатури/мишки" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Ðекоректний код клавіші \"%s\" у прив'Ñзці клавіш" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Ðекоректна назва клавіші \"%s\" у прив'Ñзці клавіш" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Потрібної кнопки \"%s\" нема на екрані" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Гаразд" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Вийти з Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file потребує аргументу\n" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ ÑеÑÑ–ÑŽ в \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "Помилка при збереженні ÑеÑÑ–Ñ— в \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Ðе під'єднано до керівника ÑеÑÑ–Ñми" + +#~ msgid "X Error: %s" +#~ msgstr "Помилка X-Ñерверу: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "ÐевдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Ðекоректне викриÑÑ‚Ð°Ð½Ð½Ñ Ð´Ñ–Ñ— \"%s\". Ð”Ñ–Ñ Ð±ÑƒÐ´Ðµ проігнорована." diff --git a/po/vi.po b/po/vi.po new file mode 100644 index 0000000..d881b6d --- /dev/null +++ b/po/vi.po @@ -0,0 +1,503 @@ +# Vietnamese messages for Openbox. +# Copyright (C) 2007 Dana Jansens +# This file is distributed under the same license as the Openbox package. +# Quan Tran <qeed.quan@gmail.com>, 2007. +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-11 02:07+0100\n" +"Last-Translator: Quan Tran <qeed.quan@gmail.com>\n" +"Language-Team: None\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "Hà nh Ä‘á»™ng \"%s\" là m không được. Hà nh Ä‘á»™ng đó không có." + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "Không" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "Äược" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "Hà nh Ä‘á»™ng" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "Không thể chuyển chá»— \"%s\" từ utf8" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "Bãi bá»" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "Äi ra" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "Có chắc chắn Ä‘i ra không?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "Äi ra" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "Có chắc chắn Ä‘i ra Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "Äi ra Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "Cá»a sổ không tên" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "Äang giết..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "Không phản ứng" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "" +"Cái cá»a sổ \"%s\" không phản ứng được. Có muốn bắt nó Ä‘i ra bằng gá»i Ä‘i %s " +"tÃnh hiệu?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "Giết Process" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "" +"Cái cá»a sổ \"%s\" không phản ứng được. Có muốn rá»i nó ra X server không" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "Rá»i ra" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "Äi đến chá»— đó" + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "Quản lý chá»— là m việc" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "_Cá»™ng thêm chá»— là m việc" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "_Bá» lát chá»— là m việc" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "Cá»a sổ" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "Chá»— là m việc" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "Tất cả chá»— là m việc" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "Lá»›_p" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "Luôn luôn ở _trên" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "_Bình thÆ°á»ng" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "Luôn luôn ở _dÆ°á»›i" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "Gá»i đến chá»— là m _việc" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "Khách thá»±c Ä‘Æ¡n" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "_Hoà n lại" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "Chu_yển Ä‘i" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "Là m _nhá» hÆ¡n/lá»›n hÆ¡n" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "Biến _xuống" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "Biến _lá»›n nhất" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "_Cuốn lên/xuống" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "_Trang/Không Trang trÃ" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "Äón_g" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "Vô hiệu văn cảnh \"%s\" ở trong chuá»™t đặt" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "Sai nút \"%s\" ở trong hình thể" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "Không thể chế directory '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "Äóng" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "Xung Ä‘á»™t vá»›i chữ trói ở trong hình thể" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "Không có thể tìm vững chắc thá»±c Ä‘Æ¡n \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "Không có thể chạy lệnh cho ống-thá»±c Ä‘Æ¡n \"%s\": %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "Vô hiệu sản xuất của ống-thá»±c Ä‘Æ¡n \"%s\"" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "Thá» mở thá»±c Ä‘Æ¡n \"%s\" nhÆ°ng mà cái đó không có" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "Thêm nữa" + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "Vô hiệu nút \"%s\" ở trong máy chuá»™t đặt" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "Không thể đổi đến chá»— nhà \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "Không mở hình từ DISPLAY được." + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "Không mở được thÆ° viện obrender." + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "ChÆ°Æ¡ng trình X không có locale cho tiếng nay." + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "Không thể dùng locale cho chÆ°Æ¡ng trình X." + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "Không thể tìm ra hình thể, sẽ dùng bắt đầu hình thể" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "Không thể Ä‘á»c theme." + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"Má»™t hay là trên má»™t XML danh từ không đúng tìm thấy ở trong Openbox tà i " +"liệu. Coi stdout cho biết thêm. Cai sai lầm cuối cùng ở trong Openbox tà i " +"liệu \"%s\" ở hà ng %d vá»›i lá»i: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox danh từ không đúng" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "Bắt đầu lại há»ng mở được executable má»›i \"%s\": %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "Bản quyá»n (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "Cách dùng: openbox [chá»n lá»±a]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Chá»n lá»±a:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help TrÆ°ng bà y giúp đỡ nà y và đi ra\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version TrÆ°ng bà y số của chÆ°Æ¡ng trình và đi ra\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr "" +" --replace Thay thế chÆ°Æ¡ng trình quản lý cá»a sổ cho đến openbox\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE Chỉ chá»— Ä‘Æ°á»ng cho tà i liệu để dùng\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable Tắt liên lạc đến session quản lý\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ÄÆ°a thông báo cho chÆ°Æ¡ng trình Openbox:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure Bắt đầu lại Openbox's tà i liệu\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart Bắt đầu lại Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit Äi ra Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"Debugging chá»n lá»±a:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync Chạy trong cách thức synchronous\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug TrÆ°ng bà y debugging Ä‘oà n chữ\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr "" +" --debug-focus TrÆ°ng bà y debugging Ä‘oà n chữ cho Ä‘iá»u khiển táºp trung\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama Tách trÆ°ng bà y và o giả xinerama mà n\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"Là m Æ¡n báo cáo bugs ở chá»— %s\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s cần chá»n lá»±a má»™t tà i liệu\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "Mệnh lệnh viết sai \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "ChÆ°Æ¡ng trình quản lý cá»a sổ khác Ä‘ang chạy trên mà n hình %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "Không thể lấy được chÆ°Æ¡ng trình quản lý cá»a sổ ở trên mà n hình %d" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "ChÆ°Æ¡ng trình quản lý cá»a sổ trên mà n hình %d không Ä‘i ra" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox đặt cho %d chá»— là m việc, nhÆ°ng mà session hiện đại có %d. Láºt đổ " +"openbox tà i liệu cho cái má»›i." +msgstr[1] "" +"Openbox đặt cho %d chá»— là m việc, nhÆ°ng mà session hiện đại có %d. Láºt đổ " +"openbox tà i liệu cho cái má»›i." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "chá»— là m việc %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "Äan Chạy %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "Vô hiệu Modifier key \"%s\" ở chá»— máy keyboard/chuá»™t đặt" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "Vô hiệu key mã \"%s\" ở chá»— key đặt" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "Vô hiệu key tên \"%s\" ở chá»— key đặt" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "Yêu cầu key \"%s\" không có ở chá»— mà n hình" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "Äồng ý" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "Äi ra Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file cần chá»n lá»±a má»™t tà i liệu\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "Không thể là m SessionLogout được bởi vì Openbox không bá» \"session " +#~ "management support\" khi compile nó" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "Không thể tiết kiệm thá»i kỳ cho \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "BÄ© trục cháºt lúc tiết kiệm thá»i kỳ cho \"%s\": %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "Không hà ng vá»›i session quản lý" + +#~ msgid "X Error: %s" +#~ msgstr "X trục cháºt: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "Là m không được \"%s\": %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "Sá»± dùng hà nh Ä‘á»™ng \"%s\" sai rồi. Không là m hà nh Ä‘á»™ng đó." diff --git a/po/zh_CN.po b/po/zh_CN.po new file mode 100644 index 0000000..4f2761a --- /dev/null +++ b/po/zh_CN.po @@ -0,0 +1,495 @@ +# Simplified Chinese Messages for openbox. +# Copyright (C) 2007 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# +# Xiaoyu PENG <peng.xiaoyu@gmail.com>, 2007. +# Shaodong Di <gnuyhlfh@gmail.com>, 2008. +# zhou sf <sxzzsf@gmail.com>, 2008. +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-11 22:07+0800\n" +"Last-Translator: zhou sf <sxzzsf@gmail.com>\n" +"Language-Team: Simplified Chinese\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "请求的动作 \"%s\" æ— æ•ˆã€‚è¯¥åŠ¨ä½œä¸å˜åœ¨ã€‚" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "å¦" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "是" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "执行" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "从 utf8 转æ¢è·¯å¾„ \"%s\" 时失败" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "å–消" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "退出" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "确认注销å—?" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "注销" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "确认退出 Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "退出 Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "未命å窗å£" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "æ€æ»ä¸..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "æ— å“应" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "çª—å£ \"%s\" 似乎失去了å“应. å‘é€ä¿¡å· %s 以强制退出å—?" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "结æŸè¿›ç¨‹" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "çª—å£ \"%s\" 似乎失去了å“应. æ–开其与 X æœåŠ¡å™¨çš„连接?" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "æ–开连接" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "跳转到..." + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "管ç†æ¡Œé¢" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "æ·»åŠ æ–°æ¡Œé¢(_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "移除最åŽä¸€ä¸ªæ¡Œé¢(_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "窗å£" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "æ¡Œé¢" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "所有桌é¢" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "层(_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "总在最上层(_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "常规(_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "总在最底层(_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "å‘é€åˆ°æ¡Œé¢(_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "客户端èœå•" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "还原(_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "移动(_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "调整大å°(_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "最å°åŒ–(_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "最大化(_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "å·èµ·/放下(_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "去除装饰(_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "å…³é—(_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "é¼ æ ‡ç»‘å®šä¸æ— 效的上下文 \"%s\"" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "é…置文件ä¸æŒ‡å®šçš„按钮 \"%s\" æ— æ•ˆ" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "æ— æ³•åˆ›å»ºç›®å½• '%s': %s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "å…³é—" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "é…置文件ä¸çš„组åˆé”®å†²çª" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "æ— æ³•æ‰¾åˆ°æœ‰æ•ˆçš„èœå•æ–‡ä»¶ \"%s\"" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "执行管é“èœå•çš„命令 \"%s\" 时失败: %s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "管é“èœå• \"%s\" çš„è¾“å‡ºæ— æ•ˆ" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "å°è¯•è¯»å–èœå• \"%s\",但是它ä¸å˜åœ¨" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "更多..." + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "é¼ æ ‡ç»‘å®šä¸çš„æ— æ•ˆæŒ‰é”® \"%s\"" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "æ— æ³•åˆ‡æ¢åˆ°ä¸»ç›®å½• \"%s\": %s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "在打开DISPLAY环境å˜é‡æ‰€æŒ‡å®šçš„X显示时失败。" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "åˆå§‹åŒ–obrender库时失败。" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "XæœåŠ¡å™¨ä¸æ”¯æŒlocale。" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "æ— æ³•è®¾ç½®XæœåŠ¡å™¨çš„locale修饰键。" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "æ— æ³•æ‰¾åˆ°æœ‰æ•ˆçš„é…置文件,使用一些简å•çš„默认值" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "æ— æ³•è¯»å…¥ä¸»é¢˜ã€‚" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"å½“è§£æž Openbox é…置文件时å‘现一个或多个 XML è¯æ³•é”™è¯¯. 更多信æ¯æŸ¥çœ‹ stdout. " +"最近的错误出现于文件 \"%s\" ä¸ç¬¬ %d 行的: %s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox è¯æ³•é”™è¯¯" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "é‡æ–°å¯åŠ¨ä»¥æ‰§è¡Œæ–°çš„å¯æ‰§è¡Œæ–‡ä»¶ \"%s\" 时失败: %s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "版æƒæ‰€æœ‰ (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "用法: openbox [选项]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"选项: \n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help 显示该帮助信æ¯åŽé€€å‡º\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version 显示版本å·åŽé€€å‡º\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace 替æ¢å½“å‰è¿è¡Œçš„窗å£ç®¡ç†å™¨\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file FILE 使用指定的é…置文件\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable ç¦æ¢è¿žæŽ¥åˆ°ä¼šè¯ç®¡ç†å™¨\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"ä¼ é€’ä¿¡æ¯ç»™è¿è¡Œä¸çš„ Openbox 实例:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure é‡æ–°è½½å…¥ Openbox çš„é…ç½®\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart é‡æ–°å¯åŠ¨ Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit 退出 Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"调试选项:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync 在åŒæ¥æ¨¡å¼ä¸è¿è¡Œ\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug 显示调试输出\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus 显示焦点处ç†çš„调试输出\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama åˆ†å‰²æ˜¾ç¤ºåˆ°ä¼ªé€ çš„ xinerama å±å¹•ä¸\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"è¯·å‘ %s 报告错误\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s 需è¦ä¸€ä¸ªå‚æ•°\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "æ— æ•ˆçš„å‘½ä»¤è¡Œå‚æ•° \"%s\"\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "å·²ç»æœ‰çª—å£ç®¡ç†å™¨è¿è¡Œåœ¨å±å¹• %d" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "在å±å¹• %d æ— æ³•è¢«é€‰ä¸ºçª—å£ç®¡ç†å™¨" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "å±å¹• %d 的窗å£ç®¡ç†å™¨æ²¡æœ‰é€€å‡º" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox é…置了 %d 个桌é¢, 当å‰ä¼šè¯æ‹¥æœ‰ %d æ¡Œé¢. 覆盖 Openbox çš„é…ç½®." +msgstr[1] "" +"Openbox é…置了 %d 个桌é¢, 当å‰ä¼šè¯æ‹¥æœ‰ %d æ¡Œé¢. 覆盖 Openbox çš„é…ç½®." + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "æ¡Œé¢ %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "è¿è¡Œ %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "键盘/é¼ æ ‡çš„ç»‘å®š \"%s\" æ— æ•ˆ" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "按盘绑定ä¸æ— 效的键盘ç \"%s\"" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "按键绑定ä¸æ— 效的键å \"%s\"" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "请求的按键 \"%s\" 在显示ä¸ä¸å˜åœ¨" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "好" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "退出 Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file 需è¦ä¸€ä¸ªå‚æ•°\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "å› ä¸ºç¼–è¯‘ Openbox 时未支æŒä¼šè¯ç®¡ç†, å› æ¤ SessionLogout åŠ¨ä½œæ— æ•ˆ." + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "æ— æ³•ä¿å˜ä¼šè¯åˆ° \"%s\": %s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "在ä¿å˜ä¼šè¯åˆ° \"%s\" 时出错: %s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "未连接到会è¯ç®¡ç†å™¨" + +#~ msgid "X Error: %s" +#~ msgstr "X 错误: %s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "执行 \"%s\" 时失败: %s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "使用的动作 \"%s\" æ— æ•ˆã€‚åŠ¨ä½œå°†è¢«å¿½ç•¥ã€‚" diff --git a/po/zh_TW.po b/po/zh_TW.po new file mode 100644 index 0000000..d8bd94a --- /dev/null +++ b/po/zh_TW.po @@ -0,0 +1,495 @@ +# Traditional Chinese Messages for openbox. +# Copyright (C) 2006 Mikael Magnusson +# This file is distributed under the same license as the openbox package. +# Wei-Lun Chao <chaoweilun@gmail.com>, 2006, 07. +# æ´ªä»»è« <pcman.tw@gmail.com>, 2008 +# +msgid "" +msgstr "" +"Project-Id-Version: Openbox 3.4.7\n" +"Report-Msgid-Bugs-To: http://bugzilla.icculus.org\n" +"POT-Creation-Date: 2011-08-01 18:20+0200\n" +"PO-Revision-Date: 2008-03-06 01:01+0800\n" +"Last-Translator: æ´ªä»»è« <pcman.tw@gmail.com>\n" +"Language-Team: Chinese (traditional) <zh-l10n@linux.org.tw>\n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: openbox/actions.c:198 +#, c-format +msgid "Invalid action \"%s\" requested. No such action exists." +msgstr "è¦æ±‚的動作「%sã€ç„¡æ•ˆã€‚ç„¡æ¤é¡žå‹•ä½œå˜åœ¨ã€‚" + +#: openbox/actions/execute.c:147 +msgid "No" +msgstr "å¦" + +#: openbox/actions/execute.c:148 +msgid "Yes" +msgstr "是" + +#: openbox/actions/execute.c:152 +msgid "Execute" +msgstr "執行" + +#: openbox/actions/execute.c:161 +#, c-format +msgid "Failed to convert the path \"%s\" from utf8" +msgstr "轉æ›è·¯å¾‘「%sã€è‡ª utf8 時失敗" + +#: openbox/actions/exit.c:69 openbox/client.c:3550 +msgid "Cancel" +msgstr "å–消" + +#: openbox/actions/exit.c:70 +msgid "Exit" +msgstr "離開" + +#: openbox/actions/exit.c:74 +msgid "Are you sure you want to log out?" +msgstr "ä½ ç¢ºå®šè¦ç™»å‡ºå—Žï¼Ÿ" + +#: openbox/actions/exit.c:75 +msgid "Log Out" +msgstr "登出" + +#: openbox/actions/exit.c:78 +msgid "Are you sure you want to exit Openbox?" +msgstr "ä½ ç¢ºå®šè¦é›¢é–‹ Openbox?" + +#: openbox/actions/exit.c:79 +msgid "Exit Openbox" +msgstr "離開 Openbox" + +#: openbox/client.c:2037 +msgid "Unnamed Window" +msgstr "未命å視窗" + +#: openbox/client.c:2051 openbox/client.c:2082 +msgid "Killing..." +msgstr "æ£åœ¨ä¸æ¢..." + +#: openbox/client.c:2053 openbox/client.c:2084 +msgid "Not Responding" +msgstr "沒有回應" + +#: openbox/client.c:3539 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to force it " +"to exit by sending the %s signal?" +msgstr "視窗「%sã€ä¼¼ä¹Žå·²ç¶“åœæ¢å›žæ‡‰ã€‚ ä½ æƒ³é€å‡º \"%s\" 訊æ¯å¼·åˆ¶çµæŸç¨‹å¼å—Žï¼Ÿ" + +#: openbox/client.c:3541 +msgid "End Process" +msgstr "çµæŸ Process" + +#: openbox/client.c:3545 +#, c-format +msgid "" +"The window \"%s\" does not seem to be responding. Do you want to disconnect " +"it from the X server?" +msgstr "視窗「%sã€ä¼¼ä¹Žå·²ç¶“åœæ¢å›žæ‡‰ã€‚ ä½ æƒ³å¾ž X 伺æœå™¨å°‡å®ƒæ–·ç·šå—Žï¼Ÿ" + +#: openbox/client.c:3547 +msgid "Disconnect" +msgstr "æ–·ç·š" + +#: openbox/client_list_combined_menu.c:93 openbox/client_list_menu.c:90 +msgid "Go there..." +msgstr "到那è£åŽ»â€¦" + +#: openbox/client_list_combined_menu.c:100 +msgid "Manage desktops" +msgstr "管ç†æ¡Œé¢" + +#: openbox/client_list_combined_menu.c:101 openbox/client_list_menu.c:166 +msgid "_Add new desktop" +msgstr "åŠ å…¥æ–°æ¡Œé¢(_A)" + +#: openbox/client_list_combined_menu.c:102 openbox/client_list_menu.c:167 +msgid "_Remove last desktop" +msgstr "移除尾端桌é¢(_R)" + +#: openbox/client_list_combined_menu.c:157 +msgid "Windows" +msgstr "視窗" + +#: openbox/client_list_menu.c:214 +msgid "Desktops" +msgstr "æ¡Œé¢" + +#: openbox/client_menu.c:259 +msgid "All desktops" +msgstr "所有桌é¢" + +#: openbox/client_menu.c:371 +msgid "_Layer" +msgstr "層次(_L)" + +#: openbox/client_menu.c:376 +msgid "Always on _top" +msgstr "最上層(_T)" + +#: openbox/client_menu.c:377 +msgid "_Normal" +msgstr "一般(_N)" + +#: openbox/client_menu.c:378 +msgid "Always on _bottom" +msgstr "最下層(_B)" + +#: openbox/client_menu.c:380 +msgid "_Send to desktop" +msgstr "傳é€åˆ°æ¡Œé¢(_S)" + +#: openbox/client_menu.c:384 +msgid "Client menu" +msgstr "客戶端é¸å–®" + +#: openbox/client_menu.c:394 +msgid "R_estore" +msgstr "還原(_E)" + +#: openbox/client_menu.c:398 +msgid "_Move" +msgstr "移動(_M)" + +#: openbox/client_menu.c:400 +msgid "Resi_ze" +msgstr "調整大å°(_Z)" + +#: openbox/client_menu.c:402 +msgid "Ico_nify" +msgstr "最å°åŒ–(_N)" + +#: openbox/client_menu.c:406 +msgid "Ma_ximize" +msgstr "最大化(_X)" + +#: openbox/client_menu.c:410 +msgid "_Roll up/down" +msgstr "å‘上/å‘下æ²å‹•(_R)" + +#: openbox/client_menu.c:414 +msgid "Un/_Decorate" +msgstr "é–‹/關視窗è£é£¾(_D)" + +#: openbox/client_menu.c:418 +msgid "_Close" +msgstr "關閉(_C)" + +#: openbox/config.c:503 +#, c-format +msgid "Invalid context \"%s\" in mouse binding" +msgstr "èˆ‡æ»‘é¼ çµ„åˆçš„上下文「%sã€ç„¡æ•ˆ" + +#: openbox/config.c:857 +#, c-format +msgid "Invalid button \"%s\" specified in config file" +msgstr "在é…置檔ä¸æŒ‡å®šçš„按鈕「%sã€ç„¡æ•ˆ" + +#: openbox/config.c:882 +msgid "" +"Openbox was compiled without Imlib2 image loading support. Icons in menus " +"will not be loaded." +msgstr "" + +#: openbox/debug.c:55 +#, c-format +msgid "Unable to make directory '%s': %s" +msgstr "無法製作目錄'%s':%s" + +#: openbox/debug.c:138 openbox/openbox.c:372 +msgid "Close" +msgstr "關閉" + +#: openbox/keyboard.c:161 +msgid "Conflict with key binding in config file" +msgstr "與é…置檔ä¸çš„按éµçµ„åˆè¡çª" + +#: openbox/menu.c:94 openbox/menu.c:106 +#, c-format +msgid "Unable to find a valid menu file \"%s\"" +msgstr "無法找到有效的é¸å–®æª”案「%sã€" + +#: openbox/menu.c:158 +#, c-format +msgid "Failed to execute command for pipe-menu \"%s\": %s" +msgstr "執行命令於管線é¸å–®ã€Œ%sã€æ™‚失敗:%s" + +#: openbox/menu.c:172 +#, c-format +msgid "Invalid output from pipe-menu \"%s\"" +msgstr "從管線é¸å–®ã€Œ%sã€çš„輸出無效" + +#: openbox/menu.c:185 +#, c-format +msgid "Attempted to access menu \"%s\" but it does not exist" +msgstr "試圖å˜å–é¸å–®ã€Œ%sã€ä½†æ˜¯å®ƒä¸å˜åœ¨" + +#: openbox/menu.c:400 openbox/menu.c:401 +msgid "More..." +msgstr "更多…" + +#: openbox/mouse.c:376 +#, c-format +msgid "Invalid button \"%s\" in mouse binding" +msgstr "èˆ‡æ»‘é¼ çµ„åˆçš„按鈕「%sã€ç„¡æ•ˆ" + +#: openbox/openbox.c:137 +#, c-format +msgid "Unable to change to home directory \"%s\": %s" +msgstr "無法變更到主目錄「%sã€ï¼š%s" + +#: openbox/openbox.c:152 +msgid "Failed to open the display from the DISPLAY environment variable." +msgstr "é–‹å•Ÿä¾ DISPLAY 環境變數所指的顯示時失敗。" + +#: openbox/openbox.c:182 +msgid "Failed to initialize the obrender library." +msgstr "åˆå§‹åŒ– obrender 函å¼åº«æ™‚失敗。" + +#: openbox/openbox.c:193 +msgid "X server does not support locale." +msgstr "X 伺æœå™¨ä¸æ”¯æ´èªžå€ã€‚" + +#: openbox/openbox.c:195 +msgid "Cannot set locale modifiers for the X server." +msgstr "無法è¨å®šç”¨æ–¼ X 伺æœå™¨çš„語å€ä¿®é£¾é …。" + +#: openbox/openbox.c:253 +msgid "Unable to find a valid config file, using some simple defaults" +msgstr "無法找到有效的é…置檔案,而使用æŸäº›ç°¡å–®çš„é è¨å€¼" + +#: openbox/openbox.c:286 +msgid "Unable to load a theme." +msgstr "無法載入佈景主題。" + +#: openbox/openbox.c:370 +#, c-format +msgid "" +"One or more XML syntax errors were found while parsing the Openbox " +"configuration files. See stdout for more information. The last error seen " +"was in file \"%s\" line %d, with message: %s" +msgstr "" +"è§£æž Openbox è¨å®šæª” XML 語法時發ç¾ä¸€å€‹æˆ–多個錯誤。 查看 stdout 以ç²å¾—更多資" +"訊。 最後一個發ç¾çš„錯誤在檔案 \"%s\" 第 %d 行。訊æ¯ï¼š%s" + +#: openbox/openbox.c:372 +msgid "Openbox Syntax Error" +msgstr "Openbox 語法錯誤" + +#: openbox/openbox.c:438 +#, c-format +msgid "Restart failed to execute new executable \"%s\": %s" +msgstr "é‡æ–°å•Ÿå‹•ä»¥åŸ·è¡Œæ–°çš„å¯åŸ·è¡Œæª”「%sã€æ™‚失敗:%s" + +#: openbox/openbox.c:517 openbox/openbox.c:519 +msgid "Copyright (c)" +msgstr "著作權 (c)" + +#: openbox/openbox.c:528 +msgid "Syntax: openbox [options]\n" +msgstr "語法:openbox [é¸é …]\n" + +#: openbox/openbox.c:529 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"é¸é …:\n" + +#: openbox/openbox.c:530 +msgid " --help Display this help and exit\n" +msgstr " --help 顯示æ¤èªªæ˜Žç„¶å¾Œé›¢é–‹\n" + +#: openbox/openbox.c:531 +msgid " --version Display the version and exit\n" +msgstr " --version 顯示版本然後離開\n" + +#: openbox/openbox.c:532 +msgid " --replace Replace the currently running window manager\n" +msgstr " --replace 替æ›ç›®å‰åŸ·è¡Œçš„視窗管ç†å“¡\n" + +#. TRANSLATORS: if you translate "FILE" here, make sure to keep the "Specify..." +#. aligned still, if you have to, make a new line with \n and 22 spaces. It's +#. fine to leave it as FILE though. +#: openbox/openbox.c:536 +msgid " --config-file FILE Specify the path to the config file to use\n" +msgstr " --config-file <檔案> 指定è¦ä½¿ç”¨çš„è¨å®šæª”路徑\n" + +#: openbox/openbox.c:537 +msgid " --sm-disable Disable connection to the session manager\n" +msgstr " --sm-disable åœç”¨èˆ‡åŸ·è¡ŒéšŽæ®µç®¡ç†ç¨‹å¼çš„連çµ\n" + +#: openbox/openbox.c:538 +msgid "" +"\n" +"Passing messages to a running Openbox instance:\n" +msgstr "" +"\n" +"傳éžè¨Šæ¯åˆ°åŸ·è¡Œä¸çš„ Openbox 實體:\n" + +#: openbox/openbox.c:539 +msgid " --reconfigure Reload Openbox's configuration\n" +msgstr " --reconfigure é‡æ–°è¼‰å…¥ Openbox é…ç½®\n" + +#: openbox/openbox.c:540 +msgid " --restart Restart Openbox\n" +msgstr " --restart é‡æ–°å•Ÿå‹• Openbox\n" + +#: openbox/openbox.c:541 +msgid " --exit Exit Openbox\n" +msgstr " --exit çµæŸ Openbox\n" + +#: openbox/openbox.c:542 +msgid "" +"\n" +"Debugging options:\n" +msgstr "" +"\n" +"åµéŒ¯é¸é …:\n" + +#: openbox/openbox.c:543 +msgid " --sync Run in synchronous mode\n" +msgstr " --sync 在åŒæ¥æ¨¡å¼ä¸é‹è¡Œ\n" + +#: openbox/openbox.c:544 +msgid " --startup CMD Run CMD after starting\n" +msgstr "" + +#: openbox/openbox.c:545 +msgid " --debug Display debugging output\n" +msgstr " --debug 顯示åµéŒ¯è¼¸å‡º\n" + +#: openbox/openbox.c:546 +msgid " --debug-focus Display debugging output for focus handling\n" +msgstr " --debug-focus 顯示焦點處ç†çš„åµéŒ¯è¼¸å‡º\n" + +#: openbox/openbox.c:547 +msgid " --debug-session Display debugging output for session management\n" +msgstr "" + +#: openbox/openbox.c:548 +msgid " --debug-xinerama Split the display into fake xinerama screens\n" +msgstr " --debug-xinerama 分割顯示以進入å‡é€ çš„ xinerama 螢幕\n" + +#: openbox/openbox.c:549 +#, c-format +msgid "" +"\n" +"Please report bugs at %s\n" +msgstr "" +"\n" +"è«‹å‘ %s å ±å‘ŠéŒ¯èª¤\n" + +#: openbox/openbox.c:632 openbox/openbox.c:666 +#, c-format +msgid "%s requires an argument\n" +msgstr "%s 需è¦ä¸€å€‹åƒæ•¸\n" + +#: openbox/openbox.c:709 +#, c-format +msgid "Invalid command line argument \"%s\"\n" +msgstr "無效的命令列引數「%sã€\n" + +#: openbox/screen.c:106 openbox/screen.c:191 +#, c-format +msgid "A window manager is already running on screen %d" +msgstr "螢幕 %d ä¸å·²ç¶“有視窗管ç†å“¡åœ¨é‹è¡Œ" + +#: openbox/screen.c:127 +#, c-format +msgid "Could not acquire window manager selection on screen %d" +msgstr "無法於螢幕 %d ç²é¸ç‚ºè¦–窗管ç†å“¡" + +#: openbox/screen.c:150 +#, c-format +msgid "The WM on screen %d is not exiting" +msgstr "螢幕 %d ä¸çš„視窗管ç†å“¡ä¸¦æœªé›¢é–‹" + +#. TRANSLATORS: If you need to specify a different order of the +#. arguments, you can use %1$d for the first one and %2$d for the +#. second one. For example, +#. "The current session has %2$d desktops, but Openbox is configured for %1$d ..." +#: openbox/screen.c:418 +#, fuzzy, c-format +msgid "" +"Openbox is configured for %d desktop, but the current session has %d. " +"Overriding the Openbox configuration." +msgid_plural "" +"Openbox is configured for %d desktops, but the current session has %d. " +"Overriding the Openbox configuration." +msgstr[0] "" +"Openbox 原先被è¨å®šç‚ºä½¿ç”¨ %d 個桌é¢ï¼Œä½†ç›®å‰çš„作æ¥éšŽæ®µæœ‰å…¶ä»–程å¼è®Šæ›´è¨å®šç‚º %d " +"å€‹ï¼Œå› æ¤å¿½ç•¥ Openbox çš„è¨å®š" + +#: openbox/screen.c:1205 +#, c-format +msgid "desktop %i" +msgstr "æ¡Œé¢ %i" + +#: openbox/startupnotify.c:241 +#, c-format +msgid "Running %s" +msgstr "æ£åœ¨é‹è¡Œ %s" + +#: openbox/translate.c:59 +#, c-format +msgid "Invalid modifier key \"%s\" in key/mouse binding" +msgstr "與éµç›¤/æ»‘é¼ çµ„åˆçš„輔助按éµã€Œ%sã€ç„¡æ•ˆ" + +#: openbox/translate.c:138 +#, c-format +msgid "Invalid key code \"%s\" in key binding" +msgstr "與按éµçµ„åˆçš„éµç¢¼ã€Œ%sã€ç„¡æ•ˆ" + +#: openbox/translate.c:145 +#, c-format +msgid "Invalid key name \"%s\" in key binding" +msgstr "與按éµçµ„åˆçš„éµå「%sã€ç„¡æ•ˆ" + +#: openbox/translate.c:151 +#, c-format +msgid "Requested key \"%s\" does not exist on the display" +msgstr "è¦æ±‚的按éµã€Œ%sã€ä¸å˜åœ¨æ–¼ç•«é¢ä¹‹ä¸" + +#: openbox/prompt.c:153 +msgid "OK" +msgstr "確定" + +#, fuzzy +#~ msgid "Openbox" +#~ msgstr "離開 Openbox" + +#~ msgid "--config-file requires an argument\n" +#~ msgstr "--config-file 需è¦ä¸€å€‹åƒæ•¸\n" + +#~ msgid "" +#~ "The SessionLogout action is not available since Openbox was built without " +#~ "session management support" +#~ msgstr "" +#~ "SessionLogout å‹•ä½œç„¡æ³•ä½¿ç”¨ï¼Œå› ç‚º Openbox 在編è¯æ™‚沒有使用作æ¥éšŽæ®µç®¡ç†æ”¯æ´" + +#~ msgid "Unable to save the session to \"%s\": %s" +#~ msgstr "無法儲å˜åŸ·è¡ŒéšŽæ®µåˆ°ã€Œ%sã€ï¼š%s" + +#~ msgid "Error while saving the session to \"%s\": %s" +#~ msgstr "當儲å˜åŸ·è¡ŒéšŽæ®µã€Œ%sã€æ™‚發生錯誤:%s" + +#~ msgid "Not connected to a session manager" +#~ msgstr "沒有連接到作æ¥éšŽæ®µç®¡ç†å“¡" + +#~ msgid "X Error: %s" +#~ msgstr "X 錯誤:%s" + +#~ msgid "Failed to execute \"%s\": %s" +#~ msgstr "執行「%sã€æ™‚失敗:%s" + +#~ msgid "Invalid use of action \"%s\". Action will be ignored." +#~ msgstr "使用的動作「%sã€ç„¡æ•ˆã€‚動作將被忽略。" diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..9fc5fe8 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,6 @@ +files=$(wildcard *.c) + +all: $(files:.c=) + +%: %.c + $(CC) `pkg-config --cflags --libs glib-2.0` $(CFLAGS) -o $@ $^ -lX11 -lXext -L/usr/X11R6/lib -I/usr/X11R6/include diff --git a/tests/aspect.c b/tests/aspect.c new file mode 100644 index 0000000..1ae3a85 --- /dev/null +++ b/tests/aspect.c @@ -0,0 +1,79 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + aspect.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + XSetWindowAttributes xswa; + unsigned long xswamask; + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400; + XSizeHints size; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + xswa.win_gravity = StaticGravity; + xswamask = CWWinGravity; + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, xswamask, &xswa); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + size.flags = PAspect; + size.min_aspect.x = 3; + size.min_aspect.y = 3; + size.max_aspect.x = 3; + size.max_aspect.y = 3; + XSetWMNormalHints(display, win, &size); + + XMapWindow(display, win); + XFlush(display); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/big.c b/tests/big.c new file mode 100644 index 0000000..bc17c5a --- /dev/null +++ b/tests/big.c @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + big.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=10,y=10,h=2000,w=2000; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + sleep(2); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/borderchange.c b/tests/borderchange.c new file mode 100644 index 0000000..56d5500 --- /dev/null +++ b/tests/borderchange.c @@ -0,0 +1,107 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + borderchange.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=10,y=10,h=200,w=200; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + XMapWindow(display, win); + XFlush(display); + + while (XPending(display)) { + XNextEvent(display, &report); + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + } + sleep(2); + + printf("setting border to 0\n"); + XSetWindowBorderWidth(display, win, 0); + XFlush(display); + + while (XPending(display)) { + XNextEvent(display, &report); + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + } + sleep(2); + + printf("setting border to 50\n"); + XSetWindowBorderWidth(display, win, 50); + XFlush(display); + + while (XPending(display)) { + XNextEvent(display, &report); + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 0; +} diff --git a/tests/confignotify.c b/tests/confignotify.c new file mode 100644 index 0000000..4bb09f8 --- /dev/null +++ b/tests/confignotify.c @@ -0,0 +1,89 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + confignotify.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=10,y=10,h=100,w=100; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XSelectInput(display, win, (ExposureMask | StructureNotifyMask | + GravityNotify)); + + XMapWindow(display, win); + XFlush(display); + + sleep(1); + XResizeWindow(display, win, w+5, h+5); + XMoveWindow(display, win, x, y); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case MapNotify: + printf("map notify\n"); + break; + case Expose: + printf("exposed\n"); + break; + case GravityNotify: + printf("gravity notify event 0x%x window 0x%x x %d y %d\n", + report.xgravity.event, report.xgravity.window, + report.xgravity.x, report.xgravity.y); + break; + case ConfigureNotify: { + int se = report.xconfigure.send_event; + int event = report.xconfigure.event; + int window = report.xconfigure.window; + int x = report.xconfigure.x; + int y = report.xconfigure.y; + int w = report.xconfigure.width; + int h = report.xconfigure.height; + int bw = report.xconfigure.border_width; + int above = report.xconfigure.above; + int or = report.xconfigure.override_redirect; + printf("confignotify send %d ev 0x%x win 0x%x %i,%i-%ix%i bw %i\n" + " above 0x%x ovrd %d\n", + se,event,window,x,y,w,h,bw,above,or); + break; + } + } + + } + + return 1; +} diff --git a/tests/confignotifymax.c b/tests/confignotifymax.c new file mode 100644 index 0000000..0debf5e --- /dev/null +++ b/tests/confignotifymax.c @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + confignotify.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + Atom _net_max[2],_net_state; + int x=10,y=10,h=100,w=100; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _net_state = XInternAtom(display, "_NET_WM_STATE", False); + _net_max[0] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + _net_max[1] = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + XChangeProperty(display, win, _net_state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_net_max, 2); + + XSelectInput(display, win, (ExposureMask | StructureNotifyMask | + GravityNotify)); + + XMapWindow(display, win); + XFlush(display); + + //sleep(1); + //XResizeWindow(display, win, w+5, h+5); + //XMoveWindow(display, win, x, y); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case MapNotify: + printf("map notify\n"); + break; + case Expose: + printf("exposed\n"); + break; + case GravityNotify: + printf("gravity notify event 0x%x window 0x%x x %d y %d\n", + report.xgravity.event, report.xgravity.window, + report.xgravity.x, report.xgravity.y); + break; + case ConfigureNotify: { + int se = report.xconfigure.send_event; + int event = report.xconfigure.event; + int window = report.xconfigure.window; + int x = report.xconfigure.x; + int y = report.xconfigure.y; + int w = report.xconfigure.width; + int h = report.xconfigure.height; + int bw = report.xconfigure.border_width; + int above = report.xconfigure.above; + int or = report.xconfigure.override_redirect; + printf("confignotify send %d ev 0x%x win 0x%x %i,%i-%ix%i bw %i\n" + " above 0x%x ovrd %d\n", + se,event,window,x,y,w,h,bw,above,or); + break; + } + } + + } + + return 1; +} diff --git a/tests/cursorio.c b/tests/cursorio.c new file mode 100644 index 0000000..527d70c --- /dev/null +++ b/tests/cursorio.c @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + cursorio.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> + +int main () { + Display *display; + Window win, child; + XEvent report; + int x=10,y=10,h=100,w=400,b=10; + XSetWindowAttributes a; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, b, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + a.cursor = XCreateFontCursor(display, XC_watch); + child = XCreateWindow(display, win, + x+w/8, y+h/8, 3*w/4, 3*h/4, 0, + CopyFromParent, InputOnly, + CopyFromParent, CWCursor, &a); + + XMapWindow(display, child); + XMapWindow(display, win); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/duplicatesession.c b/tests/duplicatesession.c new file mode 100644 index 0000000..5abe995 --- /dev/null +++ b/tests/duplicatesession.c @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + duplicatesession.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <string.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main (int argc, char **argv) { + Display *display; + Window win1, win2; + XEvent report; + int x=10,y=10,h=100,w=400; + XSizeHints size; + XTextProperty name; + Atom sm_id, enc; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + sm_id = XInternAtom(display,"SM_CLIENT_ID",False); + enc = XInternAtom(display,"STRING",False); + + win1 = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + win2 = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win1,WhitePixel(display,0)); + XSetWindowBackground(display,win2,BlackPixel(display,0)); + + XChangeProperty(display, win1, sm_id, enc, 8, + PropModeAppend, "abcdefg", strlen("abcdefg")); + XChangeProperty(display, win2, sm_id, enc, 8, + PropModeAppend, "abcdefg", strlen("abcdefg")); + + XFlush(display); + XMapWindow(display, win1); + XMapWindow(display, win2); + + while (1) + XNextEvent(display, &report); + + return 1; +} diff --git a/tests/extentsrequest.c b/tests/extentsrequest.c new file mode 100644 index 0000000..055624d --- /dev/null +++ b/tests/extentsrequest.c @@ -0,0 +1,133 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + extentsrequest.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +void request (Display *display, Atom _request, Atom _extents, Window win) { + XEvent msg; + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _request; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 0l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = 0l; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); +} + +void reply (Display* display, Atom _extents) { + printf(" waiting for extents\n"); + while (1) { + XEvent report; + XNextEvent(display, &report); + + if (report.type == PropertyNotify && + report.xproperty.atom == _extents) + { + Atom ret_type; + int ret_format; + unsigned long ret_items, ret_bytesleft; + unsigned long *prop_return; + XGetWindowProperty(display, report.xproperty.window, _extents, 0, 4, + False, XA_CARDINAL, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return); + if (ret_type == XA_CARDINAL && ret_format == 32 && ret_items == 4) + printf(" got new extents %d, %d, %d, %d\n", + prop_return[0], prop_return[1], prop_return[2], + prop_return[3]); + break; + } + } +} + +int main () { + Display *display; + Window win; + Atom _request, _extents, _type, _normal, _desktop, _state; + Atom _state_fs, _state_mh, _state_mv; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); + _normal = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False); + _desktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + _request = XInternAtom(display, "_NET_REQUEST_FRAME_EXTENTS", False); + _extents = XInternAtom(display, "_NET_FRAME_EXTENTS", False); + _state = XInternAtom(display, "_NET_WM_STATE", False); + _state_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + _state_mh = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + _state_mv = XInternAtom(display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSelectInput(display, win, PropertyChangeMask); + + printf("requesting for type normal\n"); + XChangeProperty(display, win, _type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_normal, 1); + request(display, _request, _extents, win); + reply(display, _extents); + + printf("requesting for type normal+fullscreen\n"); + XChangeProperty(display, win, _type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_normal, 1); + XChangeProperty(display, win, _state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_state_fs, 1); + request(display, _request, _extents, win); + reply(display, _extents); + + printf("requesting for type normal+maxv\n"); + XChangeProperty(display, win, _type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_normal, 1); + XChangeProperty(display, win, _state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_state_mv, 1); + request(display, _request, _extents, win); + reply(display, _extents); + + printf("requesting for type normal+maxh\n"); + XChangeProperty(display, win, _type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_normal, 1); + XChangeProperty(display, win, _state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_state_mh, 1); + request(display, _request, _extents, win); + reply(display, _extents); + + printf("requesting for type desktop\n"); + XChangeProperty(display, win, _type, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&_desktop, 1); + request(display, _request, _extents, win); + reply(display, _extents); + + return 1; +} diff --git a/tests/fakeunmap.c b/tests/fakeunmap.c new file mode 100644 index 0000000..d30a917 --- /dev/null +++ b/tests/fakeunmap.c @@ -0,0 +1,60 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + fakeunmap.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=50,y=50,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + usleep(10000); + + msg.type = UnmapNotify; + msg.xunmap.display = display; + msg.xunmap.event = RootWindow(display, DefaultScreen(display)); + msg.xunmap.window = win; + msg.xunmap.from_configure = False; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), False, + SubstructureRedirectMask|SubstructureNotifyMask, &msg); + usleep(10000); + + XUnmapWindow(display, win); + XSync(display, False); + + return 1; +} diff --git a/tests/fallback.c b/tests/fallback.c new file mode 100644 index 0000000..721ed31 --- /dev/null +++ b/tests/fallback.c @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + fallback.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window one, two; + XEvent report; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + one = XCreateWindow(display, RootWindow(display, 0), + 0,0,200,200, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + two = XCreateWindow(display, RootWindow(display, 0), + 0,0,150,150, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,one,WhitePixel(display,0)); + XSetWindowBackground(display,two,BlackPixel(display,0)); + + XSetTransientForHint(display, two, one); + + XMapWindow(display, one); + XFlush(display); + usleep(1000); + + XMapWindow(display, two); + XFlush(display); + usleep(1000); + + XDestroyWindow(display, two); + XFlush(display); + usleep(1000); + + XDestroyWindow(display, one); + XSync(display, False); + + return 1; +} diff --git a/tests/focusout.c b/tests/focusout.c new file mode 100644 index 0000000..03ba56f --- /dev/null +++ b/tests/focusout.c @@ -0,0 +1,224 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + focusout.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win, child; + XEvent report; + Atom _net_fs, _net_state; + XEvent msg; + int x=50,y=50,h=100,w=400; + XWMHints hint; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + child = XCreateWindow(display, win, + 10, 10, w-20, h-20, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + XSetWindowBackground(display,child,BlackPixel(display,0)); + + XSelectInput(display, win, + FocusChangeMask|EnterWindowMask|LeaveWindowMask); + XMapWindow(display, win); + XMapWindow(display, child); + + while (1) { + const char *mode, *detail; + + XNextEvent(display, &report); + + switch (report.type) { + case ButtonPress: + printf("button press\n"); + printf("type : %d\n", report.xbutton.type); + printf("serial : %d\n", report.xbutton.serial); + printf("send_event : %d\n", report.xbutton.send_event); + printf("display : 0x%x\n", report.xbutton.display); + printf("window : 0x%x\n", report.xbutton.window); + printf("root : 0x%x\n", report.xbutton.root); + printf("subwindow : 0x%x\n", report.xbutton.subwindow); + printf("time : %d\n", report.xbutton.time); + printf("x, y : %d, %d\n", report.xbutton.x, + report.xbutton.y); + printf("rootx, rooty: %d, %d\n", report.xbutton.x_root, + report.xbutton.y_root); + printf("state : 0x%x\n", report.xbutton.state); + printf("button : %d\n", report.xbutton.button); + printf("same_screen : %d\n", report.xbutton.same_screen); + printf("---\n"); + break; + case MotionNotify: + printf("motion\n"); + printf("type : %d\n", report.xmotion.type); + printf("serial : %d\n", report.xmotion.serial); + printf("send_event : %d\n", report.xmotion.send_event); + printf("display : 0x%x\n", report.xmotion.display); + printf("window : 0x%x\n", report.xmotion.window); + printf("root : 0x%x\n", report.xmotion.root); + printf("subwindow : 0x%x\n", report.xmotion.subwindow); + printf("time : %d\n", report.xmotion.time); + printf("x, y : %d, %d\n", report.xmotion.x, + report.xmotion.y); + printf("rootx, rooty: %d, %d\n", report.xmotion.x_root, + report.xmotion.y_root); + printf("state : 0x%x\n", report.xmotion.state); + printf("is_hint : %d\n", report.xmotion.is_hint); + printf("same_screen : %d\n", report.xmotion.same_screen); + printf("---\n"); + if (XGrabPointer(display, win, False, ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, None, None, + report.xmotion.time) == GrabSuccess) + printf("GrabSuccess\n"); + else + printf("GrabFail\n"); + break; + case ButtonRelease: + XUngrabPointer(display, report.xbutton.time); + break; + case FocusIn: + switch (report.xfocus.mode) { + case NotifyNormal: mode = "NotifyNormal"; break; + case NotifyGrab: mode = "NotifyGrab"; break; + case NotifyUngrab: mode = "NotifyUngrab"; break; + } + + switch (report.xfocus.detail) { + case NotifyAncestor: detail = "NotifyAncestor"; break; + case NotifyVirtual: detail = "NotifyVirtual"; break; + case NotifyInferior: detail = "NotifyInferior"; break; + case NotifyNonlinear: detail = "NotifyNonlinear"; break; + case NotifyNonlinearVirtual: + detail = "NotifyNonlinearVirtual"; break; + case NotifyPointer: detail = "NotifyPointer"; break; + case NotifyPointerRoot: detail = "NotifyPointerRoot"; break; + case NotifyDetailNone: detail = "NotifyDetailNone"; break; + } + printf("focusin\n"); + printf("type : %d\n", report.xfocus.type); + printf("serial : %d\n", report.xfocus.serial); + printf("send_event: %d\n", report.xfocus.send_event); + printf("display : 0x%x\n", report.xfocus.display); + printf("window : 0x%x\n", report.xfocus.window); + printf("mode : %s\n", mode); + printf("detail : %s\n", detail); + printf("---\n"); + break; + case FocusOut: + switch (report.xfocus.mode) { + case NotifyNormal: mode = "NotifyNormal"; break; + case NotifyGrab: mode = "NotifyGrab"; break; + case NotifyUngrab: mode = "NotifyUngrab"; break; + } + + switch (report.xfocus.detail) { + case NotifyAncestor: detail = "NotifyAncestor"; break; + case NotifyVirtual: detail = "NotifyVirtual"; break; + case NotifyInferior: detail = "NotifyInferior"; break; + case NotifyNonlinear: detail = "NotifyNonlinear"; break; + case NotifyNonlinearVirtual: + detail = "NotifyNonlinearVirtual"; break; + case NotifyPointer: detail = "NotifyPointer"; break; + case NotifyPointerRoot: detail = "NotifyPointerRoot"; break; + case NotifyDetailNone: detail = "NotifyDetailNone"; break; + } + printf("focusout\n"); + printf("type : %d\n", report.xfocus.type); + printf("serial : %d\n", report.xfocus.serial); + printf("send_event: %d\n", report.xfocus.send_event); + printf("display : 0x%x\n", report.xfocus.display); + printf("window : 0x%x\n", report.xfocus.window); + printf("mode : %s\n", mode); + printf("detail : %s\n", detail); + printf("---\n"); + break; + case EnterNotify: + switch (report.xcrossing.mode) { + case NotifyNormal: mode = "NotifyNormal"; break; + case NotifyGrab: mode = "NotifyGrab"; break; + case NotifyUngrab: mode = "NotifyUngrab"; break; + } + + switch (report.xcrossing.detail) { + case NotifyAncestor: detail = "NotifyAncestor"; break; + case NotifyVirtual: detail = "NotifyVirtual"; break; + case NotifyInferior: detail = "NotifyInferior"; break; + case NotifyNonlinear: detail = "NotifyNonlinear"; break; + case NotifyNonlinearVirtual: + detail = "NotifyNonlinearVirtual"; break; + case NotifyPointer: detail = "NotifyPointer"; break; + case NotifyPointerRoot: detail = "NotifyPointerRoot"; break; + case NotifyDetailNone: detail = "NotifyDetailNone"; break; + } + printf("enternotify\n"); + printf("type : %d\n", report.xcrossing.type); + printf("serial : %d\n", report.xcrossing.serial); + printf("send_event: %d\n", report.xcrossing.send_event); + printf("display : 0x%x\n", report.xcrossing.display); + printf("window : 0x%x\n", report.xcrossing.window); + printf("mode : %s\n", mode); + printf("detail : %s\n", detail); + printf("---\n"); + break; + case LeaveNotify: + switch (report.xcrossing.mode) { + case NotifyNormal: mode = "NotifyNormal"; break; + case NotifyGrab: mode = "NotifyGrab"; break; + case NotifyUngrab: mode = "NotifyUngrab"; break; + } + + switch (report.xcrossing.detail) { + case NotifyAncestor: detail = "NotifyAncestor"; break; + case NotifyVirtual: detail = "NotifyVirtual"; break; + case NotifyInferior: detail = "NotifyInferior"; break; + case NotifyNonlinear: detail = "NotifyNonlinear"; break; + case NotifyNonlinearVirtual: + detail = "NotifyNonlinearVirtual"; break; + case NotifyPointer: detail = "NotifyPointer"; break; + case NotifyPointerRoot: detail = "NotifyPointerRoot"; break; + case NotifyDetailNone: detail = "NotifyDetailNone"; break; + } + printf("leavenotify\n"); + printf("type : %d\n", report.xcrossing.type); + printf("serial : %d\n", report.xcrossing.serial); + printf("send_event: %d\n", report.xcrossing.send_event); + printf("display : 0x%x\n", report.xcrossing.display); + printf("window : 0x%x\n", report.xcrossing.window); + printf("mode : %s\n", mode); + printf("detail : %s\n", detail); + printf("---\n"); + break; + } + } + + return 1; +} diff --git a/tests/fullscreen.c b/tests/fullscreen.c new file mode 100644 index 0000000..6907cab --- /dev/null +++ b/tests/fullscreen.c @@ -0,0 +1,102 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + fullscreen.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom _net_fs, _net_state; + XEvent msg; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _net_state = XInternAtom(display, "_NET_WM_STATE", False); + _net_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + sleep(2); + + printf("fullscreen\n"); + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _net_state; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2; // toggle + msg.xclient.data.l[1] = _net_fs; + msg.xclient.data.l[2] = 0l; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + sleep(2); + + printf("restore\n"); + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _net_state; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2; // toggle + msg.xclient.data.l[1] = _net_fs; + msg.xclient.data.l[2] = 0l; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/grav.c b/tests/grav.c new file mode 100644 index 0000000..772fec2 --- /dev/null +++ b/tests/grav.c @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grav.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400,b=10; + XSizeHints *hints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, b, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + hints = XAllocSizeHints(); + hints->flags = PWinGravity; + hints->win_gravity = SouthEastGravity; + XSetWMNormalHints(display, win, hints); + XFree(hints); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + w = 600; + h = 160; + XMoveResizeWindow(display, win, 1172-w-b*2, 668-h-b*2, w, h); + XFlush(display); + sleep(1); + XResizeWindow(display, win, 900, 275); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/groupmodal.c b/tests/groupmodal.c new file mode 100644 index 0000000..12057eb --- /dev/null +++ b/tests/groupmodal.c @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grouptran.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window one, two, group; + XEvent report; + XWMHints *wmhints; + Atom state, modal; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True); + + group = XCreateWindow(display, RootWindow(display, 0), + 0,0,1,1, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + one = XCreateWindow(display, RootWindow(display, 0), + 0,0,300,300, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + two = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,one,WhitePixel(display,0)); + XSetWindowBackground(display,two,BlackPixel(display,0)); + + XSetTransientForHint(display, two, RootWindow(display,0)); + XChangeProperty(display, two, state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&modal, 1); + + wmhints = XAllocWMHints(); + + wmhints->flags = WindowGroupHint; + wmhints->window_group = group; + + XSetWMHints(display, one, wmhints); + XSetWMHints(display, two, wmhints); + + XFree(wmhints); + + XMapWindow(display, one); + XFlush(display); + sleep(1); + XMapWindow(display, two); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/grouptran.c b/tests/grouptran.c new file mode 100644 index 0000000..320da2f --- /dev/null +++ b/tests/grouptran.c @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grouptran.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window one, two, group; + XEvent report; + XWMHints *wmhints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + group = XCreateWindow(display, RootWindow(display, 0), + 0,0,1,1, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + one = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + two = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,one,WhitePixel(display,0)); + XSetWindowBackground(display,two,BlackPixel(display,0)); + + XSetTransientForHint(display, two, RootWindow(display,0)); + + wmhints = XAllocWMHints(); + + wmhints->flags = WindowGroupHint; + wmhints->window_group = group; + + XSetWMHints(display, one, wmhints); + XSetWMHints(display, two, wmhints); + + XFree(wmhints); + + XMapWindow(display, one); + XMapWindow(display, two); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/grouptran2.c b/tests/grouptran2.c new file mode 100644 index 0000000..4cd6c58 --- /dev/null +++ b/tests/grouptran2.c @@ -0,0 +1,79 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grouptran2.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window main, grouptran, child, group; + XEvent report; + XWMHints *wmhints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + group = XCreateWindow(display, RootWindow(display, 0), + 0,0,1,1, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + main = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + grouptran = XCreateWindow(display, RootWindow(display, 0), + 10,10,80,180, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child = XCreateWindow(display, RootWindow(display, 0), + 20,20,60,60, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,main,WhitePixel(display,0)); + XSetWindowBackground(display,grouptran,BlackPixel(display,0)); + XSetWindowBackground(display,child,WhitePixel(display,0)); + + XSetTransientForHint(display, grouptran, RootWindow(display,0)); + XSetTransientForHint(display, child, grouptran); + + wmhints = XAllocWMHints(); + + wmhints->flags = WindowGroupHint; + wmhints->window_group = group; + + XSetWMHints(display, main, wmhints); + XSetWMHints(display, grouptran, wmhints); + XSetWMHints(display, child, wmhints); + + XFree(wmhints); + + XMapWindow(display, main); + XMapWindow(display, grouptran); + XMapWindow(display, child); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/grouptrancircular.c b/tests/grouptrancircular.c new file mode 100644 index 0000000..3853aef --- /dev/null +++ b/tests/grouptrancircular.c @@ -0,0 +1,73 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grouptrancircular.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window one, two, group; + XEvent report; + XWMHints *wmhints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + group = XCreateWindow(display, RootWindow(display, 0), + 0,0,1,1, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + one = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + two = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,one,WhitePixel(display,0)); + XSetWindowBackground(display,two,BlackPixel(display,0)); + + XSetTransientForHint(display, one, RootWindow(display,0)); + XSetTransientForHint(display, two, RootWindow(display,0)); + + wmhints = XAllocWMHints(); + + wmhints->flags = WindowGroupHint; + wmhints->window_group = group; + + XSetWMHints(display, one, wmhints); + XSetWMHints(display, two, wmhints); + + XFree(wmhints); + + XMapWindow(display, one); + XMapWindow(display, two); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/grouptrancircular2.c b/tests/grouptrancircular2.c new file mode 100644 index 0000000..1956c8f --- /dev/null +++ b/tests/grouptrancircular2.c @@ -0,0 +1,79 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + grouptrancircular.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window one, two, three, group; + XEvent report; + XWMHints *wmhints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + group = XCreateWindow(display, RootWindow(display, 0), + 0,0,1,1, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + one = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + two = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + three = XCreateWindow(display, RootWindow(display, 0), + 0,0,100,100, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,one,WhitePixel(display,0)); + XSetWindowBackground(display,two,BlackPixel(display,0)); + + XSetTransientForHint(display, one, RootWindow(display,0)); + XSetTransientForHint(display, two, one); + XSetTransientForHint(display, three, two); + + wmhints = XAllocWMHints(); + + wmhints->flags = WindowGroupHint; + wmhints->window_group = group; + + XSetWMHints(display, one, wmhints); + XSetWMHints(display, two, wmhints); + XSetWMHints(display, three, wmhints); + + XFree(wmhints); + + XMapWindow(display, one); + XMapWindow(display, two); + XMapWindow(display, three); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/hideshow.py b/tests/hideshow.py new file mode 100755 index 0000000..2e7fc3a --- /dev/null +++ b/tests/hideshow.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +import pygtk +import gtk +import gobject +pygtk.require('2.0') + +class FolderSelector(gtk.Window): + def __init__(self, jules): + gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) + print "init folder selector", self, jules + self.set_title("Select Folder") + self.jules = jules + + self.set_size_request(140, 200) + + self.list_model = gtk.ListStore(gobject.TYPE_STRING) + self.tree = gtk.TreeView(self.list_model) + self.folder_column = gtk.TreeViewColumn('Folder') + self.tree.append_column(self.folder_column) + + self.folder_cell = gtk.CellRendererText() + self.folder_column.pack_start(self.folder_cell, True) + self.folder_column.add_attribute(self.folder_cell, 'text', 0) + + self.tree.set_search_column(0) + + self.icon_theme = gtk.icon_theme_get_default() + + self.add(self.tree) + self.show_all() + self.tree.columns_autosize() + print "done init" + +class Jules(gtk.Window): + def __init__(self): + gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) + self.set_title("Jules") + self.set_size_request(150, 320) + self.connect("delete_event", self.on_delete_event) + self.connect("destroy", self.on_destroy) + self.scroll = gtk.ScrolledWindow() + + self.tree_model = gtk.TreeStore(gobject.TYPE_STRING, + gobject.TYPE_STRING) + self.tree = gtk.TreeView(self.tree_model) + self.file_column = gtk.TreeViewColumn('name', gtk.CellRendererText(), + markup=0) + self.file_column.set_sort_indicator(True) + self.file_column.set_clickable(True) + self.file_column.set_sort_column_id(1) + self.tree.append_column(self.file_column) + self.tree.set_headers_clickable(True) + self.tree.set_search_column(0) + + self.scroll.add(self.tree) + self.add(self.scroll) + self.show_all() + + self.project_selector = FolderSelector(self) + self.project_selector.hide() + self.project_selector.hide() + + self.project_selector.show() + + def on_delete_event(self, widget, event): + return False + + def on_destroy(self, widget): + gtk.main_quit() + + def run(self): + gtk.main() + + +if __name__ == "__main__": + jules = Jules() + jules.run() diff --git a/tests/iconifydelay.c b/tests/iconifydelay.c new file mode 100644 index 0000000..d0d0573 --- /dev/null +++ b/tests/iconifydelay.c @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + iconifydelay.c for the Openbox window manager + Copyright (c) 2009 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=50,y=50,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, DefaultScreen(display)), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display, win, + WhitePixel(display, DefaultScreen(display))); + + usleep(1000000); + XMapWindow(display, win); + XFlush(display); + usleep(1000000); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = XInternAtom(display, "WM_CHANGE_STATE", False); + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = IconicState; + msg.xclient.data.l[1] = 0; + msg.xclient.data.l[2] = 0; + msg.xclient.data.l[3] = 0; + msg.xclient.data.l[4] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureNotifyMask|SubstructureRedirectMask, &msg); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/icons.c b/tests/icons.c new file mode 100644 index 0000000..e2477c2 --- /dev/null +++ b/tests/icons.c @@ -0,0 +1,251 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + icons.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include <X11/cursorfont.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <glib.h> + +Window findClient(Display *d, Window win) +{ + Window r, *children; + unsigned int n, i; + Atom state = XInternAtom(d, "WM_STATE", True); + Atom ret_type; + int ret_format; + unsigned long ret_items, ret_bytesleft; + unsigned long *prop_return; + + XQueryTree(d, win, &r, &r, &children, &n); + for (i = 0; i < n; ++i) { + Window w = findClient(d, children[i]); + if (w) return w; + } + + // try me + XGetWindowProperty(d, win, state, 0, 1, + False, state, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return); + if (ret_type == None || ret_items < 1) + return None; + return win; // found it! +} + +int main(int argc, char **argv) +{ + Display *d = XOpenDisplay(NULL); + int s = DefaultScreen(d); + Atom net_wm_icon = XInternAtom(d, "_NET_WM_ICON", True); + Atom ret_type; + unsigned int winw = 0, winh = 0; + int ret_format; + unsigned long ret_items, ret_bytesleft; + const int MAX_IMAGES = 10; + unsigned long *prop_return[MAX_IMAGES]; + XImage *i[MAX_IMAGES]; + long offset = 0; + unsigned int image = 0; + unsigned int j; // loop counter + Window id, win; + Pixmap p; + Cursor cur; + XEvent ev; + unsigned int bs = sizeof(long); + + printf("Click on a window with an icon...\n"); + + //int id = strtol(argv[1], NULL, 16); + XUngrabPointer(d, CurrentTime); + cur = XCreateFontCursor(d, XC_crosshair); + XGrabPointer(d, RootWindow(d, s), False, ButtonPressMask, GrabModeAsync, + GrabModeAsync, None, cur, CurrentTime); + while (1) { + XNextEvent(d, &ev); + if (ev.type == ButtonPress) { + XUngrabPointer(d, CurrentTime); + id = findClient(d, ev.xbutton.subwindow); + break; + } + } + + printf("Using window 0x%lx\n", id); + + do { + unsigned int w, h; + + XGetWindowProperty(d, id, net_wm_icon, offset++, 1, + False, XA_CARDINAL, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return[image]); + if (ret_type == None || ret_items < 1) { + printf("No icon found\n"); + return 1; + } + w = prop_return[image][0]; + XFree(prop_return[image]); + + XGetWindowProperty(d, id, net_wm_icon, offset++, 1, + False, XA_CARDINAL, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return[image]); + if (ret_type == None || ret_items < 1) { + printf("Failed to get height\n"); + return 1; + } + h = prop_return[image][0]; + XFree(prop_return[image]); + + XGetWindowProperty(d, id, net_wm_icon, offset, w*h, + False, XA_CARDINAL, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &prop_return[image]); + if (ret_type == None || ret_items < w*h) { + printf("Failed to get image data\n"); + return 1; + } + offset += w*h; + + printf("Found icon with size %dx%d\n", w, h); + + i[image] = XCreateImage(d, DefaultVisual(d, s), DefaultDepth(d, s), + ZPixmap, 0, NULL, w, h, 32, 0); + assert(i[image]); + i[image]->byte_order = LSBFirst; + i[image]->data = (char*)prop_return[image]; + for (j = 0; j < w*h; j++) { + unsigned char alpha = (unsigned char)i[image]->data[j*bs+3]; + unsigned char r = (unsigned char) i[image]->data[j*bs+0]; + unsigned char g = (unsigned char) i[image]->data[j*bs+1]; + unsigned char b = (unsigned char) i[image]->data[j*bs+2]; + + // background color + unsigned char bgr = 0; + unsigned char bgg = 0; + unsigned char bgb = 0; + + r = bgr + (r - bgr) * alpha / 256; + g = bgg + (g - bgg) * alpha / 256; + b = bgb + (b - bgb) * alpha / 256; + + i[image]->data[j*4+0] = (char) r; + i[image]->data[j*4+1] = (char) g; + i[image]->data[j*4+2] = (char) b; + } + + winw += w; + if (h > winh) winh = h; + + ++image; + } while (ret_bytesleft > 0 && image < MAX_IMAGES); + +#define hashsize(n) ((guint32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + + /* hash the images */ + for (j = 0; j < image; ++j) { + unsigned int w, h, length; + guint32 a,b,c; + guint32 initval = 0xf00d; + const guint32 *k = (guint32*)i[j]->data; + + w = i[j]->width; + h = i[j]->height; + length = w * h; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((guint32)length)<<2) + initval; + + /*---------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*--------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------ report the result */ + printf("image[%d] %ux%u %lu\n", j, w, h, c); + } + + win = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, winw, winh, + 0, 0, 0); + assert(win); + XMapWindow(d, win); + + p = XCreatePixmap(d, win, winw, winh, DefaultDepth(d, s)); + XFillRectangle(d, p, DefaultGC(d, s), 0, 0, winw, winh); + + for (j = 0; j < image; ++j) { + static unsigned int x = 0; + + XPutImage(d, p, DefaultGC(d, s), i[j], 0, 0, x, 0, + i[j]->width, i[j]->height); + x += i[j]->width; + XDestroyImage(i[j]); + } + + XSetWindowBackgroundPixmap(d, win, p); + XClearWindow(d, win); + + XFlush(d); + + getchar(); + + XFreePixmap(d, p); + XCloseDisplay(d); +} diff --git a/tests/mapiconic.c b/tests/mapiconic.c new file mode 100644 index 0000000..76455a0 --- /dev/null +++ b/tests/mapiconic.c @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + urgent.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom wm_state; + XEvent msg; + int x=50,y=50,h=100,w=400; + unsigned long state[2]; + XWMHints *hints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state[0] = IconicState; + state[1] = None; + + wm_state = XInternAtom(display, "WM_STATE", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + hints = XAllocWMHints(); + hints->flags = StateHint; + hints->initial_state = IconicState; + XSetWMHints(display, win, hints); + XFree(hints); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/mingrow.c b/tests/mingrow.c new file mode 100644 index 0000000..b132fa1 --- /dev/null +++ b/tests/mingrow.c @@ -0,0 +1,100 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + noresize.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + XSetWindowAttributes xswa; + unsigned long xswamask; + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=100; + XSizeHints size; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + xswa.win_gravity = StaticGravity; + xswamask = CWWinGravity; + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, xswamask, &xswa); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + size.flags = PMinSize | PMaxSize; + size.max_width = 0; + size.min_width = w; + size.max_height = 0; + size.min_height = h; + XSetWMNormalHints(display, win, &size); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + XMapWindow(display, win); + XFlush(display); + + printf("sleeping 4.."); + fflush(stdout); + sleep(1); + printf("3.."); + fflush(stdout); + sleep(1); + printf("2.."); + fflush(stdout); + sleep(1); + printf("1.."); + fflush(stdout); + sleep(1); + printf("\n"); + + size.flags = PMinSize | PMaxSize; + size.max_width = 0; + size.min_width = w*2; + size.max_height = 0; + size.min_height = h*2; + XSetWMNormalHints(display, win, &size); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/modal.c b/tests/modal.c new file mode 100644 index 0000000..edbd5cc --- /dev/null +++ b/tests/modal.c @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + modal.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window parent, child; + XEvent report; + Atom state, modal; + int x=10,y=10,h=400,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True); + + parent = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,parent,WhitePixel(display,0)); + XSetWindowBackground(display,child,BlackPixel(display,0)); + + XSetTransientForHint(display, child, parent); + XChangeProperty(display, child, state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&modal, 1); + + XMapWindow(display, parent); + XMapWindow(display, child); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/modal2.c b/tests/modal2.c new file mode 100644 index 0000000..e7afb9b --- /dev/null +++ b/tests/modal2.c @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + modal2.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window parent, child; + XEvent report; + Atom state, modal; + int x=10,y=10,h=400,w=400; + XEvent ce; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True); + + parent = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,parent,WhitePixel(display,0)); + XSetWindowBackground(display,child,BlackPixel(display,0)); + + XSetTransientForHint(display, child, parent); + + XMapWindow(display, parent); + XMapWindow(display, child); + XFlush(display); + + ce.xclient.type = ClientMessage; + ce.xclient.message_type = state; + ce.xclient.display = display; + ce.xclient.window = child; + ce.xclient.format = 32; + ce.xclient.data.l[0] = 1; + ce.xclient.data.l[1] = modal; + ce.xclient.data.l[2] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureNotifyMask | SubstructureRedirectMask, &ce); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/modal3.c b/tests/modal3.c new file mode 100644 index 0000000..76c8219 --- /dev/null +++ b/tests/modal3.c @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + modal3.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window parent, child; + XEvent report; + Atom state, modal; + int x=10,y=10,h=400,w=400; + XEvent ce; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + modal = XInternAtom(display, "_NET_WM_STATE_MODAL", True); + + parent = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,parent,WhitePixel(display,0)); + XSetWindowBackground(display,child,BlackPixel(display,0)); + + XSetTransientForHint(display, child, parent); + + XMapWindow(display, parent); + XMapWindow(display, child); + XFlush(display); + + ce.xclient.type = ClientMessage; + ce.xclient.message_type = state; + ce.xclient.display = display; + ce.xclient.window = child; + ce.xclient.format = 32; + ce.xclient.data.l[0] = 1; + ce.xclient.data.l[1] = modal; + ce.xclient.data.l[2] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureNotifyMask | SubstructureRedirectMask, &ce); + + ce.xclient.data.l[0] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureNotifyMask | SubstructureRedirectMask, &ce); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/noresize.c b/tests/noresize.c new file mode 100644 index 0000000..c98295b --- /dev/null +++ b/tests/noresize.c @@ -0,0 +1,82 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + noresize.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + XSetWindowAttributes xswa; + unsigned long xswamask; + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400; + XSizeHints size; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + xswa.win_gravity = StaticGravity; + xswamask = CWWinGravity; + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, xswamask, &xswa); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + size.flags = PMinSize | PMaxSize; + size.max_width = 0; + size.min_width = w; + size.max_height = 0; + size.min_height = h; + XSetWMNormalHints(display, win, &size); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + XMapWindow(display, win); + XFlush(display); + + XMoveWindow(display, win, 10, 10); + XMoveWindow(display, win, 10, 10); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/oldfullscreen.c b/tests/oldfullscreen.c new file mode 100644 index 0000000..543e960 --- /dev/null +++ b/tests/oldfullscreen.c @@ -0,0 +1,94 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + oldfullscreen.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <string.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +typedef struct +{ + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} Hints; + +int main (int argc, char **argv) { + Display *display; + Window win; + Window r; + XEvent report; + int x=200,y=200,h=100,w=400,s; + XSizeHints *size; + Hints hints; + Atom prop; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + XGetGeometry(display, RootWindow(display, DefaultScreen(display)), &r, + &x, &y, &w, &h, &s, &s); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + size = XAllocSizeHints(); + size->flags = PPosition; + XSetWMNormalHints(display,win,size); + XFree(size); + + hints.flags = 2; + hints.decorations = 0; + prop = XInternAtom(display, "_MOTIF_WM_HINTS", False); + XChangeProperty(display, win, prop, prop, 32, PropModeReplace, + (unsigned char *)&hints, 5); + + XFlush(display); + XMapWindow(display, win); + + XSelectInput(display, win, StructureNotifyMask | ButtonPressMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case ButtonPress: + XUnmapWindow(display, win); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + s = report.xconfigure.send_event; + printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s); + break; + } + + } + + return 1; +} diff --git a/tests/override.c b/tests/override.c new file mode 100644 index 0000000..44c3a00 --- /dev/null +++ b/tests/override.c @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + override.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> + +int main () { + XSetWindowAttributes xswa; + unsigned long xswamask; + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + xswa.override_redirect = True; + xswamask = CWOverrideRedirect; + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, xswamask, &xswa); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + sleep(1); + XUnmapWindow(display, win); + XFlush(display); + sleep(1); + XMapWindow(display, win); + XFlush(display); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/overrideinputonly.c b/tests/overrideinputonly.c new file mode 100644 index 0000000..0c13ac3 --- /dev/null +++ b/tests/overrideinputonly.c @@ -0,0 +1,58 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + override.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> + +int main (int argc, char *argv[]) { + XSetWindowAttributes xswa; + unsigned long xswamask; + Display *display; + Window win; + XEvent report; + int i,x=0,y=0,h=1,w=1; + + for (i=0; i < argc; i++) { + if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry")) { + XParseGeometry(argv[++i], &x, &y, &w, &h); + } + } + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + xswa.override_redirect = True; + xswamask = CWOverrideRedirect; + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, 0, InputOnly, + CopyFromParent, xswamask, &xswa); + + XMapWindow(display, win); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/positioned.c b/tests/positioned.c new file mode 100644 index 0000000..26f835d --- /dev/null +++ b/tests/positioned.c @@ -0,0 +1,73 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + positioned.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <string.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main (int argc, char **argv) { + Display *display; + Window win; + XEvent report; + int x=200,y=200,h=100,w=400,s; + XSizeHints *size; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + size = XAllocSizeHints(); + size->flags = PPosition; + XSetWMNormalHints(display,win,size); + XFree(size); + + XFlush(display); + XMapWindow(display, win); + + XSelectInput(display, win, StructureNotifyMask | ButtonPressMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case ButtonPress: + XUnmapWindow(display, win); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + s = report.xconfigure.send_event; + printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s); + break; + } + + } + + return 1; +} diff --git a/tests/resize.c b/tests/resize.c new file mode 100644 index 0000000..e3a84bb --- /dev/null +++ b/tests/resize.c @@ -0,0 +1,69 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + resize.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + sleep(5); + XResizeWindow(display, win, 600, 150); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/restack.c b/tests/restack.c new file mode 100644 index 0000000..fe1bcdb --- /dev/null +++ b/tests/restack.c @@ -0,0 +1,139 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + extentsrequest.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom _restack; + XEvent msg; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _restack = XInternAtom(display, "_NET_RESTACK_WINDOW", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + printf("requesting bottom in 3\n"); + sleep(3); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _restack; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = Below; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + + printf("requesting top in 3\n"); + sleep(3); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _restack; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = Above; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + + printf("requesting bottomif in 3\n"); + sleep(3); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _restack; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = BottomIf; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + + printf("requesting topif in 3\n"); + sleep(3); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _restack; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = TopIf; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + + printf("requesting opposite in 3\n"); + sleep(3); + + msg.xclient.type = ClientMessage; + msg.xclient.message_type = _restack; + msg.xclient.display = display; + msg.xclient.window = win; + msg.xclient.format = 32; + msg.xclient.data.l[0] = 2l; + msg.xclient.data.l[1] = 0l; + msg.xclient.data.l[2] = Opposite; + msg.xclient.data.l[3] = 0l; + msg.xclient.data.l[4] = 0l; + XSendEvent(display, RootWindow(display, 0), False, + SubstructureNotifyMask | SubstructureRedirectMask, &msg); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/shape.c b/tests/shape.c new file mode 100644 index 0000000..c3de7da --- /dev/null +++ b/tests/shape.c @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + shape.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/extensions/shape.h> + +int main () { + + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=50,y=50,h=100,w=400; + XWMHints hint; + XRectangle xrect; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + xrect.x = 10; + xrect.y = 10; + xrect.width = w - 20; + xrect.height = h - 20; + XShapeCombineRectangles(display, win, + ShapeBounding, 0, 0, &xrect, 1, + ShapeSet, Unsorted); + + XSetWindowBackground(display,win,BlackPixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/showhide.c b/tests/showhide.c new file mode 100644 index 0000000..d1a63e2 --- /dev/null +++ b/tests/showhide.c @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + showhide.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + XEvent msg; + int x=50,y=50,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + while (1) { + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + XMapWindow(display, win); + XFlush(display); + usleep(1000); + XDestroyWindow(display, win); + XSync(display, False); + + break; + sleep(2); + } + + return 1; +} diff --git a/tests/skiptaskbar.c b/tests/skiptaskbar.c new file mode 100644 index 0000000..bb585c6 --- /dev/null +++ b/tests/skiptaskbar.c @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + skiptaskbar.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom state, skip; + XClassHint classhint; + int x=10,y=10,h=400,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + skip = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", True); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XChangeProperty(display, win, state, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&skip, 1); + + classhint.res_name = "test"; + classhint.res_class = "Test"; + XSetClassHint(display, win, &classhint); + + XMapWindow(display, win); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/skiptaskbar2.c b/tests/skiptaskbar2.c new file mode 100644 index 0000000..3fb2231 --- /dev/null +++ b/tests/skiptaskbar2.c @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + skiptaskbar2.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win; + XEvent report, ce; + Atom state, skip; + int x=10,y=10,h=400,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + state = XInternAtom(display, "_NET_WM_STATE", True); + skip = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", True); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + sleep(1); + + ce.xclient.type = ClientMessage; + ce.xclient.message_type = state; + ce.xclient.display = display; + ce.xclient.window = win; + ce.xclient.format = 32; + ce.xclient.data.l[0] = 1; + ce.xclient.data.l[1] = skip; + ce.xclient.data.l[2] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureNotifyMask | SubstructureRedirectMask, &ce); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/stackabove.c b/tests/stackabove.c new file mode 100644 index 0000000..791690f --- /dev/null +++ b/tests/stackabove.c @@ -0,0 +1,73 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + extentsrequest.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +/* Will find the frame for this window and stack itself right above it */ +#define GO_ABOVE 0x4c0003c + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win, frame, a, p, *c; + unsigned int n; + XWindowChanges changes; + XEvent report; + XEvent msg; + int x=10,y=10,h=100,w=400; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + printf("requesting move in 10\n"); + sleep(10); + + frame = win; + while (XQueryTree(display, frame, &a, &p, &c, &n) && p != a) + frame = p; + + changes.sibling = GO_ABOVE; + while (XQueryTree(display, changes.sibling, &a, &p, &c, &n) && p != a) + changes.sibling = p; + + changes.stack_mode = Above; + XConfigureWindow(display, frame, CWSibling | CWStackMode, &changes); + XFlush(display); + + printf("moved 0x%lx above 0x%lx\n", frame, changes.sibling); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/stacking.c b/tests/stacking.c new file mode 100644 index 0000000..4d2bda9 --- /dev/null +++ b/tests/stacking.c @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + stacking.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window parent, child1, child2, group; + XEvent report; + int x=10,y=10,h=400,w=400; + XWMHints *hints; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + group = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + parent = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child1 = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + child2 = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,parent,WhitePixel(display,0)); + XSetWindowBackground(display,child1,BlackPixel(display,0)); + XSetWindowBackground(display,child2,WhitePixel(display,0)); + + hints = XAllocWMHints(); + hints->flags = WindowGroupHint; + hints->window_group = group; + XSetWMHints(display, parent, hints); + XSetWMHints(display, child1, hints); + XSetWMHints(display, child2, hints); + XFree(hints); + + XSetTransientForHint(display, child1, + RootWindow(display, DefaultScreen(display))); + XSetTransientForHint(display, child2, parent); + + XMapWindow(display, parent); + XFlush(display); + sleep(3); + XMapWindow(display, child1); + XFlush(display); + sleep(3); + XMapWindow(display, child2); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/strut.c b/tests/strut.c new file mode 100644 index 0000000..15a4860 --- /dev/null +++ b/tests/strut.c @@ -0,0 +1,84 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + strut.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom _net_strut; + XEvent msg; + int x=10,y=10,h=100,w=400; + int s[4]; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _net_strut = XInternAtom(display, "_NET_WM_STRUT", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + sleep(2); + + printf("top\n"); + s[0] = 0; s[1] = 0; s[2] = 20; s[3] = 0; + XChangeProperty(display, win, _net_strut, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) s, 4); + XFlush(display); + sleep(2); + + printf("none\n"); + XDeleteProperty(display, win, _net_strut); + XFlush(display); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/title.c b/tests/title.c new file mode 100644 index 0000000..799bdf5 --- /dev/null +++ b/tests/title.c @@ -0,0 +1,86 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + title.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <string.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main (int argc, char **argv) { + Display *display; + Window win; + XEvent report; + int x=10,y=10,h=100,w=400; + XSizeHints size; + XTextProperty name; + Atom nameprop,nameenc; + + if (argc < 2) return 1; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + if (argc > 2) + nameprop = XInternAtom(display,argv[2],False); + else + nameprop = XInternAtom(display,"WM_NAME",False); + if (argc > 3) + nameenc = XInternAtom(display,argv[3],False); + else + nameenc = XInternAtom(display,"STRING",False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + +// XStringListToTextProperty(&argv[1], 1, &name); +// XSetWMName(display, win, &name); + XChangeProperty(display, win, nameprop, nameenc, 8, + PropModeAppend, argv[1], strlen(argv[1])); + + XFlush(display); + XMapWindow(display, win); + + XSelectInput(display, win, ExposureMask | StructureNotifyMask); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/urgent.c b/tests/urgent.c new file mode 100644 index 0000000..c29e300 --- /dev/null +++ b/tests/urgent.c @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + urgent.c for the Openbox window manager + Copyright (c) 2003-2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +int main () { + Display *display; + Window win; + XEvent report; + Atom _net_fs, _net_state; + XEvent msg; + int x=50,y=50,h=100,w=400; + XWMHints hint; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + _net_state = XInternAtom(display, "_NET_WM_STATE", False); + _net_fs = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + sleep(1); + + printf("urgent on\n"); + hint.flags = XUrgencyHint; + XSetWMHints(display, win, &hint); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case Expose: + printf("exposed\n"); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + printf("confignotify %i,%i-%ix%i\n",x,y,w,h); + break; + } + + } + + return 1; +} diff --git a/tests/usertimewin.c b/tests/usertimewin.c new file mode 100644 index 0000000..7a2aa97 --- /dev/null +++ b/tests/usertimewin.c @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + usertimewin.c for the Openbox window manager + Copyright (c) 2007 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +int main () { + Display *display; + Window win, twin; + XEvent report; + Atom atime, atimewin; + int x=10,y=10,h=400,w=400; + Time num; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + atime = XInternAtom(display, "_NET_WM_USER_TIME", True); + atimewin = XInternAtom(display, "_NET_WM_USER_TIME_WINDOW", True); + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + twin = XCreateWindow(display, RootWindow(display, 0), + x, y, w/2, h/2, 10, CopyFromParent, CopyFromParent, + CopyFromParent, 0, 0); + + XSetWindowBackground(display,win,WhitePixel(display,0)); + + XMapWindow(display, win); + XFlush(display); + + sleep(2); + + printf("Setting time window\n"); + XChangeProperty(display, win, atimewin, XA_WINDOW, 32, + PropModeReplace, (unsigned char*)&twin, 1); + XFlush(display); + + sleep(1); + + num = 100; + printf("Setting time stamp on time window\n"); + XChangeProperty(display, twin, atime, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&num, 1); + XFlush(display); + + while (1) { + XNextEvent(display, &report); + } + + return 1; +} diff --git a/tests/wmhints.c b/tests/wmhints.c new file mode 100644 index 0000000..b8ab57b --- /dev/null +++ b/tests/wmhints.c @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + oldfullscreen.c for the Openbox window manager + Copyright (c) 2010 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include <string.h> +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +typedef struct +{ + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} Hints; + +int main (int argc, char **argv) { + Display *display; + Window win; + Window r; + XEvent report; + int x=200,y=200,h=100,w=400,s; + Hints hints; + Atom prop; + + display = XOpenDisplay(NULL); + + if (display == NULL) { + fprintf(stderr, "couldn't connect to X server :0\n"); + return 0; + } + + win = XCreateWindow(display, RootWindow(display, 0), + x, y, w, h, 0, CopyFromParent, CopyFromParent, + CopyFromParent, 0, NULL); + XSetWindowBackground(display,win,WhitePixel(display,0)); + + hints.flags = 2; + hints.decorations = 0; + prop = XInternAtom(display, "_MOTIF_WM_HINTS", False); + XChangeProperty(display, win, prop, prop, 32, PropModeReplace, + (unsigned char *)&hints, 5); + + XSelectInput(display, win, StructureNotifyMask | ButtonPressMask); + + XMapWindow(display, win); + XFlush(display); + + sleep(1); + + hints.flags = 2; + hints.decorations = 1<<3 || 1 << 1; /* border and title */ + prop = XInternAtom(display, "_MOTIF_WM_HINTS", False); + XChangeProperty(display, win, prop, prop, 32, PropModeReplace, + (unsigned char *)&hints, 5); + + XMapWindow(display, win); + XFlush(display); + + + while (1) { + XNextEvent(display, &report); + + switch (report.type) { + case ButtonPress: + XUnmapWindow(display, win); + break; + case ConfigureNotify: + x = report.xconfigure.x; + y = report.xconfigure.y; + w = report.xconfigure.width; + h = report.xconfigure.height; + s = report.xconfigure.send_event; + printf("confignotify %i,%i-%ix%i (send: %d)\n",x,y,w,h,s); + break; + } + + } + + return 1; +} diff --git a/themes/Artwiz-boxed/openbox-3/themerc b/themes/Artwiz-boxed/openbox-3/themerc new file mode 100644 index 0000000..890c98d --- /dev/null +++ b/themes/Artwiz-boxed/openbox-3/themerc @@ -0,0 +1,102 @@ +menu.title.bg: raised gradient diagonal +menu.title.bg.color: rgb:90/94/98 +menu.title.bg.colorTo: rgb:20/24/28 +menu.title.text.color: white +menu.title.text.justify: center + +menu.items.bg: sunken gradient diagonal +menu.items.bg.color: rgb:10/20/30 +menu.items.bg.colorTo: rgb:70/80/90 +menu.items.text.color: rgb:90/a0/b0 +menu.items.justify: center + +menu.items.active.bg: raised gradient diagonal +menu.items.active.bg.color: rgb:90/94/98 +menu.items.active.bg.colorTo: rgb:20/24/28 +menu.items.active.text.color: white +menu.bullet.image.color: rgb:90/a0/b0 +menu.bullet.selected.image.color: #ffffff + +window.active.title.bg: raised gradient vertical +window.active.title.bg.color: rgb:80/84/88 +window.active.title.bg.colorTo: rgb:30/34/38 +window.inactive.title.bg: raised vertical gradient +window.inactive.title.bg.color: rgb:50/54/58 +window.inactive.title.bg.colorTo: black + +window.active.label.bg: sunken diagonal gradient +window.active.label.bg.color: rgb:10/20/30 +window.active.label.bg.colorTo: rgb:70/80/90 +window.active.label.text.color: white +window.inactive.label.bg: sunken gradient diagonal +window.inactive.label.bg.color: black +window.inactive.label.bg.colorTo: rgb:40/50/60 +window.inactive.label.text.color: rgb:60/64/68 +window.label.text.justify: center + +window.active.button.unpressed.bg: raised gradient diagonal +window.active.button.unpressed.bg.color: rgb:90/94/98 +window.active.button.unpressed.bg.colorTo: rgb:20/24/28 +window.active.button.unpressed.image.color: white + +window.inactive.button.unpressed.bg: raised gradient diagonal +window.inactive.button.unpressed.bg.color: rgb:50/54/58 +window.inactive.button.unpressed.bg.colorTo: black +window.inactive.button.unpressed.image.color: rgb:70/74/78 + +window.active.button.pressed.bg: sunken gradient diagonal +window.active.button.pressed.bg.color: rgb:20/40/50 +window.active.button.pressed.bg.colorTo: rgb:60/70/80 + +window.inactive.button.pressed.bg: sunken gradient diagonal +window.inactive.button.pressed.bg.color: rgb:50/54/58 +window.inactive.button.pressed.bg.colorTo: black +window.inactive.button.pressed.image.color: rgb:70/74/78 + +window.active.client.color: rgb:40/44/48 +window.inactive.client.color: rgb:20/24/28 + +window.active.handle.bg: raised gradient diagonal +window.active.handle.bg.color: rgb:70/74/78 +window.active.handle.bg.colorTo: rgb:40/44/48 + +window.inactive.handle.bg: raised gradient diagonal +window.inactive.handle.bg.color: rgb:50/54/58 +window.inactive.handle.bg.colorTo: black + +window.active.grip.bg: sunken diagonal gradient +window.active.grip.bg.color: rgb:20/30/40 +window.active.grip.bg.colorTo: rgb:60/70/80 + +window.inactive.grip.bg: sunken diagonal gradient +window.inactive.grip.bg.color: black +window.inactive.grip.bg.colorTo: rgb:30/40/50 + +window.active.button.toggled.bg: raised gradient diagonal +window.active.button.toggled.bg.color: rgb:90/94/98 +window.active.button.toggled.bg.colorTo: rgb:20/24/28 +window.active.button.toggled.image.color: white + +window.inactive.button.toggled.bg: raised gradient diagonal +window.inactive.button.toggled.bg.color: rgb:50/54/58 +window.inactive.button.toggled.bg.colorTo: black +window.inactive.button.toggled.image.color: rgb:70/74/78 + +window.active.button.disabled.bg: raised gradient diagonal +window.active.button.disabled.bg.color: rgb:90/94/98 +window.active.button.disabled.bg.colorTo: rgb:20/24/28 +window.active.button.disabled.image.color: grey + +window.inactive.button.disabled.bg: raised gradient diagonal +window.inactive.button.disabled.bg.color: rgb:50/54/58 +window.inactive.button.disabled.bg.colorTo: black +window.inactive.button.disabled.image.color: rgb:70/74/78 + +border.color: black +padding.width: 1 +borderWidth: 1 +window.handle.width: 4 + +window.active.label.text.font: +menu.title.text.font: +menu.items.font: diff --git a/themes/Bear2/openbox-3/close.xbm b/themes/Bear2/openbox-3/close.xbm new file mode 100644 index 0000000..ab7ff80 --- /dev/null +++ b/themes/Bear2/openbox-3/close.xbm @@ -0,0 +1,4 @@ +#define close_width 8 +#define close_height 8 +static unsigned char close_bits[] = { + 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3 }; diff --git a/themes/Bear2/openbox-3/close_pressed.xbm b/themes/Bear2/openbox-3/close_pressed.xbm new file mode 100644 index 0000000..663c458 --- /dev/null +++ b/themes/Bear2/openbox-3/close_pressed.xbm @@ -0,0 +1,5 @@ +#define close_pressed_width 10 +#define close_pressed_height 10 +static unsigned char close_pressed_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x03, 0x9c, 0x03, 0xf8, 0x01, 0xf0, 0x00, + 0xf0, 0x00, 0xf8, 0x01, 0x9c, 0x03, 0x0c, 0x03 }; diff --git a/themes/Bear2/openbox-3/desk.xbm b/themes/Bear2/openbox-3/desk.xbm new file mode 100644 index 0000000..f6e24ce --- /dev/null +++ b/themes/Bear2/openbox-3/desk.xbm @@ -0,0 +1,4 @@ +#define desk_width 8 +#define desk_height 8 +static unsigned char desk_bits[] = { + 0x00, 0x00, 0x48, 0x78, 0x7f, 0x78, 0x48, 0x00 }; diff --git a/themes/Bear2/openbox-3/desk_toggled.xbm b/themes/Bear2/openbox-3/desk_toggled.xbm new file mode 100644 index 0000000..d89d4bb --- /dev/null +++ b/themes/Bear2/openbox-3/desk_toggled.xbm @@ -0,0 +1,4 @@ +#define desk_toggled_width 8 +#define desk_toggled_height 8 +static unsigned char desk_toggled_bits[] = { + 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 }; diff --git a/themes/Bear2/openbox-3/iconify.xbm b/themes/Bear2/openbox-3/iconify.xbm new file mode 100644 index 0000000..0f415e0 --- /dev/null +++ b/themes/Bear2/openbox-3/iconify.xbm @@ -0,0 +1,4 @@ +#define iconify_width 8 +#define iconify_height 8 +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }; diff --git a/themes/Bear2/openbox-3/iconify_pressed.xbm b/themes/Bear2/openbox-3/iconify_pressed.xbm new file mode 100644 index 0000000..28a9cab --- /dev/null +++ b/themes/Bear2/openbox-3/iconify_pressed.xbm @@ -0,0 +1,5 @@ +#define iconify_pressed_width 10 +#define iconify_pressed_height 10 +static unsigned char iconify_pressed_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03 }; diff --git a/themes/Bear2/openbox-3/max.xbm b/themes/Bear2/openbox-3/max.xbm new file mode 100644 index 0000000..3a13089 --- /dev/null +++ b/themes/Bear2/openbox-3/max.xbm @@ -0,0 +1,4 @@ +#define max_width 8 +#define max_height 8 +static unsigned char max_bits[] = { + 0xff, 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff }; diff --git a/themes/Bear2/openbox-3/max_pressed.xbm b/themes/Bear2/openbox-3/max_pressed.xbm new file mode 100644 index 0000000..5116ae3 --- /dev/null +++ b/themes/Bear2/openbox-3/max_pressed.xbm @@ -0,0 +1,5 @@ +#define max_pressed_width 10 +#define max_pressed_height 10 +static unsigned char max_pressed_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03, 0x04, 0x02, 0x04, 0x02, + 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0xfc, 0x03 }; diff --git a/themes/Bear2/openbox-3/max_toggled.xbm b/themes/Bear2/openbox-3/max_toggled.xbm new file mode 100644 index 0000000..ff1c954 --- /dev/null +++ b/themes/Bear2/openbox-3/max_toggled.xbm @@ -0,0 +1,4 @@ +#define max_toggled_width 8 +#define max_toggled_height 8 +static unsigned char max_toggled_bits[] = { + 0xfc, 0xfc, 0x84, 0x9f, 0x91, 0xf1, 0x11, 0x1f }; diff --git a/themes/Bear2/openbox-3/shade.xbm b/themes/Bear2/openbox-3/shade.xbm new file mode 100644 index 0000000..ff774f8 --- /dev/null +++ b/themes/Bear2/openbox-3/shade.xbm @@ -0,0 +1,4 @@ +#define shade_width 8 +#define shade_height 8 +static unsigned char shade_bits[] = { + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/themes/Bear2/openbox-3/shade_pressed.xbm b/themes/Bear2/openbox-3/shade_pressed.xbm new file mode 100644 index 0000000..d65fe7a --- /dev/null +++ b/themes/Bear2/openbox-3/shade_pressed.xbm @@ -0,0 +1,5 @@ +#define shade_pressed_width 10 +#define shade_pressed_height 10 +static unsigned char shade_pressed_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/themes/Bear2/openbox-3/themerc b/themes/Bear2/openbox-3/themerc new file mode 100644 index 0000000..b0db53e --- /dev/null +++ b/themes/Bear2/openbox-3/themerc @@ -0,0 +1,83 @@ +!! Bear2 by David Barr <david@chalkskeletons.com> +!! http://david.chalkskeletons.com +!! another blue theme + +!!General + +window.handle.width: 4 +window.client.padding.width: 0 +window.client.padding.height: 0 +border.Width: 1 +padding.width: 3 +menu.overlap: 2 +border.color: #4e4e4e +window.frameColor: #eeeeec +window.*.client.color: #eeeeec +*.text.justify: center + +!!Fonts + +window.active.label.text.font: shadow=y:shadowoffset=1:shadowtint=3 +window.inactive.label.text.font: shadow=y:shadowoffset=1:shadowtint=0 +menu.items.font: +menu.title.text.font: shadow=y:shadowoffset=1:shadowtint=30 + +!!Menu +menu.border.color: #9d9d9d +menu.title.bg: flat border vertical gradient +menu.title.bg.color: #3465A4 +menu.title.bg.colorTo: #407CCA +menu.title.bg.border.color: #729fcf +menu.title.text.color: #ffffff + +menu.items.bg: flat border solid +menu.items.bg.color: #eeeeec +menu.items.bg.border.color: #EFEBE7 + +menu.items.text.color: #444444 +menu.items.disabled.text.color: #babdb6 + +menu.items.active.bg: flat solid +menu.items.active.bg.color: #4481c0 +menu.items.active.bg.colorTo: #4175aa +menu.items.active.text.color: #f6f8fb +menu.items.active.bg.border.color: #416c98 + +!!Active + +window.active.title.bg: flat border gradient osx +window.active.title.bg.color: #3465A4 +window.active.title.bg.colorTo: #407CCA +window.active.title.bg.border.color: #699acd + +window.active.label.bg: parentrelative +window.active.label.text.color: #ffffff + +window.active.button.*.bg: parentrelative +window.active.button.*.image.color: #efefef +window.active.button.hover.bg.color: #729fcf +window.active.button.hover.image.color: #ffffff +window.active.button.pressed.bg.color: #a7cef2 +window.active.button.disabled.image.color: #6d8fbc + +window.active.handle.bg: raised solid +window.active.handle.bg.color: #E7e7e7 +window.*.grip.bg: parentrelative + +!!Inactive + +window.inactive.title.bg: flat border gradient osx +window.inactive.title.bg.color: #dcdcdc +window.inactive.title.bg.colorTo: #eeeeec +window.inactive.title.bg.border.color: #efefef +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #888a85 + +window.inactive.button.*.bg: parentrelative +window.inactive.button.*.image.color: #888a85 +window.inactive.button.pressed.bg.color: #d3d7cf +window.inactive.button.pressed.bg.color: #4f5051 +window.inactive.button.disabled.image.color: #d0d0d0 + +window.inactive.handle.bg: raised solid +window.inactive.handle.bg.color: #E7e7e7 diff --git a/themes/Clearlooks-3.4/openbox-3/themerc b/themes/Clearlooks-3.4/openbox-3/themerc new file mode 100644 index 0000000..05e8922 --- /dev/null +++ b/themes/Clearlooks-3.4/openbox-3/themerc @@ -0,0 +1,126 @@ +# Name: Clearlooks +# Author: John McKnight <jmcknight@gmail.com> +# Note: This is a port of the Clearlooks metacity theme to Openbox. + +### Menu +#menu.border.color: #b5aa99 + +menu.title.bg: Flat Border Gradient Vertical +menu.title.bg.color: #589bda +menu.title.bg.colorTo: #3c7cb7 +menu.title.bg.border.color: #7cb6ec +menu.title.text.color: #ffffff +menu.title.text.justify: Center + +menu.items.bg: Flat Solid +menu.items.bg.color: #fcfbfa +menu.items.text.color: #000000 +menu.items.disable.text.color: #b5b3ac + +menu.items.active.bg: Flat Gradient Vertical +menu.items.active.bg.color: #5c9edb +menu.items.active.bg.colorTo: #4489ca +menu.items.active.text.color: #ffffff + + +### Window active +window.active.title.bg: Flat Border Gradient Vertical +window.active.title.bg.color: #589bda +window.active.title.bg.colorTo: #3c7cb7 +window.active.title.bg.border.color: #7cb6ec +window.active.title.separator.color: #334c62 + +window.active.label.bg: Parentrelative +window.active.label.text.color: #ffffff + +window.active.handle.bg: Flat Border Solid +window.active.handle.bg.color: #3c7cb7 +window.active.handle.bg.border.color: #7cb6ec + +window.active.grip.bg: Flat Border Solid +window.active.grip.bg.color: #3c7cb7 +window.active.grip.bg.border.color: #7cb6ec + +window.active.button.unpressed.bg: Flat Border Gradient Vertical +window.active.button.unpressed.bg.color: #5ea0dd +window.active.button.unpressed.bg.colorTo: #3f85c5 +window.active.button.unpressed.bg.border.color: #36536f +window.active.button.unpressed.image.color: #ffffff + +window.active.button.pressed.bg: Flat Border Gradient Vertical +window.active.button.pressed.bg.color: #3c82c3 +window.active.button.pressed.bg.colorTo: #3c7ab5 +window.active.button.pressed.bg.border.color: #36536f +window.active.button.pressed.image.color: #ffffff + +window.active.button.disabled.bg: Flat Border Gradient Vertical +window.active.button.disabled.bg.color: #3c82c3 +window.active.button.disabled.bg.colorTo: #3c7ab5 +window.active.button.disabled.bg.border.color: #36536f +window.active.button.disabled.image.color: #36536f + +window.active.button.toggled.bg: Flat Border Gradient Vertical +window.active.button.toggled.bg.color: #5ea0dd +window.active.button.toggled.bg.colorTo: #3f85c5 +window.active.button.toggled.bg.border.color: #36536f +window.active.button.toggled.image.color: #dcd4c9 + + +### Window inactive +window.inactive.border.color: #3d3a37 + +window.inactive.title.bg: Flat Border Gradient Vertical +window.inactive.title.bg.color: #efece6 +window.inactive.title.bg.colorTo: #d9d2c7 +window.inactive.title.bg.border.color: #ffffff +window.inactive.title.separator.color: #9a8e7c + +window.inactive.label.bg: Parentrelative +window.inactive.label.text.color: #000000 + +window.inactive.handle.bg: Flat Border Solid +window.inactive.handle.bg.color: #d9d2c7 +window.inactive.handle.bg.border.color: #ffffff + +window.inactive.grip.bg: Flat Border Solid +window.inactive.grip.bg.color: #d9d2c7 +window.inactive.grip.bg.border.color: #ffffff + +window.inactive.button.unpressed.bg: Flat Border Gradient Vertical +window.inactive.button.unpressed.bg.color: #ede9e3 +window.inactive.button.unpressed.bg.colorTo: #dbd5ca +window.inactive.button.unpressed.bg.border.color: #8f8370 +window.inactive.button.unpressed.image.color: #000000 + +window.inactive.button.pressed.bg: Flat Border Gradient Vertical +window.inactive.button.pressed.bg.color: #ede9e3 +window.inactive.button.pressed.bg.colorTo: #dbd5ca +window.inactive.button.pressed.bg.border.color: #8f8370 +window.inactive.button.pressed.image.color: #000000 + +window.inactive.button.disabled.bg: Flat Border Gradient Vertical +window.inactive.button.disabled.bg.color: #ede9e3 +window.inactive.button.disabled.bg.colorTo: #dbd5ca +window.inactive.button.disabled.bg.border.color: #8f8370 +window.inactive.button.disabled.image.color: #000000 + +window.inactive.button.toggled.bg: Flat Border Gradient Vertical +window.inactive.button.toggled.bg.color: #ede9e3 +window.inactive.button.toggled.bg.colorTo: #dbd5ca +window.inactive.button.toggled.bg.border.color: #8f8370 +window.inactive.button.toggled.image.color: #000000 + + +### Everything else +border.width: 1 +padding.width: 2 +window.handle.width: 4 +window.client.padding.width: 0 +border.color: #1f252b +menu.overlap: 0 + +### Fonts +window.active.label.text.font: +window.inactive.label.text.font: +menu.items.font: +menu.title.text.font: diff --git a/themes/Clearlooks-Olive/openbox-3/themerc b/themes/Clearlooks-Olive/openbox-3/themerc new file mode 100644 index 0000000..249f0e6 --- /dev/null +++ b/themes/Clearlooks-Olive/openbox-3/themerc @@ -0,0 +1,121 @@ +# Name: Clearlooks-Olive +# Author: John McKnight <jmcknight@gmail.com> +# Note: This is a port of the Clearlooks-Olive metacity theme to Openbox. + +# Menu settings +menu.title.bg: Raised Gradient Vertical +menu.title.bg.color: #95ad70 +menu.title.bg.colorTo: #7d925d +menu.title.text.color: #ffffff +menu.title.text.justify: Left + +menu.items.bg: Flat Solid +menu.items.bg.color: #f1eee0 +menu.items.text.color: #444444 +menu.items.disabled.text.color: #aaaaaa +menu.items.active.disabled.text.color: #969696 + +menu.items.active.bg: Flat Gradient Vertical Border +menu.items.active.bg.color: #b4c994 +menu.items.active.bg.colorTo: #92aa6d +menu.items.active.bg.border.color: #92aa6d +menu.items.active.text.color: #ffffff + + +# Window settings (focused) +window.active.title.bg: Raised Gradient Vertical +window.active.title.bg.color: #95ad70 +window.active.title.bg.colorTo: #7d925d + +window.active.label.bg: Parentrelative +window.active.label.text.color: #ffffff + +window.active.handle.bg: Raised Gradient Vertical +window.active.handle.bg.color: #95ad70 +window.active.handle.bg.colorTo: #7d925d + +window.active.grip.bg: Raised Gradient Vertical +window.active.grip.bg.color: #95ad70 +window.active.grip.bg.colorTo: #7d925d + +window.active.button.unpressed.bg: Flat Gradient Vertical Border +window.active.button.unpressed.bg.color: #65744e +window.active.button.unpressed.bg.colorTo: #5b6846 +window.active.button.unpressed.bg.border.color: #abbc91 +window.active.button.unpressed.image.color: #ffffff + +window.active.button.pressed.bg: Flat Gradient Vertical Border +window.active.button.pressed.bg.color: #373d2b +window.active.button.pressed.bg.colorTo: #333828 +window.active.button.pressed.bg.border.color: #abbc91 +window.active.button.pressed.image.color: #ffffff + +window.active.button.disabled.bg: Flat Gradient Vertical Border +window.active.button.disabled.bg.color: #373d2b +window.active.button.disabled.bg.colorTo: #333828 +window.active.button.disabled.bg.border.color: #abbc91 +window.active.button.disabled.image.color: #abbc91 + +window.active.button.toggled.bg: Flat Gradient Vertical Border +window.active.button.toggled.bg.color: #65744e +window.active.button.toggled.bg.colorTo: #5b6846 +window.active.button.toggled.bg.border.color: #abbc91 +window.active.button.toggled.image.color: #b9b7a7 + + +# Window settings (unfocused) +window.inactive.title.bg: Raised Gradient Vertical +window.inactive.title.bg.color: #f1eeea +window.inactive.title.bg.colorTo: #d8cfc7 + +window.inactive.label.bg: Parentrelative +window.inactive.label.text.color: #000000 + +window.inactive.handle.bg: Raised Gradient Vertical +window.inactive.handle.bg.color: #f1eeea +window.inactive.handle.bg.colorTo: #d8cfc7 + +window.inactive.grip.bg: Raised Gradient Vertical +window.inactive.grip.bg.color: #f1eeea +window.inactive.grip.bg.colorTo: #d8cfc7 + +window.inactive.button.unpressed.bg: Flat Gradient Vertical Border +window.inactive.button.unpressed.bg.color: #efebe7 +window.inactive.button.unpressed.bg.colorTo: #ddd6ce +window.inactive.button.unpressed.bg.border.color: #8f8173 +window.inactive.button.unpressed.image.color: #000000 + +window.inactive.button.pressed.bg: Flat Gradient Vertical Border +window.inactive.button.pressed.bg.color: #efebe7 +window.inactive.button.pressed.bg.colorTo: #ddd6ce +window.inactive.button.pressed.bg.border.color: #8f8173 +window.inactive.button.pressed.image.color: #000000 + +window.inactive.button.disabled.bg: Flat Gradient Vertical Border +window.inactive.button.disabled.bg.color: #efebe7 +window.inactive.button.disabled.bg.colorTo: #ddd6ce +window.inactive.button.disabled.bg.border.color: #8f8173 +window.inactive.button.disabled.image.color: #8f8173 + +window.inactive.button.toggled.bg: Flat Gradient Vertical Border +window.inactive.button.toggled.bg.color: #efebe7 +window.inactive.button.toggled.bg.colorTo: #ddd6ce +window.inactive.button.toggled.bg.border.color: #8f8173 +window.inactive.button.toggled.image.color: #000000 + + +### Everything else +border.width: 1 +padding.width: 2 +window.handle.width: 3 +window.client.padding.width: 0 +window.client.padding.height: 0 +border.color: #000000 +menu.overlap: 0 + + +### Fonts +window.active.label.text.font:shadow=y:shadowtint=70:shadowoffset=1 +window.inactive.label.text.font:shadow=y:shadowtint=20:shadowoffset=1 +menu.items.font: +menu.title.text.font:shadow=y:shadowtint=70 diff --git a/themes/Clearlooks/openbox-3/themerc b/themes/Clearlooks/openbox-3/themerc new file mode 100644 index 0000000..f81c428 --- /dev/null +++ b/themes/Clearlooks/openbox-3/themerc @@ -0,0 +1,164 @@ +!# Clearlooks-Evolving +!# Clearlooks as it evolves in gnome-git... +!# Last updated 09/03/10 + +# Fonts +# these are really halos, but who cares? + +*.font: shadow=n +window.active.label.text.font:shadow=y:shadowtint=30:shadowoffset=1 +window.inactive.label.text.font:shadow=y:shadowtint=00:shadowoffset=0 +menu.items.font:shadow=y:shadowtint=0:shadowoffset=1 + +!# general stuff + +border.width: 1 +padding.width: 3 +padding.height: 2 +window.handle.width: 3 +window.client.padding.width: 0 +menu.overlap: 2 +*.justify: center + +!# lets set our damn shadows here, eh? + +*.bg.highlight: 50 +*.bg.shadow: 05 + +window.active.title.bg.highlight: 35 +window.active.title.bg.shadow: 05 + +window.inactive.title.bg.highlight: 30 +window.inactive.title.bg.shadow: 05 + +window.*.grip.bg.highlight: 50 +window.*.grip.bg.shadow: 30 + +window.*.handle.bg.highlight: 50 +window.*.handle.bg.shadow: 30 + +!# Menu settings + +menu.border.color: #aaaaaa +menu.border.width: 1 + +menu.title.bg: solid flat +menu.title.bg.color: #E6E7E6 +menu.title.text.color: #111111 + +menu.items.bg: Flat Solid +menu.items.bg.color: #ffffff +menu.items.text.color: #111111 +menu.items.disabled.text.color: #aaaaaa + +menu.items.active.bg: Flat Gradient splitvertical border + +menu.items.active.bg.color: #97b8e2 +menu.items.active.bg.color.splitTo: #a8c5e9 + +menu.items.active.bg.colorTo: #91b3de +menu.items.active.bg.colorTo.splitTo: #80a7d6 +menu.items.active.bg.border.color: #4b6e99 +menu.items.active.text.color: #ffffff + +menu.separator.width: 1 +menu.separator.padding.width: 0 +menu.separator.padding.height: 3 +menu.separator.color: #aaaaaa + +!# set handles here and only the once? + +window.*.handle.bg: Raised solid +window.*.handle.bg.color: #eaebec + +window.*.grip.bg: Raised solid +window.*.grip.bg.color: #eaebec + +!# Active + +window.*.border.color: #585a5d + +window.active.title.separator.color: #4e76a8 + +*.title.bg: Raised Gradient splitvertical +*.title.bg.color: #8CB0DC +*.title.bg.color.splitTo: #99BAE3 +*.title.bg.colorTo: #86ABD9 +*.title.bg.colorTo.splitTo: #7AA1D1 + +window.active.label.bg: Parentrelative +window.active.label.text.color: #ffffff + +window.active.button.*.bg: Flat Gradient splitvertical Border + +window.active.button.*.bg.color: #92B4DF +window.active.button.*.bg.color.splitTo: #B0CAEB +window.active.button.*.bg.colorTo: #86ABD9 +window.active.button.*.bg.colorTo.splitTo: #769FD0 + +window.active.button.*.bg.border.color: #49678B +window.active.button.*.image.color: #F4F5F6 + +window.active.button.hover.bg.color: #b5d3ef +window.active.button.hover.bg.color.splitTo: #b5d3ef +window.active.button.hover.bg.colorTo: #9cbae7 +window.active.button.hover.bg.colorTo.splitTo: #8caede +window.active.button.hover.bg.border.color: #4A658C +window.active.button.hover.image.color: #ffffff + +window.active.button.pressed.bg: Flat solid Border +window.active.button.pressed.bg.color: #7aa1d2 + +window.active.button.hover.bg.border.color: #4A658C + +!# inactive + +!#window.inactive.border.color: #7e8285 +window.inactive.title.separator.color: #96999d + +window.inactive.title.bg: Raised Gradient splitvertical +window.inactive.title.bg.color: #E3E2E0 +window.inactive.title.bg.color.splitTo: #EBEAE9 +window.inactive.title.bg.colorTo: #DEDCDA +window.inactive.title.bg.colorTo.splitTo: #D5D3D1 + +window.inactive.label.bg: Parentrelative +window.inactive.label.text.color: #70747d + +window.inactive.button.*.bg: Flat Gradient splitVertical Border +window.inactive.button.*.bg.color: #ffffff +window.inactive.button.*.bg.color.splitto: #ffffff +window.inactive.button.*.bg.colorTo: #F9F8F8 +window.inactive.button.*.bg.colorTo.splitto: #E9E7E6 +window.inactive.button.*.bg.border.color: #928F8B +window.inactive.button.*.image.color: #6D6C6C + + +!# osd (pop ups and what not, dock?) + +osd.border.width: 1 +osd.border.color: #aaaaaa + +osd.bg: flat border gradient splitvertical +osd.bg.color: #F0EFEE +osd.bg.color.splitto: #f5f5f4 +osd.bg.colorTo: #EAEBEC +osd.bg.colorTo.splitto: #E7E5E4 + +osd.bg.border.color: #ffffff + +osd.active.label.bg: parentrelative +osd.active.label.bg.color: #efefef +osd.active.label.bg.border.color: #9c9e9c +osd.active.label.text.color: #444 + +osd.inactive.label.bg: parentrelative +osd.inactive.label.text.color: #70747d + +!# yeah whatever, this is fine anyhoo? +osd.hilight.bg: flat vertical gradient +osd.hilight.bg.color: #9ebde5 +osd.hilight.bg.colorTo: #749dcf +osd.unhilight.bg: flat vertical gradient +osd.unhilight.bg.color: #BABDB6 +osd.unhilight.bg.colorTo: #efefef diff --git a/themes/Makefile b/themes/Makefile new file mode 100644 index 0000000..b90edac --- /dev/null +++ b/themes/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C .. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/themes/Mikachu/openbox-3/bullet.xbm b/themes/Mikachu/openbox-3/bullet.xbm new file mode 100644 index 0000000..88481ec --- /dev/null +++ b/themes/Mikachu/openbox-3/bullet.xbm @@ -0,0 +1,4 @@ +#define bullet_width 4 +#define bullet_height 7 +static unsigned char bullet_bits[] = { + 0x01, 0x03, 0x07, 0x0f, 0x07, 0x03, 0x01 }; diff --git a/themes/Mikachu/openbox-3/close.xbm b/themes/Mikachu/openbox-3/close.xbm new file mode 100644 index 0000000..aea5488 --- /dev/null +++ b/themes/Mikachu/openbox-3/close.xbm @@ -0,0 +1,5 @@ +/* Created with The GIMP */ +#define close_width 8 +#define close_height 8 +static unsigned char close_bits[] = { + 0x00, 0xc3, 0x66, 0x3c, 0x3c, 0x66, 0xc3, 0x00 }; diff --git a/themes/Mikachu/openbox-3/desk.xbm b/themes/Mikachu/openbox-3/desk.xbm new file mode 100644 index 0000000..4345dfa --- /dev/null +++ b/themes/Mikachu/openbox-3/desk.xbm @@ -0,0 +1,5 @@ +/* Created with The GIMP */ +#define stick_width 8 +#define stick_height 8 +static unsigned char stick_bits[] = { + 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 }; diff --git a/themes/Mikachu/openbox-3/iconify.xbm b/themes/Mikachu/openbox-3/iconify.xbm new file mode 100644 index 0000000..1cd40fc --- /dev/null +++ b/themes/Mikachu/openbox-3/iconify.xbm @@ -0,0 +1,5 @@ +/* Created with The GIMP */ +#define icon_width 8 +#define icon_height 8 +static unsigned char icon_bits[] = { + 0x00, 0x00, 0x42, 0x66, 0x3c, 0x18, 0x00, 0x00 }; diff --git a/themes/Mikachu/openbox-3/max.xbm b/themes/Mikachu/openbox-3/max.xbm new file mode 100644 index 0000000..2cd94e8 --- /dev/null +++ b/themes/Mikachu/openbox-3/max.xbm @@ -0,0 +1,5 @@ +/* Created with The GIMP */ +#define max_width 8 +#define max_height 8 +static unsigned char max_bits[] = { + 0x00, 0x00, 0x18, 0x3c, 0x66, 0x42, 0x00, 0x00 }; diff --git a/themes/Mikachu/openbox-3/themerc b/themes/Mikachu/openbox-3/themerc new file mode 100644 index 0000000..ae7e56b --- /dev/null +++ b/themes/Mikachu/openbox-3/themerc @@ -0,0 +1,174 @@ +!! Menu settings + +menu.title.bg: raised gradient crossdiagonal bevel1 +menu.title.bg.color: #6699CC +menu.title.bg.colorTo: #334866 +menu.title.bg.border.color: #000000 +menu.title.text.color: #CCCCFF +menu.title.text.justify: center + +menu.items.bg: flat gradient vertical bevel1 +menu.items.bg.color: #B8B8D8 +menu.items.bg.colorTo: #A0A0BA +menu.items.bg.border.color: #000000 +menu.items.text.color: #000022 +menu.items.disabled.text.color: #711 + +menu.items.active.bg: raised gradient mirrorhorizontal +menu.items.active.bg.color: #555577 +menu.items.active.bg.colorTo: #6699CC +menu.items.active.bg.border.color: #000000 +menu.items.active.text.color: #CCCCFF + +menu.frame.justify: left + +!! General window settings +window.label.text.justify: left + +!! focused window settings +window.active.client.color: #8080A0 + +window.active.title.bg: raised gradient crossdiagonal bevel1 +window.active.title.bg.color: #6699CC +window.active.title.bg.colorTo: #334866 +window.active.title.bg.border.color: #000000 + +window.active.handle.bg: flat gradient crossdiagonal bevel1 +window.active.handle.bg.color: #6699CC +window.active.handle.bg.colorTo: #334866 +window.active.handle.bg.border.color: #000000 + +window.active.grip.bg: parentrelative +window.active.grip.bg.color: #000000 +window.active.grip.bg.colorTo: #000000 +window.active.grip.bg.border.color: #000000 + +window.active.label.bg: parentrelative +window.active.label.bg.color: #000000 +window.active.label.bg.colorTo: #000000 +window.active.label.bg.border.color: #000000 +window.active.label.text.color: #BFE9FF + +window.active.button.unpressed.bg: parentrelative +window.active.button.unpressed.bg.color: #000000 +window.active.button.unpressed.bg.colorTo: #000000 +window.active.button.unpressed.bg.border.color: #000000 +window.active.button.unpressed.image.color: grey85 + +window.active.button.pressed.bg: sunken parentrelative bevel1 +window.active.button.pressed.bg.color: #6699CC +window.active.button.pressed.bg.colorTo: #334866 +window.active.button.pressed.bg.border.color: #000000 +window.active.button.pressed.image.color: green + +window.active.button.toggled.bg: sunken parentrelative bevel2 +window.active.button.toggled.bg.color: #6699CC +window.active.button.toggled.bg.colorTo: #334866 +window.active.button.toggled.bg.border.color: #000000 +window.active.button.toggled.image.color: grey85 + +window.active.button.toggled.hover.bg: sunken parentrelative bevel2 +window.active.button.toggled.hover.bg.color: #6699CC +window.active.button.toggled.hover.bg.colorTo: #334866 +window.active.button.toggled.hover.bg.border.color: #000000 +window.active.button.toggled.hover.image.color: #00FF00 + +window.active.button.disabled.bg: parentrelative +window.active.button.disabled.bg.color: #000000 +window.active.button.disabled.bg.colorTo: #000000 +window.active.button.disabled.image.color: #000000 +window.active.button.disabled.bg.border.color: #000000 + +window.active.button.hover.bg: parentrelative +window.active.button.hover.image.color: #00FF00 +window.active.button.hover.bg.color: #000000 +window.active.button.hover.bg.colorTo: #000000 +window.active.button.hover.bg.border.color: #000000 + +!window.active.button.hover.bg: raised gradient crossdiagonal bevel1 +!window.active.button.hover.image.color: #00FF00 +!window.active.button.hover.bg.color: #334866 +!window.active.button.hover.bg.colorTo: #6699CC +!window.active.button.hover.bg.border.color: #000000 + +!! unfocused window settings +window.inactive.client.color: grey50 + +window.inactive.title.bg: flat gradient diagonal bevel1 +window.inactive.title.bg.color: #7F7FA0 +window.inactive.title.bg.colorTo: #333350 +window.inactive.title.bg.border.color: #000000 + +window.inactive.handle.bg: flat gradient diagonal bevel1 +window.inactive.handle.bg.color: grey50 +window.inactive.handle.bg.colorTo: grey20 +window.inactive.handle.bg.border.color: #000000 + +window.inactive.grip.bg: parentrelative +window.inactive.grip.bg.color: #000000 +window.inactive.grip.bg.colorTo: #000000 +window.inactive.grip.bg.border.color: #000000 + +window.inactive.label.bg: parentrelative +window.inactive.label.bg.color: #000000 +window.inactive.label.bg.colorTo: #000000 +window.inactive.label.bg.border.color: #000000 +window.inactive.label.text.color: #C3C3E0 + +window.inactive.button.unpressed.bg: parentrelative +window.inactive.button.unpressed.bg.color: #000000 +window.inactive.button.unpressed.bg.colorTo: #000000 +window.inactive.button.unpressed.bg.border.color: #000000 +window.inactive.button.unpressed.image.color: grey60 + +window.inactive.button.pressed.bg: sunken parentrelative bevel1 +window.inactive.button.pressed.bg.color: grey60 +window.inactive.button.pressed.bg.colorTo: grey20 +window.inactive.button.pressed.bg.border.color: #000000 +window.inactive.button.pressed.image.color: #00CC00 + +window.inactive.button.disabled.bg: parentrelative +window.inactive.button.disabled.bg.color: #000000 +window.inactive.button.disabled.bg.colorTo: #000000 +window.inactive.button.disabled.bg.border.color: #000000 +window.inactive.button.disabled.image.color: #000000 + +window.inactive.button.toggled.bg: sunken parentrelative bevel2 +window.inactive.button.toggled.bg.color: grey60 +window.inactive.button.toggled.bg.colorTo: grey20 +window.inactive.button.toggled.bg.border.color: #000000 +window.inactive.button.toggled.image.color: grey60 + +window.inactive.button.toggled.hover.bg: sunken parentrelative bevel2 +window.inactive.button.toggled.hover.bg.color: grey60 +window.inactive.button.toggled.hover.bg.colorTo: grey20 +window.inactive.button.toggled.hover.bg.border.color: #000000 +window.inactive.button.toggled.hover.image.color: #00CC00 + +window.inactive.button.hover.bg: parentrelative +window.inactive.button.hover.bg.color: #000000 +window.inactive.button.hover.bg.colorTo: #000000 +window.inactive.button.hover.image.color: #00CC00 +window.inactive.button.hover.bg.border.color: #000000 + +osd.label.bg: parentrelative sunken + +!! Global width settings +border.width: 1 +padding.width: 1 +window.handle.width: 0 +window.client.padding.width: 0 +window.client.padding.height: 0 +focus.inner.color: #A6CAF3 +focus.outer.color: #0000A0 + +!! Miscellaneous settings +border.color: #223344 + +!! Font stuff (not used anymore) +window.active.label.text.font: Candara,sans:size=10:shadow=y:shadowoffset=2:shadowtint=32:weight=0 +window.inactive.label.text.font: Candara,sans:size=10:shadowoffset=3:shadowtint=32:shadow=y:weight=0 +menu.title.text.font: Technical,sans:size=20:shadowoffset=2:shadowtint=35:shadow=y +menu.items.font: Technical,sans:size=10:shadowoffset=2:shadowtint=15:shadow=y + +menu.overlap: -3 diff --git a/themes/Natura/openbox-3/close.xbm b/themes/Natura/openbox-3/close.xbm new file mode 100644 index 0000000..ab7ff80 --- /dev/null +++ b/themes/Natura/openbox-3/close.xbm @@ -0,0 +1,4 @@ +#define close_width 8 +#define close_height 8 +static unsigned char close_bits[] = { + 0xc3, 0xe7, 0x7e, 0x3c, 0x3c, 0x7e, 0xe7, 0xc3 }; diff --git a/themes/Natura/openbox-3/close_hover.xbm b/themes/Natura/openbox-3/close_hover.xbm new file mode 100644 index 0000000..94ce59f --- /dev/null +++ b/themes/Natura/openbox-3/close_hover.xbm @@ -0,0 +1,5 @@ +#define close_hover_width 10 +#define close_hover_height 10 +static unsigned char close_hover_bits[] = { + 0x03, 0x03, 0x87, 0x03, 0xce, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x78, 0x00, + 0xfc, 0x00, 0xce, 0x01, 0x87, 0x03, 0x03, 0x03 }; diff --git a/themes/Natura/openbox-3/desk.xbm b/themes/Natura/openbox-3/desk.xbm new file mode 100644 index 0000000..6cdbbf1 --- /dev/null +++ b/themes/Natura/openbox-3/desk.xbm @@ -0,0 +1,4 @@ +#define desk_width 8 +#define desk_height 8 +static unsigned char desk_bits[] = { + 0x00, 0x00, 0x48, 0x78, 0x7e, 0x78, 0x48, 0x00 }; diff --git a/themes/Natura/openbox-3/desk_hover.xbm b/themes/Natura/openbox-3/desk_hover.xbm new file mode 100644 index 0000000..5e385d6 --- /dev/null +++ b/themes/Natura/openbox-3/desk_hover.xbm @@ -0,0 +1,4 @@ +#define desk_hover_width 8 +#define desk_hover_height 8 +static unsigned char desk_hover_bits[] = { + 0x00, 0x88, 0x88, 0xf8, 0xff, 0xf8, 0x88, 0x88 }; diff --git a/themes/Natura/openbox-3/desk_toggled.xbm b/themes/Natura/openbox-3/desk_toggled.xbm new file mode 100644 index 0000000..d89d4bb --- /dev/null +++ b/themes/Natura/openbox-3/desk_toggled.xbm @@ -0,0 +1,4 @@ +#define desk_toggled_width 8 +#define desk_toggled_height 8 +static unsigned char desk_toggled_bits[] = { + 0x00, 0x66, 0x66, 0x00, 0x00, 0x66, 0x66, 0x00 }; diff --git a/themes/Natura/openbox-3/iconify.xbm b/themes/Natura/openbox-3/iconify.xbm new file mode 100644 index 0000000..424cc67 --- /dev/null +++ b/themes/Natura/openbox-3/iconify.xbm @@ -0,0 +1,4 @@ +#define iconify_width 8 +#define iconify_height 8 +static unsigned char iconify_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e }; diff --git a/themes/Natura/openbox-3/iconify_hover.xbm b/themes/Natura/openbox-3/iconify_hover.xbm new file mode 100644 index 0000000..0f56602 --- /dev/null +++ b/themes/Natura/openbox-3/iconify_hover.xbm @@ -0,0 +1,4 @@ +#define iconify_hover_width 8 +#define iconify_hover_height 8 +static unsigned char iconify_hover_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff }; diff --git a/themes/Natura/openbox-3/max.xbm b/themes/Natura/openbox-3/max.xbm new file mode 100644 index 0000000..3a13089 --- /dev/null +++ b/themes/Natura/openbox-3/max.xbm @@ -0,0 +1,4 @@ +#define max_width 8 +#define max_height 8 +static unsigned char max_bits[] = { + 0xff, 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff }; diff --git a/themes/Natura/openbox-3/max_hover.xbm b/themes/Natura/openbox-3/max_hover.xbm new file mode 100644 index 0000000..247c492 --- /dev/null +++ b/themes/Natura/openbox-3/max_hover.xbm @@ -0,0 +1,5 @@ +#define max_hover_width 10 +#define max_hover_height 10 +static unsigned char max_hover_bits[] = { + 0xff, 0x03, 0xff, 0x03, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xff, 0x03 }; diff --git a/themes/Natura/openbox-3/max_toggled.xbm b/themes/Natura/openbox-3/max_toggled.xbm new file mode 100644 index 0000000..ff1c954 --- /dev/null +++ b/themes/Natura/openbox-3/max_toggled.xbm @@ -0,0 +1,4 @@ +#define max_toggled_width 8 +#define max_toggled_height 8 +static unsigned char max_toggled_bits[] = { + 0xfc, 0xfc, 0x84, 0x9f, 0x91, 0xf1, 0x11, 0x1f }; diff --git a/themes/Natura/openbox-3/shade.xbm b/themes/Natura/openbox-3/shade.xbm new file mode 100644 index 0000000..aaf1ec7 --- /dev/null +++ b/themes/Natura/openbox-3/shade.xbm @@ -0,0 +1,4 @@ +#define shade_width 8 +#define shade_height 8 +static unsigned char shade_bits[] = { + 0x7e, 0x7e, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/themes/Natura/openbox-3/shade_hover.xbm b/themes/Natura/openbox-3/shade_hover.xbm new file mode 100644 index 0000000..756842a --- /dev/null +++ b/themes/Natura/openbox-3/shade_hover.xbm @@ -0,0 +1,4 @@ +#define shade_hover_width 8 +#define shade_hover_height 8 +static unsigned char shade_hover_bits[] = { + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/themes/Natura/openbox-3/themerc b/themes/Natura/openbox-3/themerc new file mode 100644 index 0000000..fff1631 --- /dev/null +++ b/themes/Natura/openbox-3/themerc @@ -0,0 +1,99 @@ +!! Natura by quandar (http://deletefactory.net/quandar) + +!! Global Width +window.handle.width: 2 + +window.client.padding.width: 0 +window.client.padding.height: 0 +border.Width: 1 +padding.width: 2 + +window.active.border.color: #000000 +window.inactive.border.color: #000000 +menu.border.color: #000000 +menu.overlap: 0 +window.frameColor: #eeeeee +window.*.client.color: #eeeeee +*.text.justify: left + +!! Menu +menu.title.bg: flat gradient split +menu.title.bg.color: #5c4e45 +menu.title.bg.colorTo: #51443e +menu.title.text.color: #ffffff + +menu.items.bg: flat solid +menu.items.bg.color: #f9f2ee + +menu.items.text.color: #000000 +menu.items.disabled.text.color: #737573 + +menu.items.active.bg: flat gradient split +menu.items.active.bg.color: #5c4e45 +menu.items.active.bg.colorTo:#51443e +menu.items.active.text.color: #f9f2ee + +!! Active Windows +window.active.title.bg: flat gradient split +window.active.title.bg.color: #5c4e45 +window.active.title.bg.colorTo: #51443e +window.active.*.bg.border.color: #000000 + +window.active.label.bg: parentrelative +window.active.label.text.color: #f9f2ee + +window.active.button.*.bg: parentrelative +window.active.button.*.image.color: #f9f2ee +window.active.button.*.bg.border.color: #4e5860 + +window.active.button.hover.bg.color: #576773 +window.active.button.hover.bg.border.color: #9aabb9 +window.active.button.pressed.bg.color: #343b40 +window.active.button.pressed.bg.colorTo: #000000 +window.active.button.pressed.image.color: #b6b6b6 +window.active.button.pressed.bg.border.color: #b1a19e + +window.active.button.disabled.bg: parentrelative +window.active.button.disabled.image.color: #725e51 + +window.active.handle.bg: flat solid +window.active.handle.bg.color:#51443e + +window.*.grip.bg: flat solid +window.*.grip.bg.color: #51443e + +window.handle.width: 2 + +!! Inactive Windows +window.inactive.title.bg: flat gradient vertical +window.inactive.title.bg.color: #f0ece3 +window.inactive.title.bg.colorTo: #f8f7f2 +window.inactive.*.border.color: #bfbfbf + +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #919191 + +window.inactive.button.*.bg: parentrelative +window.inactive.button.*.bg.color: #eeeeee +window.inactive.button.*.image.color: #b6b6b6 +window.inactive.button.*.bg.border.color: #c9c9c9 + +window.inactive.button.pressed.bg.color: #c5c2c5 +window.inactive.button.pressed.bg.border.color: #7b7d7b +window.inactive.button.pressed.image.color: #999999 + +window.inactive.button.hover.bg.color: #afb1b2 +window.inactive.button.hover.bg.border.color: #d9dfe4 +window.inactive.button.hover.image.color: #a0a0a0 +window.inactive.button.disabled.bg: parentrelative +window.inactive.button.disabled.image.color: #dddddd + +window.inactive.handle.bg: flat solid +window.inactive.handle.bg.color: #f9f7f3 +window.inactive.grip.bg: parentrelative + +!! Fonts +window.active.label.text.font: shadow=y:shadowoffset=1:shadowtint=75 +window.inactive.label.text.font: shadow=y:shadowoffset=1:shadowtint=0 +menu.items.font: +menu.title.text.font: shadow=y:shadowoffset=1:shadowtint=75 diff --git a/themes/Onyx-Citrus/openbox-3/themerc b/themes/Onyx-Citrus/openbox-3/themerc new file mode 100644 index 0000000..a849342 --- /dev/null +++ b/themes/Onyx-Citrus/openbox-3/themerc @@ -0,0 +1,85 @@ +!! Onyx-Citrus by Dana Jansens +!! inspired by "Carbon" by p0ng, and the ever so popular Vista glass look + +!! Menu background +menu.items.bg: flat gradient vertical +menu.items.bg.color: #303030 +menu.items.bg.colorTo: #080808 + +!! Menu text +menu.items.text.color: #b8b8b8 +menu.items.justify: left +menu.items.disabled.text.color: #606060 + +!! Menu headers +menu.title.bg: raised splitvertical gradient +menu.title.bg.color: #303030 +menu.title.bg.colorTo: #181818 +menu.title.text.color: white +menu.title.text.justify: center + +!! Selected menu item +menu.items.active.bg: raised splitvertical gradient +menu.items.active.bg.color: #e18a51 +menu.items.active.bg.colorTo: #e1621d +menu.items.active.text.color: white + +!! Titlebar +window.active.title.bg: raised splitvertical gradient +window.active.title.bg.color: #414141 +window.active.title.bg.colorTo: #202020 +window.inactive.title.bg: gradient splitvertical gradient flat +window.inactive.title.bg.color: #DEE0D8 +window.inactive.title.bg.colorTo: #E0E0D8 + +!! Titlebar text +window.label.text.justify: center +window.active.label.bg: parentrelative +window.active.label.text.color: #f8f8f8 +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #747474 + +!! Window buttons +window.*.button.*.bg: parentrelative +window.active.button.*.hover.bg: flat splitvertical gradient border +window.inactive.button.*.hover.bg: parentrelative +window.*.button.*.pressed.bg: flat splitvertical gradient border + +window.active.button.*.hover.bg.color: #e18a51 +window.active.button.*.hover.bg.colorTo: #e1621d +window.active.button.*.hover.bg.border.color: #ab5d20 + +window.active.button.*.pressed.bg.color: #99663c +window.active.button.*.pressed.bg.colorTo: #99663c +window.active.button.*.pressed.image.color: #888888 +window.active.button.*.pressed.bg.border.color: #683913 +window.inactive.button.*.pressed.bg.color: #ffaa64 +window.inactive.button.*.pressed.bg.colorTo: #ffaa64 +window.inactive.button.*.pressed.image.color: #fdceb7 +window.inactive.button.*.pressed.bg.border.color: #f0832c + +window.active.button.*.image.color: #e0e0e0 +window.active.button.disabled.image.color: #707070 +window.inactive.button.*.image.color: #747474 +window.inactive.button.disabled.image.color: #c0c0c0 + +!! Borders +window.active.border.color: #181818 +window.inactive.border.color: #404040 +window.inactive.title.separator.color: #eeeee6 +border.width: 1 +padding.width: 2 +window.handle.width: 0 +window.active.client.color: #181818 +window.inactive.client.color: #CACAB6 +window.client.padding.width: 1 +window.client.padding.height: 1 +menu.overlap: 0 + +!! Font shadows +menu.items.font:shadow=y:shadowtint=30 + +!! On-screen displays +osd.bg: gradient vertical flat +osd.bg.color: #303030 +osd.bg.colorTo: #080808 diff --git a/themes/Onyx/openbox-3/themerc b/themes/Onyx/openbox-3/themerc new file mode 100644 index 0000000..8de9d06 --- /dev/null +++ b/themes/Onyx/openbox-3/themerc @@ -0,0 +1,85 @@ +!! Onyx by Dana Jansens +!! inspired by "Carbon" by p0ng, and the ever so popular Vista glass look + +!! Menu background +menu.items.bg: flat gradient vertical +menu.items.bg.color: #303030 +menu.items.bg.colorTo: #080808 + +!! Menu text +menu.items.text.color: #b8b8b8 +menu.items.justify: left +menu.items.disabled.text.color: #606060 + +!! Menu headers +menu.title.bg: raised splitvertical gradient +menu.title.bg.color: #303030 +menu.title.bg.colorTo: #181818 +menu.title.text.color: white +menu.title.text.justify: center + +!! Selected menu item +menu.items.active.bg: raised splitvertical gradient +menu.items.active.bg.color: #6d95de +menu.items.active.bg.colorTo: #2b829d +menu.items.active.text.color: #f8f8f8 + +!! Titlebar +window.active.title.bg: raised splitvertical gradient +window.active.title.bg.color: #414141 +window.active.title.bg.colorTo: #202020 +window.inactive.title.bg: gradient splitvertical gradient flat +window.inactive.title.bg.color: #DEE0D8 +window.inactive.title.bg.colorTo: #E0E0D8 + +!! Titlebar text +window.label.text.justify: center +window.active.label.bg: parentrelative +window.active.label.text.color: #f8f8f8 +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #747474 + +!! Window buttons +window.*.button.*.bg: parentrelative +window.active.button.*.hover.bg: flat splitvertical gradient border +window.inactive.button.*.hover.bg: parentrelative +window.*.button.*.pressed.bg: flat splitvertical gradient border + +window.active.button.*.hover.bg.color: #398dc6 +window.active.button.*.hover.bg.colorTo: #236d83 +window.active.button.*.hover.bg.border.color: #236d83 + +window.active.button.*.pressed.bg.color: #235679 +window.active.button.*.pressed.bg.colorTo: #154350 +window.active.button.*.pressed.image.color: #898989 +window.active.button.*.pressed.bg.border.color: #154350 +window.inactive.button.*.pressed.bg.color: #4ab5ff +window.inactive.button.*.pressed.bg.colorTo: #38b3d6 +window.inactive.button.*.pressed.image.color: #b7fdef +window.inactive.button.*.pressed.bg.border.color: #38b3d6 + +window.active.button.*.image.color: #e0e0e0 +window.active.button.disabled.image.color: #707070 +window.inactive.button.*.image.color: #747474 +window.inactive.button.disabled.image.color: #c0c0c0 + +!! Borders +window.active.border.color: #181818 +window.inactive.border.color: #404040 +window.inactive.title.separator.color: #eeeee6 +border.width: 1 +padding.width: 2 +window.handle.width: 0 +window.active.client.color: #181818 +window.inactive.client.color: #CACAB6 +window.client.padding.width: 1 +window.client.padding.height: 1 +menu.overlap: 0 + +!! Font shadows +menu.items.font:shadow=y:shadowtint=30 + +!! On-screen displays +osd.bg: gradient vertical flat +osd.bg.color: #303030 +osd.bg.colorTo: #080808 diff --git a/themes/Orang/openbox-3/themerc b/themes/Orang/openbox-3/themerc new file mode 100644 index 0000000..ee444c5 --- /dev/null +++ b/themes/Orang/openbox-3/themerc @@ -0,0 +1,100 @@ +!! i be a mess + +menu.title.bg: flat solid border +menu.title.bg.color: #ce5c00 +menu.title.bg.border.color: #fcaf3e +menu.title.text.color: #ffffff + +menu.items.bg: flat solid +#menu.items.bg.color: #ffffff +menu.items.bg.color: #d3d7cf +menu.items.text.color: #000000 +menu.items.disabled.text.color: #737573 + +menu.items.active.bg: flat solid +menu.items.active.bg.color: #ce5c00 +menu.items.active.text.color: #ffffff + +!! General window settings +*.justify: left + +!! focused window settings +window.active.client.color: #f7f7f7 + +window.active.title.bg: flat border solid +window.active.title.bg.color: #ce5c00 +window.active.title.bg.border.color: #fcaf3e + +window.active.handle.bg: flat solid border +window.active.handle.bg.color: #d3d7cf +window.active.handle.bg.border.color: #eeeeec +!window.active.handle.bg.color: #969494 +window.active.grip.bg: parentrelative + +window.active.label.bg: parentrelative +window.active.label.text.color: #ffffff + +window.active.button.*.bg: parentrelative +window.active.button.*.image.color: #ffffff + +window.active.button.pressed.bg: flat solid bevel1 +!window.active.button.pressed.bg.color: #888888 +window.active.button.pressed.bg.color: #f57900 +window.active.button.pressed.image.color: #d3d7cf + +!#hover +window.active.button.hover.bg: flat solid +window.active.button.hover.image.color: #ffffff +window.active.button.hover.bg.color: #fcaf3e + +window.active.button.disabled.bg: flat solid +window.active.button.disabled.bg.color: #dfb454 +window.active.button.disabled.image.color: #ffffff + + +!! unfocused window settings +window.inactive.client.color: #f7f7f7 + +window.inactive.title.bg: flat solid border +window.inactive.title.bg.color: #d3d7cf +window.inactive.title.bg.border.color: #dfe3db + +window.inactive.handle.bg: flat solid +window.inactive.handle.bg.color: #d3d7cf + +window.inactive.grip.bg: parentrelative + +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #888a85 + +window.inactive.button.*.bg: parentrelative +window.inactive.button.unpressed.image.color: #555555 + +window.inactive.button.pressed.bg: flat border solid +window.inactive.button.pressed.bg.color: #aaaaaa +window.inactive.button.pressed.bg.border.color: #e5e5e5 + +window.inactive.button.hover.bg: flat solid +window.inactive.button.hover.bg.color: #e3e3e3 + +window.inactive.button.disabled.bg:flat solid +window.inactive.button.disabled.bg.color: #e3e3e3 + + +!! Global width settings +border.Width: 1 +padding.width: 1 +window.handle.width: 3 +window.client.padding.width: 0 +window.client.padding.height: 0 +menu.overlap: 2 + +!! Miscellaneous settings +border.color: #000000 + +!! font me! +window.active.label.text.font: +window.inactive.label.text.font: +menu.title.text.font: +menu.items.font: + diff --git a/themes/Syscrash/openbox-3/max.xbm b/themes/Syscrash/openbox-3/max.xbm new file mode 100644 index 0000000..44c7cef --- /dev/null +++ b/themes/Syscrash/openbox-3/max.xbm @@ -0,0 +1,4 @@ +#define max_width 6 +#define max_height 6 +static unsigned char max_bits[] = { + 0x3e, 0x22, 0x2f, 0x29, 0x39, 0x0f }; diff --git a/themes/Syscrash/openbox-3/max_disabled.xbm b/themes/Syscrash/openbox-3/max_disabled.xbm new file mode 100644 index 0000000..6d030af --- /dev/null +++ b/themes/Syscrash/openbox-3/max_disabled.xbm @@ -0,0 +1,4 @@ +#define max_width 6 +#define max_height 6 +static unsigned char max_bits[] = { + 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; diff --git a/themes/Syscrash/openbox-3/max_pressed.xbm b/themes/Syscrash/openbox-3/max_pressed.xbm new file mode 100644 index 0000000..6d030af --- /dev/null +++ b/themes/Syscrash/openbox-3/max_pressed.xbm @@ -0,0 +1,4 @@ +#define max_width 6 +#define max_height 6 +static unsigned char max_bits[] = { + 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f }; diff --git a/themes/Syscrash/openbox-3/max_toggled.xbm b/themes/Syscrash/openbox-3/max_toggled.xbm new file mode 100644 index 0000000..020f779 --- /dev/null +++ b/themes/Syscrash/openbox-3/max_toggled.xbm @@ -0,0 +1,4 @@ +#define max_toggled_width 6 +#define max_toggled_height 6 +static unsigned char max_toggled_bits[] = { + 0x3c, 0x27, 0x25, 0x3d, 0x11, 0x1f }; diff --git a/themes/Syscrash/openbox-3/themerc b/themes/Syscrash/openbox-3/themerc new file mode 100644 index 0000000..65666d9 --- /dev/null +++ b/themes/Syscrash/openbox-3/themerc @@ -0,0 +1,114 @@ +!! syscrash's theme, based off nightm4re's flax or something + +!!--------------------------------------------------------------------------- +!! Dimensions + +border.width: 1 +border.color: #6d6d6d + +padding.width: 1 +window.handle.width: 0 +window.client.padding.width: 0 +window.client.padding.height: 0 + +menu.overlap: 0 + +!!--------------------------------------------------------------------------- +!! Fonts + +window.active.label.text.font: shadow=y +window.label.text.justify: left + +menu.title.text.font: shadow=y +menu.title.text.justify: left + +menu.items.font: + +!!--------------------------------------------------------------------------- +!! Menu Settings + +menu.title.bg: flat gradient vertical +menu.title.bg.color: #4c4c4c +menu.title.bg.colorTo: #707070 +menu.title.text.color: white + +menu.items.bg: flat solid +menu.items.bg.color: #e6e6e0 +menu.items.text.color: #22221c + +menu.items.active.bg: flat solid +menu.items.active.bg.color: #4c4c4c +menu.items.active.text.color: #ffffff + +menu.items.disabled.text.color: #8c8c75 + +!!--------------------------------------------------------------------------- +!! Window Settings + +window.active.padding.width: 10 +window.active.title.bg: flat solid +window.active.title.bg.color: #9b9b9b + +window.active.label.bg: flat gradient vertical border +window.active.label.bg.color: #4c4c4c +window.active.label.bg.colorTo: #707070 +window.active.label.text.color: white + +window.active.handle.bg: flat gradient crossdiagonal +window.active.handle.bg.color: #d3deda +window.active.handle.bg.colorTo: #9fbfc1 + +window.active.grip.bg: flat solid +window.active.grip.bg.color: #9fbfc1 + +window.active.client.color: #4c4c4c +window.inactive.client.color: #707070 + +window.inactive.title.bg: flat solid +window.inactive.title.bg.color: #9b9b9b + +window.inactive.label.bg: parentrelative +window.inactive.label.text.color: #4c4c4c + +window.inactive.handle.bg: flat solid +window.inactive.handle.bg.color: #e6e6e0 + +window.inactive.grip.bg: flat solid +window.inactive.grip.bg.color: #e6e6e0 + +!!--------------------------------------------------------------------------- +!! Button Settings + +window.active.button.unpressed.bg: flat gradient vertical border +window.active.button.unpressed.bg.color: #515151 +window.active.button.unpressed.bg.colorTo: #676767 +window.active.button.unpressed.image.color: white + +window.active.button.pressed.bg: flat gradient crossdiagonal border +window.active.button.pressed.bg.color: #d3deda +window.active.button.pressed.bg.colorTo: #9fbfc1 + +window.active.button.hover.bg: flat solid border +window.active.button.hover.bg.color: #e6e6e0 + +window.active.button.disabled.bg: parentrelative +window.active.button.disabled.image.color: #8c8c75 + +!! without this it looks right +!!window.active.button.toggled.bg: flat solid +!!window.active.button.toggled.bg.color: #9fbfc1 + +window.inactive.button.unpressed.bg: parentrelative +window.inactive.button.unpressed.image.color: #4c4c4c + +window.inactive.button.hover.bg: flat solid border +window.inactive.button.hover.bg.color: #e6e6e0 +window.inactive.button.pressed.bg: flat gradient crossdiagonal border +window.inactive.button.pressed.bg.color: #d3deda +window.inactive.button.pressed.bg.colorTo: #9fbfc1 + +window.inactive.button.disabled.bg: parentrelative +window.inactive.button.disabled.image.color: #8c8c75 + +!! window.inactive.button.toggled.bg: flat solid +!! window.inactive.button.toggled.bg.color: #444438 diff --git a/tools/gdm-control/Makefile b/tools/gdm-control/Makefile new file mode 100644 index 0000000..cfc4653 --- /dev/null +++ b/tools/gdm-control/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C ../.. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/tools/gdm-control/gdm-control.c b/tools/gdm-control/gdm-control.c new file mode 100644 index 0000000..db28841 --- /dev/null +++ b/tools/gdm-control/gdm-control.c @@ -0,0 +1,295 @@ +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <X11/Xlib.h> +#include <X11/Xauth.h> + +#include <glib.h> + +typedef enum +{ + INVALID, + NONE, + SHUTDOWN, + REBOOT, + SUSPEND, + SWITCHUSER +} Action; + +#define GDM_PROTOCOL_SOCKET_PATH1 "/var/run/gdm_socket" +#define GDM_PROTOCOL_SOCKET_PATH2 "/tmp/.gdm_socket" + +#define GDM_PROTOCOL_MSG_CLOSE "CLOSE" +#define GDM_PROTOCOL_MSG_VERSION "VERSION" +#define GDM_PROTOCOL_MSG_AUTHENTICATE "AUTH_LOCAL" +#define GDM_PROTOCOL_MSG_QUERY_ACTION "QUERY_LOGOUT_ACTION" +#define GDM_PROTOCOL_MSG_SET_ACTION "SET_SAFE_LOGOUT_ACTION" +#define GDM_PROTOCOL_MSG_FLEXI_XSERVER "FLEXI_XSERVER" + +#define GDM_ACTION_STR_NONE GDM_PROTOCOL_MSG_SET_ACTION" NONE" +#define GDM_ACTION_STR_SHUTDOWN GDM_PROTOCOL_MSG_SET_ACTION" HALT" +#define GDM_ACTION_STR_REBOOT GDM_PROTOCOL_MSG_SET_ACTION" REBOOT" +#define GDM_ACTION_STR_SUSPEND GDM_PROTOCOL_MSG_SET_ACTION" SUSPEND" + +#define GDM_MIT_MAGIC_COOKIE_LEN 16 + +static int fd = 0; + +static void gdm_disconnect() +{ + if (fd > 0) + close(fd); + fd = 0; +} + +static char* get_display_number(void) +{ + char *display_name; + char *retval; + char *p; + + display_name = XDisplayName(NULL); + + p = strchr(display_name, ':'); + if (!p) + return g_strdup ("0"); + + while (*p == ':') p++; + + retval = g_strdup (p); + + p = strchr (retval, '.'); + if (p != NULL) + *p = '\0'; + + return retval; +} + +static char* gdm_send_protocol_msg (const char *msg) +{ + GString *retval; + char buf[256]; + char *p; + int len; + + p = g_strconcat(msg, "\n", NULL); + if (write (fd, p, strlen(p)) < 0) { + g_free (p); + + g_warning ("Failed to send message to GDM: %s", + g_strerror (errno)); + return NULL; + } + g_free (p); + + p = NULL; + retval = NULL; + while ((len = read(fd, buf, sizeof(buf) - 1)) > 0) { + buf[len] = '\0'; + + if (!retval) + retval = g_string_new(buf); + else + retval = g_string_append(retval, buf); + + if ((p = strchr(retval->str, '\n'))) + break; + } + + if (p) *p = '\0'; + + return retval ? g_string_free(retval, FALSE) : NULL; +} + +static gboolean gdm_authenticate() +{ + FILE *f; + Xauth *xau; + const char *xau_path; + char *display_number; + gboolean retval; + + if (!(xau_path = XauFileName())) + return FALSE; + + if (!(f = fopen(xau_path, "r"))) + return FALSE; + + retval = FALSE; + display_number = get_display_number(); + + while ((xau = XauReadAuth(f))) { + char buffer[40]; /* 2*16 == 32, so 40 is enough */ + char *msg; + char *response; + int i; + + if (xau->family != FamilyLocal || + strncmp (xau->number, display_number, xau->number_length) || + strncmp (xau->name, "MIT-MAGIC-COOKIE-1", xau->name_length) || + xau->data_length != GDM_MIT_MAGIC_COOKIE_LEN) + { + XauDisposeAuth(xau); + continue; + } + + for (i = 0; i < GDM_MIT_MAGIC_COOKIE_LEN; i++) + g_snprintf(buffer + 2*i, 3, "%02x", (guint)(guchar)xau->data[i]); + + XauDisposeAuth(xau); + + msg = g_strdup_printf(GDM_PROTOCOL_MSG_AUTHENTICATE " %s", buffer); + response = gdm_send_protocol_msg(msg); + g_free (msg); + + if (response && !strcmp(response, "OK")) { + /*auth_cookie = g_strdup(buffer);*/ + g_free(response); + retval = TRUE; + break; + } + + g_free (response); + } + + fclose(f); + return retval; +} + +static gboolean gdm_connect() +{ + struct sockaddr_un addr; + char *response; + + assert(fd <= 0); + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + g_warning("Failed to create GDM socket: %s", g_strerror (errno)); + gdm_disconnect(); + return FALSE; + } + + if (g_file_test(GDM_PROTOCOL_SOCKET_PATH1, G_FILE_TEST_EXISTS)) + strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH1); + else + strcpy(addr.sun_path, GDM_PROTOCOL_SOCKET_PATH2); + + addr.sun_family = AF_UNIX; + + if (connect(fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) { + g_warning("Failed to establish a connection with GDM: %s", + g_strerror(errno)); + gdm_disconnect(); + return FALSE; + } + + response = gdm_send_protocol_msg(GDM_PROTOCOL_MSG_VERSION); + if (!response || strncmp(response, "GDM ", strlen("GDM ") != 0)) { + g_free(response); + + g_warning("Failed to get protocol version from GDM"); + gdm_disconnect(); + return FALSE; + } + g_free(response); + + if (!gdm_authenticate()) { + g_warning("Failed to authenticate with GDM"); + gdm_disconnect(); + return FALSE; + } + + return TRUE; +} + +int main(int argc, char **argv) +{ + int i; + Action a = INVALID; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) { + a = INVALID; + break; + } + if (!strcmp(argv[i], "--none")) { + a = NONE; + break; + } + if (!strcmp(argv[i], "--shutdown")) { + a = SHUTDOWN; + break; + } + if (!strcmp(argv[i], "--reboot")) { + a = REBOOT; + break; + } + if (!strcmp(argv[i], "--suspend")) { + a = SUSPEND; + break; + } + if (!strcmp(argv[i], "--switch-user")) { + a = SWITCHUSER; + break; + } + } + + if (!a) { + printf("Usage: gdm-control ACTION\n\n"); + printf("Actions:\n"); + printf(" --help Display this help and exit\n"); + printf(" --none Do nothing special when the current session ends\n"); + printf(" --shutdown Shutdown the computer when the current session ends\n"); + printf(" --reboot Reboot the computer when the current session ends\n"); + printf(" --suspend Suspend the computer when the current session ends\n"); + printf(" --switch-user Log in as a new user (this works immediately)\n\n"); + return 0; + } + + { + char *d, *response; + const char *action_string; + + d = XDisplayName(NULL); + if (!d) { + fprintf(stderr, + "Unable to find the X display specified by the DISPLAY " + "environment variable. Ensure that it is set correctly."); + return 1; + } + + switch (a) { + case NONE: + action_string = GDM_ACTION_STR_NONE; + break; + case SHUTDOWN: + action_string = GDM_ACTION_STR_SHUTDOWN; + break; + case REBOOT: + action_string = GDM_ACTION_STR_REBOOT; + break; + case SUSPEND: + action_string = GDM_ACTION_STR_SUSPEND; + break; + case SWITCHUSER: + action_string = GDM_PROTOCOL_MSG_FLEXI_XSERVER; + break; + default: + assert(0); + } + + if (gdm_connect()) { + response = gdm_send_protocol_msg(action_string); + g_free(response); + gdm_disconnect(); + } + } + + return 0; +} diff --git a/tools/gnome-panel-control/Makefile b/tools/gnome-panel-control/Makefile new file mode 100644 index 0000000..cfc4653 --- /dev/null +++ b/tools/gnome-panel-control/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C ../.. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/tools/gnome-panel-control/gnome-panel-control.c b/tools/gnome-panel-control/gnome-panel-control.c new file mode 100644 index 0000000..67aa039 --- /dev/null +++ b/tools/gnome-panel-control/gnome-panel-control.c @@ -0,0 +1,100 @@ +#include <X11/Xlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> + +typedef enum +{ + NONE, + MAIN_MENU, + RUN_DIALOG +} Action; + +int main(int argc, char **argv) +{ + int i; + Action a = NONE; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) { + a = NONE; + break; + } + if (!strcmp(argv[i], "--main-menu")) { + a = MAIN_MENU; + break; + } + if (!strcmp(argv[i], "--run-dialog")) { + a = RUN_DIALOG; + break; + } + } + + if (!a) { + printf("Usage: gnome-panel-control ACTION\n\n"); + printf("Actions:\n"); + printf(" --help Display this help and exit\n"); + printf(" --main-menu Show the main menu\n"); + printf(" --run-dialog Show the run dialog\n\n"); + return 0; + } + + { + Display *d; + Window root; + XClientMessageEvent ce; + Atom act_atom; + Time timestamp; + + d = XOpenDisplay(NULL); + if (!d) { + fprintf(stderr, + "Unable to open the X display specified by the DISPLAY " + "environment variable. Ensure you have permission to " + "connect to the display."); + return 1; + } + root = RootWindowOfScreen(DefaultScreenOfDisplay(d)); + + switch (a) { + case MAIN_MENU: + act_atom = XInternAtom(d, "_GNOME_PANEL_ACTION_MAIN_MENU", False); + break; + case RUN_DIALOG: + act_atom = XInternAtom(d, "_GNOME_PANEL_ACTION_RUN_DIALOG", False); + break; + default: + assert(0); + } + + /* Generate a timestamp */ + { + XEvent event; + Window win; + + win = XCreateSimpleWindow(d, root, 0, 0, 1, 1, 0, 0, 0); + + XSelectInput(d, win, PropertyChangeMask); + + XChangeProperty(d, win, act_atom, act_atom, 8, + PropModeAppend, NULL, 0); + XWindowEvent(d, win, PropertyChangeMask, &event); + + XDestroyWindow(d, win); + + timestamp = event.xproperty.time; + } + + ce.type = ClientMessage; + ce.window = root; + ce.message_type = XInternAtom(d, "_GNOME_PANEL_ACTION", False); + ce.format = 32; + ce.data.l[0] = act_atom; + ce.data.l[1] = timestamp; + XSendEvent(d, root, False, StructureNotifyMask, (XEvent*) &ce); + + XCloseDisplay(d); + } + + return 0; +} diff --git a/tools/obxprop/Makefile b/tools/obxprop/Makefile new file mode 100644 index 0000000..cfc4653 --- /dev/null +++ b/tools/obxprop/Makefile @@ -0,0 +1,4 @@ +all clean install: + $(MAKE) -C ../.. -$(MAKEFLAGS) $@ + +.PHONY: all clean install diff --git a/tools/obxprop/obxprop.c b/tools/obxprop/obxprop.c new file mode 100644 index 0000000..83ff54a --- /dev/null +++ b/tools/obxprop/obxprop.c @@ -0,0 +1,351 @@ +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <glib.h> + +gint fail(const gchar *s) { + if (s) + fprintf(stderr, "%s\n", s); + else + fprintf + (stderr, + "Usage: obxprop [OPTIONS] [--] [PROPERTIES ...]\n\n" + "Options:\n" + " --help Display this help and exit\n" + " --display DISPLAY Connect to this X display\n" + " --id ID Show the properties for this window\n" + " --root Show the properties for the root window\n"); + return 1; +} + +gint parse_hex(gchar *s) { + gint result = 0; + while (*s) { + gint add; + if (*s >= '0' && *s <='9') + add = *s-'0'; + else if (*s >= 'A' && *s <='F') + add = *s-'A'+10; + else if (*s >= 'a' && *s <='f') + add = *s-'a'+10; + else + break; + + result *= 16; + result += add; + ++s; + } + return result; +} + +Window find_client(Display *d, Window win) +{ + Window r, *children; + guint n, i; + Atom state = XInternAtom(d, "WM_STATE", True); + Atom ret_type; + gint ret_format, res; + gulong ret_items, ret_bytesleft, *xdata; + + XQueryTree(d, win, &r, &r, &children, &n); + for (i = 0; i < n; ++i) { + Window w = find_client(d, children[i]); + if (w) return w; + } + + // try me + res = XGetWindowProperty(d, win, state, 0, 1, + False, state, &ret_type, &ret_format, + &ret_items, &ret_bytesleft, + (unsigned char**) &xdata); + XFree(xdata); + if (res != Success || ret_type == None || ret_items < 1) + return None; + return win; // found it! +} + +static gboolean get_all(Display *d, Window win, Atom prop, + Atom *type, gint *size, + guchar **data, guint *num) +{ + gboolean ret = FALSE; + gint res; + guchar *xdata = NULL; + gulong ret_items, bytes_left; + + res = XGetWindowProperty(d, win, prop, 0l, G_MAXLONG, + FALSE, AnyPropertyType, type, size, + &ret_items, &bytes_left, &xdata); + if (res == Success) { + if (ret_items > 0) { + guint i; + + *data = g_malloc(ret_items * (*size / 8)); + for (i = 0; i < ret_items; ++i) + switch (*size) { + case 8: + (*data)[i] = xdata[i]; + break; + case 16: + ((guint16*)*data)[i] = ((gushort*)xdata)[i]; + break; + case 32: + ((guint32*)*data)[i] = ((gulong*)xdata)[i]; + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + } + *num = ret_items; + ret = TRUE; + XFree(xdata); + } + return ret; +} + +gchar *append_string(gchar *before, gchar *after, gboolean quote) +{ + gchar *tmp; + const gchar *q = quote ? "\"" : ""; + if (before) + tmp = g_strdup_printf("%s, %s%s%s", before, q, after, q); + else + tmp = g_strdup_printf("%s%s%s", q, after, q); + g_free(before); + return tmp; +} + +gchar *append_int(gchar *before, guint after) +{ + gchar *tmp; + if (before) + tmp = g_strdup_printf("%s, %u", before, after); + else + tmp = g_strdup_printf("%u", after); + g_free(before); + return tmp; +} + +gchar* read_strings(gchar *val, guint n, gboolean utf8) +{ + GSList *strs = NULL, *it; + gchar *ret, *p; + guint i; + + p = val; + while (p < val + n) { + strs = g_slist_append(strs, g_strndup(p, n - (p - val))); + p += strlen(p) + 1; /* next string */ + } + + ret = NULL; + for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) { + char *data; + + if (utf8) { + if (g_utf8_validate(it->data, -1, NULL)) + data = g_strdup(it->data); + else + data = g_strdup(""); + } + else + data = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL); + + ret = append_string(ret, data, TRUE); + g_free(data); + } + + while (strs) { + g_free(strs->data); + strs = g_slist_delete_link(strs, strs); + } + return ret; +} + +gchar* read_atoms(Display *d, guchar *val, guint n) +{ + gchar *ret; + guint i; + + ret = NULL; + for (i = 0; i < n; ++i) + ret = append_string(ret, XGetAtomName(d, ((guint32*)val)[i]), FALSE); + return ret; +} + +gchar* read_numbers(guchar *val, guint n, guint size) +{ + gchar *ret; + guint i; + + ret = NULL; + for (i = 0; i < n; ++i) + switch (size) { + case 8: + ret = append_int(ret, ((guint8*)val)[i]); + break; + case 16: + ret = append_int(ret, ((guint16*)val)[i]); + break; + case 32: + ret = append_int(ret, ((guint32*)val)[i]); + break; + default: + g_assert_not_reached(); /* unhandled size */ + } + + return ret; +} + +gboolean read_prop(Display *d, Window w, Atom prop, const gchar **type, gchar **val) +{ + guchar *ret; + guint nret; + gint size; + Atom ret_type; + + ret = NULL; + if (get_all(d, w, prop, &ret_type, &size, &ret, &nret)) { + *type = XGetAtomName(d, ret_type); + + if (strcmp(*type, "STRING") == 0) + *val = read_strings((gchar*)ret, nret, FALSE); + else if (strcmp(*type, "UTF8_STRING") == 0) + *val = read_strings((gchar*)ret, nret, TRUE); + else if (strcmp(*type, "ATOM") == 0) { + g_assert(size == 32); + *val = read_atoms(d, ret, nret); + } + else + *val = read_numbers(ret, nret, size); + + g_free(ret); + return TRUE; + } + return FALSE; +} + +void show_properties(Display *d, Window w, int argc, char **argv) +{ + Atom* props; + int i, n; + + props = XListProperties(d, w, &n); + + for (i = 0; i < n; ++i) { + const char *type; + char *name, *val; + + name = XGetAtomName(d, props[i]); + + if (read_prop(d, w, props[i], &type, &val)) { + int found = 1; + if (argc) { + int i; + + found = 0; + for (i = 0; i < argc; i++) + if (!strcmp(name, argv[i])) { + found = 1; + break; + } + } + if (found) + g_print("%s(%s) = %s\n", name, type, (val ? val : "")); + g_free(val); + } + + XFree(name); + } + + XFree(props); +} + +int main(int argc, char **argv) +{ + Display *d; + Window id, userid = None; + int i; + char *dname = NULL; + gboolean root = FALSE; + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--help")) { + return fail(NULL); + } + else if (!strcmp(argv[i], "--root")) + root = TRUE; + else if (!strcmp(argv[i], "--id")) { + if (++i == argc) + return fail(NULL); + if (argv[i][0] == '0' && argv[i][1] == 'x') { + /* hex */ + userid = parse_hex(argv[i]+2); + } + else { + /* decimal */ + userid = atoi(argv[i]); + } + if (!userid) + return fail("Unable to parse argument to --id."); + } + else if (!strcmp(argv[i], "--display")) { + if (++i == argc) + return fail(NULL); + dname = argv[i]; + } + else if (*argv[i] != '-') + break; + else if (!strcmp(argv[i], "--")) { + i++; + break; + } + else + return fail(NULL); + } + + d = XOpenDisplay(dname); + if (!d) { + return fail("Unable to find an X display. " + "Ensure you have permission to connect to the display."); + } + + if (root) + userid = RootWindow(d, DefaultScreen(d)); + + if (userid == None) { + int j; + j = XGrabPointer(d, RootWindow(d, DefaultScreen(d)), + False, ButtonPressMask, + GrabModeAsync, GrabModeAsync, + None, XCreateFontCursor(d, XC_crosshair), + CurrentTime); + if (j != GrabSuccess) + return fail("Unable to grab the pointer device"); + while (1) { + XEvent ev; + + XNextEvent(d, &ev); + if (ev.type == ButtonPress) { + XUngrabPointer(d, CurrentTime); + userid = ev.xbutton.subwindow; + break; + } + } + id = find_client(d, userid); + } + else + id = userid; /* they picked this one */ + + if (id == None) + return fail("Unable to find window with the requested ID"); + + show_properties(d, id, argc - i, &argv[i]); + + XCloseDisplay(d); + + return 0; +} diff --git a/tools/themeupdate/themeupdate.py b/tools/themeupdate/themeupdate.py new file mode 100755 index 0000000..c2ad6a7 --- /dev/null +++ b/tools/themeupdate/themeupdate.py @@ -0,0 +1,410 @@ +#! /usr/bin/python + +# themeupdate.py for the Openbox window manager +# This utility is for updating old themes from Blackbox, Fluxbox, and Openbox2 +# to Openbox3 +# +# Copyright (c) 2003-2007 Dana Jansens +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# See the COPYING file for a copy of the GNU General Public License. + +import sys + +data = [] +valid = True + +def out(str): + sys.stderr.write(str) + sys.stderr.flush() + +def read_bool(): + while True: + inp = sys.stdin.readline(1).strip() + if inp == 'y' or inp == '': return True + if inp == 'n': return False + +def getkeyval(line): + key = line[:line.find(':')].strip() + value = line[line.find(':') + 1:].strip() + if not (key and value): + key = value = None + return key, value + +def find_key(data, keysubstr, exact = False): + i = 0 + n = len(data) + while i < n: + l = data[i] + key, value = getkeyval(l) + if key and value: + if (exact and key == keysubstr) or \ + (not exact and key.find(keysubstr) != -1): + return i, key, value + i += 1 + return -1, None, None + +def simple_replace(data): + pairs = {} + pairs['.picColor'] = '.imageColor' + pairs['menu.frame'] = 'menu.items' + pairs['menu.hilite'] = 'menu.selected' + pairs['borderColor'] = 'border.color' + pairs['imageColor'] = 'image.color' + pairs['textColor'] = 'text.color' + pairs['interlaceColor'] = 'interlace.color' + + for k in pairs.keys(): + while 1: + i, key, nul = find_key(data, k); + if i >= 0: + newl = data[i].replace(k, pairs[k]) + out('Updating "' + key + + '" to "' + key.replace(k, pairs[k]) + '"\n') + data[i] = newl + else: + break + + pairs = {} + pairs['window.focus.font'] = 'window.active.label.text.font' + pairs['window.unfocus.font'] = 'window.inactive.label.text.font' + pairs['window.justify'] = 'window.label.justify' + pairs['menu.frame.disableColor'] = 'menu.disabled.textColor' + pairs['window.label.focus.font'] = 'window.active.label.text.font' + pairs['window.label.unfocus.font'] = 'window.inactive.label.text.font' + pairs['window.label.justify'] = 'window.label.text.justify' + pairs['menu.title.font'] = 'menu.title.text.font' + pairs['menu.title.justify'] = 'menu.title.text.justify' + pairs['menuOverlap'] = 'menu.overlap' + pairs['handleWidth'] = 'window.handle.width' + pairs['borderWidth'] = 'border.width' + pairs['bevelWidth'] = 'padding.width' + pairs['frameWidth'] = 'window.client.padding.width' + pairs['window.frame.focusColor'] = 'window.active.client.color' + pairs['window.frame.unfocusColor'] = 'window.inactive.client.color' + pairs['window.title.focus'] = 'window.active.title.bg' + pairs['window.title.unfocus'] = 'window.inactive.title.bg' + pairs['window.label.focus'] = 'window.active.label.bg' + pairs['window.label.unfocus'] = 'window.inactive.label.bg' + pairs['window.handle.focus'] = 'window.active.handle.bg' + pairs['window.handle.unfocus'] = 'window.inactive.handle.bg' + pairs['window.grip.focus'] = 'window.active.grip.bg' + pairs['window.grip.unfocus'] = 'window.inactive.grip.bg' + pairs['menu.items'] = 'menu.items.bg' + pairs['menu.title'] = 'menu.title.bg' + pairs['menu.selected'] = 'menu.items.active.bg' + pairs['window.title.focus'] = 'window.active.title.bg' + pairs['window.label.focus'] = 'window.active.label.bg' + pairs['window.title.unfocus'] = 'window.inactive.title.bg' + pairs['window.label.unfocus'] = 'window.inactive.label.bg' + pairs['window.button.disabled.focus'] = 'window.active.button.disabled.bg' + pairs['window.button.disabled.unfocus'] = \ + 'window.inactive.button.disabled.bg' + pairs['window.button.pressed.focus'] = 'window.active.button.pressed.bg' + pairs['window.button.pressed.unfocus'] = \ + 'window.inactive.button.pressed.bg' + pairs['window.button.toggled.focus'] = 'window.active.button.toggled.bg' + pairs['window.button.toggled.unfocus'] = \ + 'window.inactive.button.toggled.bg' + pairs['window.button.focus'] = 'window.active.button.unpressed.bg' + pairs['window.button.unfocus'] = 'window.inactive.button.unpressed.bg' + pairs['window.button.hover.focus'] = 'window.active.button.hover.bg' + pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover.bg' + + for k in pairs.keys(): + while 1: + i, key, nul = find_key(data, k, True); + if i >= 0: + newl = data[i].replace(k, pairs[k]) + out('Updating "' + key + + '" to "' + key.replace(k, pairs[k]) + '"\n') + data[i] = newl + else: + break + + pairs = {} + pairs['window.title.focus'] = 'window.active.title' + pairs['window.title.unfocus'] = 'window.inactive.title' + pairs['window.label.focus'] = 'window.active.label' + pairs['window.label.unfocus'] = 'window.inactive.label' + pairs['window.handle.focus'] = 'window.active.handle' + pairs['window.handle.unfocus'] = 'window.inactive.handle' + pairs['window.grip.focus'] = 'window.active.grip' + pairs['window.grip.unfocus'] = 'window.inactive.grip' + pairs['menu.selected'] = 'menu.items.active' + pairs['window.title.focus'] = 'window.active.title' + pairs['window.label.focus'] = 'window.active.label' + pairs['window.title.unfocus'] = 'window.inactive.title' + pairs['window.label.unfocus'] = 'window.inactive.label' + pairs['window.button.disabled.focus'] = 'window.active.button.disabled' + pairs['window.button.disabled.unfocus'] = \ + 'window.inactive.button.disabled' + pairs['window.button.pressed.focus'] = 'window.active.button.pressed' + pairs['window.button.pressed.unfocus'] = \ + 'window.inactive.button.pressed' + pairs['window.button.toggled.focus'] = 'window.active.button.toggled' + pairs['window.button.toggled.unfocus'] = \ + 'window.inactive.button.toggled' + pairs['window.button.focus'] = 'window.active.button' + pairs['window.button.unfocus'] = 'window.inactive.button' + pairs['window.button.hover.focus'] = 'window.active.button.hover' + pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover' + pairs['window.label.unfocus'] = 'window.inactive.label' + pairs['window.label.focus'] = 'window.active.label' + pairs['window.button.focus'] = 'window.active.button.unpressed' + pairs['menu.disabled'] = 'menu.items.disabled' + pairs['menu.selected'] = 'menu.items.active' + pairs['window.button.unfocus'] = 'window.inactive.button.unpressed' + pairs['window.button.pressed.focus'] = 'window.active.button.pressed' + pairs['window.button.pressed.unfocus'] = 'window.inactive.button.pressed' + pairs['window.button.disabled.focus'] = 'window.active.button.disabled' + pairs['window.button.disabled.unfocus'] = 'window.inactive.button.disabled' + pairs['window.button.hover.focus'] = 'window.active.button.hover' + pairs['window.button.hover.unfocus'] = 'window.inactive.button.hover' + pairs['window.button.toggled.focus'] = 'window.active.button.toggled' + pairs['window.button.toggled.unfocus'] = 'window.inactive.button.toggled' + + for k in pairs.keys(): + while 1: + i, key, nul = find_key(data, k); + if i >= 0: + newl = data[i].replace(k, pairs[k]) + out('Updating "' + key + + '" to "' + key.replace(k, pairs[k]) + '"\n') + data[i] = newl + else: + break + +def replace_colors(data): + i = 0 + n = len(data) + while i < n: + l = data[i] + key, value = getkeyval(l) + if key and value: + if key.find('.color') != -1: + if key.find('client.color') == -1 \ + and key.find('image.color') == -1 \ + and key.find('bg.color') == -1 \ + and key.find('border.color') == -1 \ + and key.find('interlace.color') == -1 \ + and key.find('text.color') == -1: + newl = data[i].replace('.color', '.bg.color') + out('Updating "' + key + + '" to "' + key.replace('.color', '.bg.color') + '"\n') + data[i] = newl + if key.find('.border.color') != -1 \ + and key.find('bg.border.color') == -1: + newl = data[i].replace('.border.color', '.bg.border.color') + out('Updating "' + key + + '" to "' + key.replace('.border.color', + '.bg.border.color') + '"\n') + data[i] = newl + if key.find('.interlace.color') != -1 \ + and key.find('bg.interlace.color') == -1: + newl = data[i].replace('.interlace.color', + '.bg.interlace.color') + out('Updating "' + key + + '" to "' + key.replace('.interlace.color', + '.bg.interlace.color') + '"\n') + data[i] = newl + i += 1 + +def remove(data): + invalid = [] + invalid.append('toolbar') + for inv in invalid: + while 1: + i, key, nul = find_key(data, inv) + if i >= 0: + out(key + ' is no longer supported.\nRemove (Y/n)? ') + if read_bool(): + out('Removing "' + key + '"\n') + data.pop(i) + else: + break + invalid.append('rootCommand') + invalid.append('menu.bullet') + invalid.append('menu.bullet.image.color') + invalid.append('menu.bullet.selected.image.color') + invalid.append('menu.frame.justify') + for inv in invalid: + while 1: + i, key, nul = find_key(data, inv, True) + if i >= 0: + out(key + ' is no longer supported.\nRemove (Y/n)? ') + if read_bool(): + out('Removing "' + key + '"\n') + data.pop(i) + else: + break + +def pressed(data): + i, nul, nul = find_key(data, 'window.button.pressed', True) + if i >= 0: + out('The window.button.pressed option has been replaced by ' + + 'window.button.pressed.focus and ' + + 'window.button.pressed.unfocus.\nUpdate (Y/n)? ') + if read_bool(): + l = data[i] + out('Removing "window.button.pressed"\n') + data.pop(i) + out('Adding "window.button.pressed.unfocus"\n') + data.insert(i, l.replace('window.button.pressed', + 'window.button.pressed.unfocus')) + out('Adding "window.button.pressed.focus"\n') + data.insert(i, l.replace('window.button.pressed', + 'window.button.pressed.focus')) + +def x_fonts(data): + i, nul, nul = find_key(data, 'window.font') + if i >= 0: + out('You appear to specify fonts using the old X fonts ' + + 'syntax.\nShall I remove all fonts from the theme (Y/n)? ') + if not read_bool(): + return + else: return + while 1: + i, key, nul = find_key(data, '.font') + if i < 0: + break + out('Removing "' + key + '"\n') + data.pop(i) + +def xft_fonts(data): + i, nul, nul = find_key(data, '.xft.') + if i >= 0: + out('You appear to specify fonts using the old Xft fonts ' + + 'syntax.\nShall I update these to the new syntax (Y/n)? ') + if not read_bool(): + return + else: return + fonts = {} + fonts['window'] = 'window.label.focus.font' + fonts['menu.items'] = 'menu.items.font' + fonts['menu.title'] = 'menu.title.font' + for f in fonts.keys(): + li, nul, flags = find_key(data, f + '.xft.flags') + if li < 0: + li, nul, flags = find_key(data, '*.xft.flags') + else: + out('Removing ' + f + '.xft.flags\n') + data.pop(li) + oi, nul, offset = find_key(data, f + '.xft.shadow.offset') + if oi < 0: + oi, nul, offset = find_key(data, '*.xft.shadow.offset') + else: + out('Removing ' + f + '.xft.shadow.offset\n') + data.pop(oi) + ti, nul, tint = find_key(data, f + '.xft.shadow.tint') + if ti < 0: + ti, nul, tint = find_key(data, '*.xft.shadow.tint') + else: + out('Removing ' + f + '.xft.shadow.tint\n') + data.pop(ti) + fi, nul, face = find_key(data, f + '.xft.font') + if fi < 0: + fi, nul, face = find_key(data, '*.xft.font') + if fi >= 0: fi = len(data) - 1 + else: + out('Removing ' + f + '.xft.font\n') + data.pop(fi) + + if fi >= 0: + s = face + if li >= 0: + if flags.find('bold'): + s = s + ':bold' + if flags.find('shadow'): + s = s + ':shadow=y' + if oi >= 0: + s = s + ':shadowoffset=' + offset + if ti >= 0: + s = s + ':shadowtint=' + tint + out('Adding ' + fonts[f] + '\n') + data.insert(fi, fonts[f] + ': ' + s) + + for stars in ('*.xft.flags', '*.xft.shadow.offset' , + '*.xft.shadow.tint', '*.xft.font'): + i, key, nul = find_key(data, stars) + if i >= 0: + out('Removing ' + key + '\n') + data.pop(i) + +def pixelsize(data): + fonts = ('window.label.focus.font', + 'menu.items.font', + 'menu.title.font') + for f in fonts: + i, key, value = find_key(data, f, True) + if value: + if value.find('pixelsize') == -1: + out('*** ERROR *** The ' + key + ' font size is not being ' + 'specified by pixelsize. It is recommended that you use ' + 'pixelsize instead of pointsize for specifying theme ' + 'fonts. e.g. "sans:pixelsize=12"\n') + global valid + valid = False + +def warn_missing(data): + need = ('window.active.button.hover', 'window.inactive.button.hover', + 'menu.overlap') + for n in need: + i, nul, nul = find_key(data, n) + if i < 0: + out('The ' + n + ' value was not found in the theme, but it ' + 'can optionally be set.\n') + +def err_missing(data): + need = ('window.active.button.disabled', + 'window.inactive.button.disabled', + 'window.active.client.color', + 'window.inactive.client.color') + for n in need: + i, nul, nul = find_key(data, n) + if i < 0: + out('*** ERROR *** The ' + n + ' value was not found in the ' + 'theme, but it is required to be set.\n') + global valid + valid = False + + +def usage(): + out('Usage: themupdate.py /path/to/themerc > newthemerc\n\n') + sys.exit() + +try: + file = open(sys.argv[1]) +except IndexError: + usage() +except IOError: + out('Unable to open file "' + sys.argv[1] + '"\n\n') + usage() + +data = file.readlines() +for i in range(len(data)): + data[i] = data[i].strip() + +simple_replace(data) +replace_colors(data) +remove(data) +pressed(data) +x_fonts(data) +xft_fonts(data) +pixelsize(data) +warn_missing(data) +err_missing(data) + +for l in data: + print l + +sys.exit(not valid) diff --git a/version.h.in b/version.h.in new file mode 100644 index 0000000..0602a2a --- /dev/null +++ b/version.h.in @@ -0,0 +1,6 @@ +#ifndef ob__version_h +#define ob__version_h + +#define OPENBOX_VERSION "@OB_VERSION@" + +#endif |