From feaf3ac4e5847d27e3747b09e7443915afa97b0b Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 19 Feb 2003 19:28:11 +0000 Subject: [PATCH] time to refactor shit hard --- scripts/behavior.py | 2 + scripts/callbacks.py | 11 ++ scripts/config.py | 207 ++++++++++++++++++++++++++++++++++++ scripts/focus.py | 41 +++---- scripts/focusmodel.py | 2 + scripts/historyplacement.py | 102 +++++++++++------- scripts/motion.py | 205 ++++++++++++++++++++++------------- scripts/windowplacement.py | 12 +-- src/actions.cc | 6 +- src/config.cc | 67 +----------- src/config.hh | 25 +---- src/frame.cc | 4 +- src/openbox.cc | 64 ++++------- src/openbox.hh | 17 ++- src/python.cc | 75 +++++++++++++ src/python.hh | 5 + src/screen.cc | 5 +- src/screen.hh | 7 -- wrap/ob_python.i | 6 +- wrap/ob_screen.i | 1 - 20 files changed, 576 insertions(+), 288 deletions(-) diff --git a/scripts/behavior.py b/scripts/behavior.py index b29c655e..fd79b4f8 100644 --- a/scripts/behavior.py +++ b/scripts/behavior.py @@ -110,4 +110,6 @@ def setup_scroll(): ob.mbind("C-A-Down", ob.MouseContext.Frame, ob.MouseAction.Click, callbacks.send_to_prev_desktop) +export_functions = setup_window_clicks, setup_window_buttons, setup_scroll + print "Loaded behavior.py" diff --git a/scripts/callbacks.py b/scripts/callbacks.py index dbd077d5..0264626b 100644 --- a/scripts/callbacks.py +++ b/scripts/callbacks.py @@ -270,4 +270,15 @@ def exit(data=0): """Exits Openbox.""" ob.openbox.shutdown() +export_functions = iconify, restore, close, focus, raise_win, lower_win, \ + toggle_maximize, toggle_maximize_horz, \ + toggle_maximize_vert, maximize, maximize_horz, \ + maximize_vert, unmaximize, unmaximize_horz, \ + unmaximize_vert, toggle_shade, shade, unshade, \ + change_desktop, show_desktop, hide_desktop, \ + toggle_show_desktop, next_desktop, prev_desktop, \ + up_desktop, down_desktop, left_desktop, right_desktop, \ + send_to_desktop, toggle_all_desktops, send_to_all_desktops,\ + send_to_next_desktop, send_to_prev_desktop, restart, exit + print "Loaded callbacks.py" diff --git a/scripts/config.py b/scripts/config.py index 704a1276..97a45596 100644 --- a/scripts/config.py +++ b/scripts/config.py @@ -1,3 +1,210 @@ +# Openbox's config system. Please use the defined functions instead of +# accessing the internal data structures directly, for the sake of us all. + +import ob + +def add(modulename, name, friendlyname, description, type, default, + **keywords): + """Add a variable to the configuration system. + + Add a variable to the configuration system for a module. + modulename - The name of the module, e.g. 'focus' + name - The name of the variable, e.g. 'my_variable' + friendlyname - The user-friendly name of the variable, e.g. 'My Variable' + description - The detailed destription of the variable, e.g. 'Does Things' + type - The type of the variable, one of: + - 'boolean' + - 'enum' + - 'integer' + - 'string' + - 'file' + - 'function' + - 'object' + default - The default value for the variable, e.g. 300 + keywords - Extra keyword=value pairs to further define the variable. These + can be: + - For 'enum' types: + - options : A list of possible options for the variable. + This *must* be set for all enum variables. + - For 'integer' types: + - min : The minimum value for the variable. + - max : The maximum value for the variable. + """ + modulename = str(modulename).lower() + name = str(name).lower() + friendlyname = str(friendlyname) + description = str(description) + type = str(type).lower() + + # make sure the sub-dicts exist + try: + _settings[modulename] + try: + _settings[modulename][name] + except KeyError: + _settings[modulename][name] = {} + except KeyError: + _settings[modulename] = {} + _settings[modulename][name] = {} + + # add the keywords first as they are used for the tests in set() + for key,value in zip(keywords.keys(), keywords.values()): + _settings[modulename][name][key] = value + + _settings[modulename][name]['name'] = friendlyname + _settings[modulename][name]['description'] = description + _settings[modulename][name]['type'] = type + _settings[modulename][name]['default'] = default + + # put it through the tests + try: + set(modulename, name, default) + except: + del _settings[modulename][name] + import sys + raise sys.exc_info()[0], sys.exc_info()[1] # re-raise it + +def set(modulename, name, value): + """Set a variable's value. + + Sets the value for a variable of the specified module. + modulename - The name of the module, e.g. 'focus' + name - The name of the variable, e.g. 'my_variable' + value - The new value for the variable. + """ + modulename = str(modulename).lower() + name = str(name).lower() + + # proper value checking for 'boolean's + if _settings[modulename][name]['type'] == 'boolean': + if not (value == 0 or value == 1): + raise ValueError, 'Attempted to set ' + name + ' to a value of '+\ + str(value) + ' but boolean variables can only contain 0 or'+\ + ' 1.' + + # proper value checking for 'enum's + elif _settings[modulename][name]['type'] == 'enum': + options = _settings[modulename][name]['options'] + if not value in options: + raise ValueError, 'Attempted to set ' + name + ' to a value of '+\ + str(value) + ' but this is not one of the possible values '+\ + 'for this enum variable. Possible values are: ' +\ + str(options) + "." + + # min/max checking for 'integer's + elif _settings[modulename][name]['type'] == 'integer': + try: + min = _settings[modulename][name]['min'] + if value < min: + raise ValueError, 'Attempted to set ' + name + ' to a value '+\ + ' of ' + str(value) + ' but it has a minimum value ' +\ + ' of ' + str(min) + '.' + except KeyError: pass + try: + max = _settings[modulename][name]['max'] + if value > max: + raise ValueError, 'Attempted to set ' + name + ' to a value '+\ + ' of ' + str(value) + ' but it has a maximum value ' +\ + ' of ' + str(min) + '.' + except KeyError: pass + + _settings[modulename][name]['value'] = value + +def reset(modulename, name): + """Reset a variable to its default value. + + Resets the value for a variable in the specified module back to its + original (default) value. + modulename - The name of the module, e.g. 'focus' + name - The name of the variable, e.g. 'my_variable' + """ + modulename = str(modulename).lower() + name = str(name).lower() + _settings[modulename][name]['value'] = \ + _settings[modulename][name]['default'] + +def get(modulename, name): + """Returns the value of a variable. + + Returns the current value for a variable in the specified module. + modulename - The name of the module, e.g. 'focus' + name - The name of the variable, e.g. 'my variable' + """ + modulename = str(modulename).lower() + name = str(name).lower() + return _settings[modulename][name]['value'] + +#---------------------------- Internals --------------------------- + +"""The main configuration dictionary, which holds sub-dictionaries for each + module. + + The format for entries in here like this (for a string): + _settings['modulename']['varname']['name'] = 'Text Label' + _settings['modulename']['varname']['description'] = 'Does this' + _settings['modulename']['varname']['type'] = 'string' + _settings['modulename']['varname']['default'] = 'Foo' + _settings['modulename']['varname']['value'] = 'Foo' + # 'value' should always be initialized to the same + # value as the 'default' field! + + Here's an example of an enum: + _settings['modulename']['varname']['name'] = 'My Enum Variable' + _settings['modulename']['varname']['description'] = 'Does Enum-like things.' + _settings['modulename']['varname']['type'] = 'enum' + _settings['modulename']['varname']['default'] = \ + _settings['modulename']['varname']['value'] = [ 'Blue', 'Green', 'Pink' ] + + And Here's an example of an integer with bounds: + _settings['modulename']['varname']['name'] = 'A Bounded Integer' + _settings['modulename']['varname']['description'] = 'A fierce party animal!' + _settings['modulename']['varname']['type'] = 'integer' + _settings['modulename']['varname']['default'] = \ + _settings['modulename']['varname']['value'] = 0 + _settings['modulename']['varname']['min'] = 0 + _settings['modulename']['varname']['max'] = 49 + + Hopefully you get the idea. + """ +_settings = {} + +"""Valid values for a variable's type.""" +_types = [ 'boolean', # Boolean types can only hold a value of 0 or 1. + + 'enum', # Enum types hold a value from a list of possible values. + # An 'options' field *must* be provided for enums, + # containing a list of possible values for the variable. + + 'integer', # Integer types hold a single number, as well as a 'min' + # and 'max' property. + # If the 'min' or 'max' is ignore then bounds checking + # will not be performed in that direction. + + 'string', # String types hold a text string. + + 'file', # File types hold a file object. + + 'function',# Function types hold any callable object. + + 'object' # Object types can hold any python object. + ]; + + + + + + + + + + + + + + + + + ############################################################################# ### Options that can be changed to adjust the behavior of Openbox. ### ############################################################################# diff --git a/scripts/focus.py b/scripts/focus.py index 0dfd3fb8..9eb6bfb8 100644 --- a/scripts/focus.py +++ b/scripts/focus.py @@ -2,26 +2,28 @@ ### Functions for helping out with your window focus. ### ########################################################################### -########################################################################### -### Options that affect the behavior of the focus module. ### -########################################################################### -AVOID_SKIP_TASKBAR = 1 -"""Don't focus windows which have requested to not be displayed in taskbars. - You will still be able to focus the windows, but not through cycling, and - they won't be focused as a fallback if 'fallback' is enabled.""" -FALLBACK = 0 -"""Send focus somewhere when nothing is left with the focus, if possible.""" -########################################################################### - -########################################################################### -########################################################################### +import config, ob -########################################################################### -### Internal stuff, should not be accessed outside the module. ### -########################################################################### +export_functions = () -import ob +config.add('focus', + 'avoid_skip_taskbar', + 'Avoid SkipTaskbar Windows', + "Don't focus windows which have requested to not be displayed " + \ + "in taskbars. You will still be able to focus the windows, but " + \ + "not through cycling, and they won't be focused as a fallback " + \ + "if 'Focus Fallback' is enabled.", + 'boolean', + 1) +config.add('focus', + 'fallback', + 'Focus Fallback', + "Send focus somewhere when nothing is left with the focus, if " + \ + "possible.", + 'boolean', + 1) + # maintain a list of clients, stacked in focus order _clients = [] _skip = 0 @@ -30,7 +32,8 @@ def _focusable(client, desktop): if not client.normal(): return 0 if not (client.canFocus() or client.focusNotify()): return 0 if client.iconic(): return 0 - if AVOID_SKIP_TASKBAR and client.skipTaskbar(): return 0 + if config.get('focus', 'avoid_skip_taskbar') and \ + client.skipTaskbar(): return 0 desk = client.desktop() if not (desk == 0xffffffff or desk == desktop): return 0 @@ -60,7 +63,7 @@ def _focused(data): _remove(data.client) except ValueError: pass # happens if _focused comes before _newwindow _clients.insert(0, data.client) - elif FALLBACK: + elif config.get('focus', 'fallback'): # pass around focus desktop = ob.openbox.screen(data.screen).desktop() for c in _clients: diff --git a/scripts/focusmodel.py b/scripts/focusmodel.py index 4b7bb38a..a0b6c927 100644 --- a/scripts/focusmodel.py +++ b/scripts/focusmodel.py @@ -56,4 +56,6 @@ def setup_sloppy_focus(click_focus = 1, click_raise = 0): ob.mbind("Left", ob.MouseContext.Window, ob.MouseAction.Press, callbacks.raise_win) +export_functions = setup_click_focus, setup_sloppy_focus + print "Loaded focusmodel.py" diff --git a/scripts/historyplacement.py b/scripts/historyplacement.py index 63f19f04..cb9fb96c 100644 --- a/scripts/historyplacement.py +++ b/scripts/historyplacement.py @@ -3,39 +3,63 @@ ### to the ob.EventAction.PlaceWindow event to use it. ### ############################################################################## -import windowplacement # fallback routines - -############################################################################## -### Options for the historyplacement module (Options in the ### -### windowplacement module also apply!) ### -############################################################################## -IGNORE_REQUESTED_POSITIONS = 0 -"""When non-zero, the placement algorithm will attempt to place windows even - when they request a position (like XMMS). Note this only applies to normal - windows, not to special cases like desktops and docks.""" -DONT_DUPLICATE = 1 -"""When non-zero, if 2 copies of the same match in history are to be placed - before one of them is closed (so it would be placed over-top of the last - one), this will cause the second window to not be placed via history, and - the FALLBACK will be used instead.""" -FALLBACK = windowplacement.random -"""The window placement algorithm that will be used when history placement - does not have a place for the window.""" -CONFIRM_CALLBACK = 0 -"""Set this to a function to have the function called before attempting to - place a window via history. If the function returns a non-zero, then an - attempt will be made to place the window. If it returns zero, the - FALLBACK method will be directly applied instead.""" -FILENAME = 'historydb' -"""The name of the file where history data will be stored. The number of - the screen is appended onto this filename.""" -############################################################################## +import windowplacement, config def place(data): """Place a window usingthe history placement algorithm.""" _place(data) -########################################################################### +export_functions = place + +############################################################################## + +config.add('historyplacement', + 'ignore_requested_positions', + 'Ignore Requested Positions', + "When true, the placement algorithm will attempt to place " + \ + "windows even when they request a position (like XMMS can)." + \ + "Note this only applies to 'normal' windows, not to special " + \ + "cases like desktops and docks.", + 'boolean', + 0) +config.add('historyplacement', + 'dont_duplicate', + "Don't Diplicate", + "When true, if 2 copies of the same match in history are to be " + \ + "placed before one of them is closed (so it would be placed " + \ + "over-top of the last one), this will cause the second window to "+\ + "not be placed via history, and the 'Fallback Algorithm' will be "+\ + "used instead.", + 'boolean', + 1) +config.add('historyplacement', + 'filename', + 'History Database Filename', + "The name of the file where history data will be stored. The " + \ + "number of the screen is appended onto this name. The file will " +\ + "be placed in ~/.openbox/.", + 'string', + 'historydb') +config.add('historyplacement', + 'fallback', + 'Fallback Algorithm', + "The window placement algorithm that will be used when history " + \ + "placement does not have a place for the window.", + 'enum', + windowplacement.random, + options = windowplacement.export_functions) +config.add('historyplacement', + 'confirm_callback', + 'Confirm Placement Callback', + "A function which will be called before attempting to place a " + \ + "window via history. If the function returns true, then an " + \ + "attempt will be made to place the window. If it returns false, " +\ + "the 'Fallback Algorithm' will be directly applied instead. The " +\ + "function must take 1 argument, which will be the callback data " +\ + "which was passed to invoke the window placement.", + 'function', + None) + ########################################################################### ########################################################################### @@ -67,8 +91,9 @@ class _state: def _load(data): global _data try: - file = open(os.environ['HOME'] + '/.openbox/' + FILENAME+"." + - str(data.screen), 'r') + file = open(os.environ['HOME'] + '/.openbox/' + \ + config.get('historyplacement', 'filename') + \ + "." + str(data.screen), 'r') # read data for line in file.readlines(): line = line[:-1] # drop the '\n' @@ -88,8 +113,9 @@ def _load(data): def _save(data): global _data - file = open(os.environ['HOME']+'/.openbox/'+FILENAME+"."+str(data.screen), - 'w') + file = open(os.environ['HOME']+'/.openbox/'+ \ + config.get('historyplacement', 'filename') + \ + "." + str(data.screen), 'w') if file: while len(_data)-1 < data.screen: _data.append([]) @@ -121,11 +147,13 @@ def _find(screen, state): def _place(data): global _data if data.client: - if not (IGNORE_REQUESTED_POSITIONS and data.client.normal()): + if not (config.get('historyplacement', 'ignore_requested_positions') \ + and data.client.normal()): if data.client.positionRequested(): return state = _create_state(data) try: - if not CONFIRM_CALLBACK or CONFIRM_CALLBACK(data): + confirm = config.get('historyplacement', 'confirm_callback') + if not confirm or confirm(data): print "looking for : " + state.appname + " : " + \ state.appclass + " : " + state.role @@ -134,7 +162,8 @@ def _place(data): coords = _data[data.screen][i] print "Found in history ("+str(coords.x)+","+\ str(coords.y)+")" - if not (DONT_DUPLICATE and coords.placed): + if not (config.get('historyplacement', 'dont_duplicate') \ + and coords.placed): data.client.move(coords.x, coords.y) coords.placed = 1 return @@ -144,7 +173,8 @@ def _place(data): print "No match in history" except TypeError: pass - if FALLBACK: FALLBACK(data) + fallback = config.get('historyplacement', 'fallback') + if fallback: fallback(data) def _save_window(data): global _data diff --git a/scripts/motion.py b/scripts/motion.py index 0ce865a3..e25604e5 100644 --- a/scripts/motion.py +++ b/scripts/motion.py @@ -3,48 +3,11 @@ ### resize windows. ### ############################################################################ -############################################################################# -### Options that can be modified to change the functions' behaviors. ### -############################################################################# -EDGE_RESISTANCE = 10 -"""The amount of resistance to provide to moving a window past a screen - boundary. Specify a value of 0 to disable edge resistance.""" -POPUP_IN_WINDOW = 0 -"""When this is non-zero, the coordinates popups will be placed relative to - the window being moved/resized. When zero, they will appear relative to the - entire screen.""" -POPUP_CENTERED = 1 -"""When this is non-zero, the coordinates popups will be centered relative to - the window or screen (see POPUP_IN_WINDOW). When zero, they will be placed - at based upon POPUP_COORDS.""" -POPUP_COORDS = 0, 0 -"""When POPUP_CENTERED is zero, these coordinates will be used to place the - coordinates popup. The popup will be placed relative to the window or the - screen (see POPUP_IN_WINDOW). A value of 0, 0 would place it in the top - left corner, while a value of -1, -1 would place it in the bottom right. - These values behave simmilarly to those passed to the -geometry flag of many - applications.""" -MOVE_POPUP = 1 -"""Display a coordinates popup when moving windows.""" -MOVE_RUBBERBAND = 0 -"""NOT IMPLEMENTED (yet?) - Display an outline while moving instead of moving the actual window, - until the move is completed. Good for slower systems.""" -RESIZE_POPUP = 1 -"""Display a size popup when resizing windows.""" -RESIZE_RUBBERBAND = 0 -"""NOT IMPLEMENTED (yet?) - Display an outline while resizing instead of resizing the actual - window, until the resize is completed. Good for slower systems.""" -RESIZE_NEAREST = 1 -"""Non-zero to resize from the corner nearest where the mouse is, 0 to - resize always from the bottom right corner.""" -############################################################################# - def move(data): """Moves the window interactively. This should only be used with - MouseAction.Motion events. If MOVE_POPUP or MOVE_RUBBERBAND is enabled, - then the end_move function needs to be bound as well.""" + MouseAction.Motion events. If 'Coords Popup for Moving' or 'Rubberband + Mode for Moving' is enabled, then the end_move function needs to be + bound as well.""" _move(data) def end_move(data): @@ -53,16 +16,107 @@ def end_move(data): def resize(data): """Resizes the window interactively. This should only be used with - MouseMotion events. If RESIZE_POPUP or RESIZE_RUBBERBAND is enabled, - then the end_resize function needs to be bound as well.""" + MouseMotion events. If 'Coords Popup for Resizing' or 'Rubberband Mode + for Resizing' is enabled, then the end_resize function needs to be + bound as well.""" _resize(data) def end_resize(data): """Complete the interactive resize of a window.""" _end_resize(data) -########################################################################### -########################################################################### +export_functions = move, end_move, resize, end_resize + +############################################################################# + +import config + +config.add('motion', + 'edge_resistance', + 'Edge Resistance', + "The amount of resistance to provide to moving a window past a " + \ + "screen boundary. Specify a value of 0 to disable edge resistance.", + 'integer', + 10, + min = 0) +config.add('motion', + 'popup_in_window', + 'Coords Popup In Window', + "When this is true, the coordinates popups will be placed " + \ + "relative to the window being moved/resized. When false, they " + \ + "will appear relative to the entire screen.", + 'boolean', + 0) +config.add('motion', + 'popup_centered', + 'Coords Popup Centered', + "When this is true, the coordinates popups will be centered " + \ + "relative to the window or screen (see 'Coords Popup In " + \ + "Window'). When false, they will be placed based upon the " + \ + "'Coords Popup Position' options.", + 'boolean', + 1) +config.add('motion', + 'popup_coords_x', + 'Coords Popup Position - X', + "When 'Coords Popup Centered' is false, this position will be " + \ + "used to place the coordinates popups. The popups will be " + \ + "placed relative to the window or the screen (see 'Coords " + \ + "Popup In Window'). A value of 0 would place it at the left " + \ + "edge, while a value of -1 would place it at the right edge. " + \ + "This value behaves similarly to those passed to the -geometry " + \ + "flag of many applications.", + 'integer', + 0) +config.add('motion', + 'popup_coords_y', + 'Coords Popup Position - Y', + "When 'Coords Popup Centered' is false, this position will be " + \ + "used to place the coordinates popups. The popups will be " + \ + "placed relative to the window or the screen (see 'Coords Popup " +\ + "In Window'). A value of 0 would place it at the top edge, " + \ + "while a value of -1 would place it at the bottom edge. This " + \ + "value behaves similarly to those passed to the -geometry flag " + \ + "of many applications.", + 'integer', + 0) +config.add('motion', + 'move_popup', + 'Coords Popup for Moving', + "Option to display a coordinates popup when moving windows.", + 'boolean', + 1) +config.add('motion', + 'move_rubberband', + 'Rubberband Mode for Moving', + "NOT IMPLEMENTED (yet?)\n"+\ + "Display an outline while moving instead of moving the actual " + \ + "window, until the move is completed. Good for slower systems.", + 'boolean', + 0) +config.add('motion', + 'resize_popup', + 'Coords Popup for Resizing', + "Option to display a coordinates popup when resizing windows.", + 'boolean', + 1) +config.add('motion', + 'resize_rubberband', + 'Rubberband Mode for Resizing', + "NOT IMPLEMENTED (yet?)\n"+\ + "Display an outline while resizing instead of resizing the " + \ + "actual window, until the resize is completed. Good for slower " + \ + "systems.", + 'boolean', + 0) +config.add('motion', + 'resize_nearest', + 'Resize Nearest Corner', + "When true, resizing will occur from the corner nearest where " + \ + "the mouse is. When false resizing will always occur from the " + \ + "bottom right corner.", + 'boolean', + 1) ########################################################################### ### Internal stuff, should not be accessed outside the module. ### @@ -92,19 +146,24 @@ _screen = 0 _motion_mask = 0 def _place_popup(): - if POPUP_IN_WINDOW: + if config.get('motion', 'popup_in_window'): + # use the actual client's area, not the frame's area = _client.frame.area() + size = _client.frame.size() + area = otk.Rect(area.x() + size.left, area.y() + size.top, + area.width() - size.left - size.right, + area.height() - size.top - size.bottom) else: area = otk.Rect(otk.Point(0, 0), ob.openbox.screen(_screen).size()) size = _popwidget.minSize() - if POPUP_CENTERED: + if config.get('motion', 'popup_centered'): x = area.position().x() + (area.size().width() - size.width()) / 2 y = area.position().y() + (area.size().height() - size.height()) / 2 else: - try: x, y = POPUP_COORDS - except: x = y = 0 - if x < 0: x += area.right() - size.width() + 2 - if y < 0: y += area.bottom() - size.height() + 2 + x = config.get('motion', 'popup_coords_x') + y = config.get('motion', 'popup_coords_y') + if x < 0: x += area.width() - size.width() + 1 + if y < 0: y += area.width() - size.height() + 1 x += area.position().x() y += area.position().y() _popwidget.moveresize(otk.Rect(x, y, size.width(), size.height())) @@ -132,7 +191,8 @@ def _do_move(final): y = _cy + _dy + _client.frame.area().y() - _client.area().y() global _last_x, _last_y - if EDGE_RESISTANCE: + resist = config.get('motion', 'edge_resistance') + if resist: fs = _client.frame.size() w = _client.area().width() + fs.left + fs.right h = _client.area().height() + fs.top + fs.bottom @@ -143,16 +203,16 @@ def _do_move(final): t = area.top() b = area.bottom() - h + 1 # left screen edge - if _last_x > x and x < l and x >= l - EDGE_RESISTANCE: + if _last_x > x and x < l and x >= l - resist: x = l # right screen edge - if _last_x < x and x > r and x <= r + EDGE_RESISTANCE: + if _last_x < x and x > r and x <= r + resist: x = r # top screen edge - if _last_y > y and y < t and y >= t - EDGE_RESISTANCE: + if _last_y > y and y < t and y >= t - resist: y = t # right screen edge - if _last_y < y and y > b and y <= b + EDGE_RESISTANCE: + if _last_y < y and y > b and y <= b + resist: y = b global _inmove @@ -163,13 +223,13 @@ def _do_move(final): _last_x = x _last_y = y - if MOVE_RUBBERBAND: - # draw the outline ... - f=0 + if not final and config.get('motion', 'move_rubberband'): + # XXX draw the outline ... + pass else: _client.move(x, y, final) - if MOVE_POPUP: + if config.get('motion', 'move_popup'): global _popwidget text = "X: " + str(x) + " Y: " + str(y) if not _popwidget: @@ -200,25 +260,22 @@ def _move(data): _inmove = 1 def _end_move(data): - global MOVE_RUBBERBAND global _inmove, _popwidget if _inmove: - r = MOVE_RUBBERBAND - MOVE_RUBBERBAND = 0 _do_move(1) - MOVE_RUBBERBAND = r _inmove = 0 _popwidget = 0 ob.kungrab() -def _do_resize(): +def _do_resize(final): global _screen, _client, _cx, _cy, _cw, _ch, _px, _py, _dx, _dy dx = _dx dy = _dy # pick a corner to anchor - if not (RESIZE_NEAREST or _context == ob.MouseContext.Grip): + if not (config.get('motion', 'resize_nearest') or + _context == ob.MouseContext.Grip): corner = ob.Client.TopLeft else: x = _px - _cx @@ -240,13 +297,13 @@ def _do_resize(): w = _cw + dx h = _ch + dy - if RESIZE_RUBBERBAND: - # draw the outline ... - f=0 + if not final and config.get('motion', 'resize_rubberband'): + # XXX draw the outline ... + pass else: _client.resize(corner, w, h) - if RESIZE_POPUP: + if config.get('motion', 'resize_popup'): global _popwidget ls = _client.logicalSize() text = "W: " + str(ls.width()) + " H: " + str(ls.height()) @@ -276,20 +333,16 @@ def _resize(data): _dx = data.xroot - _px _dy = data.yroot - _py _motion_mask = data.state - _do_resize() + _do_resize(0) global _inresize if not _inresize: ob.kgrab(_screen, _motion_grab) _inresize = 1 def _end_resize(data): - global RESIZE_RUBBERBAND, _inresize - global _popwidget + global _inresize, _popwidget if _inresize: - r = RESIZE_RUBBERBAND - RESIZE_RUBBERBAND = 0 - _do_resize() - RESIZE_RUBBERBAND = r + _do_resize(1) _inresize = 0 _popwidget = 0 ob.kungrab() diff --git a/scripts/windowplacement.py b/scripts/windowplacement.py index 493e526b..505993c5 100644 --- a/scripts/windowplacement.py +++ b/scripts/windowplacement.py @@ -7,15 +7,7 @@ ### these. ### ############################################################################ -############################################################################## -### Options for the windowplacement module: ### -### ### -### ### -############################################################################## - -import otk -import ob -import random +import otk, ob, random _rand = random.Random() @@ -53,4 +45,6 @@ def cascade(data): _cascade_x += frame_size.top _cascade_y += frame_size.top +export_functions = random, cascade + print "Loaded windowplacement.py" diff --git a/src/actions.cc b/src/actions.cc index 9659b2f0..c629006a 100644 --- a/src/actions.cc +++ b/src/actions.cc @@ -145,7 +145,8 @@ void Actions::buttonReleaseHandler(const XButtonEvent &e) data.action = MouseAction::Click; openbox->bindings()->fireButton(&data); - long dblclick = openbox->screen(screen)->config().double_click_delay; + long dblclick = 0; + python_get_long("double_click_delay", &dblclick); if (e.time - _release.time < (unsigned)dblclick && _release.win == e.window && _release.button == e.button) { @@ -303,7 +304,8 @@ void Actions::motionHandler(const XMotionEvent &e) if (!_dragging) { int dx = x_root - _press.pos.x(); int dy = y_root - _press.pos.y(); - long threshold = openbox->screen(screen)->config().drag_threshold; + long threshold = 0; + python_get_long("drag_threshold", &threshold); if (!(std::abs(dx) >= threshold || std::abs(dy) >= threshold)) return; // not at the threshold yet } diff --git a/src/config.cc b/src/config.cc index 04728de2..5c6ddfe9 100644 --- a/src/config.cc +++ b/src/config.cc @@ -2,70 +2,7 @@ #include "config.h" -#include "config.hh" -#include "otk/screeninfo.hh" -#include "otk/renderstyle.hh" -#include "otk/util.hh" -#include "otk/property.hh" -#include "otk/display.hh" - -extern "C" { -#include - -#include "gettext.h" -#define _(str) gettext(str) -} - -#include - -namespace ob { - -static PyObject *obdict = NULL; - -bool python_get_long(const char *name, long *value) -{ - PyObject *val = PyDict_GetItemString(obdict, const_cast(name)); - if (!(val && PyInt_Check(val))) return false; - - *value = PyInt_AsLong(val); - return true; -} - -bool python_get_string(const char *name, otk::ustring *value) -{ - PyObject *val = PyDict_GetItemString(obdict, const_cast(name)); - if (!(val && PyString_Check(val))) return false; - - std::string temp(PyString_AsString(val), PyString_Size(val)); - *value = temp; - return true; -} - -bool python_get_stringlist(const char *name, std::vector *value) -{ - PyObject *val = PyDict_GetItemString(obdict, const_cast(name)); - if (!(val && PyList_Check(val))) return false; - - value->clear(); - - for (int i = 0, end = PyList_Size(val); i < end; ++i) { - PyObject *str = PyList_GetItem(val, i); - if (PyString_Check(str)) - value->push_back(PyString_AsString(str)); - } - return true; -} - -void Config::load() -{ - const otk::ScreenInfo *info = otk::display->screenInfo(_screen); - Window root = info->rootWindow(); - - // set up access to the python global variables - PyObject *obmodule = PyImport_ImportModule("config"); - obdict = PyModule_GetDict(obmodule); - Py_DECREF(obmodule); - +/* python_get_stringlist("DESKTOP_NAMES", &desktop_names); python_get_string("THEME", &theme); @@ -121,4 +58,4 @@ Config::~Config() { } -} +*/ diff --git a/src/config.hh b/src/config.hh index 6cbbaacd..4f664c19 100644 --- a/src/config.hh +++ b/src/config.hh @@ -3,33 +3,12 @@ #define __config_hh /*! @file config.hh - @brief The Config class contains configuration options set by the user's - scripts + @brief The Config class contains functions for accessing config variables + in the scripts. */ -#include "otk/ustring.hh" - -#include - namespace ob { -class Config { - int _screen; - -public: - std::vector desktop_names; - otk::ustring theme; - otk::ustring titlebar_layout; - long double_click_delay; - long drag_threshold; - long num_desktops; - - Config(int screen); - ~Config(); - - void load(); -}; - } #endif // __config_hh diff --git a/src/frame.cc b/src/frame.cc index 94384b3d..4e77d106 100644 --- a/src/frame.cc +++ b/src/frame.cc @@ -9,6 +9,7 @@ extern "C" { } #include "frame.hh" +#include "config.hh" #include "openbox.hh" #include "otk/display.hh" #include "otk/surface.hh" @@ -99,7 +100,8 @@ Frame::Frame(Client *client) applyStyle(*otk::RenderStyle::style(_client->screen())); - _layout = openbox->screen(_client->screen())->config().titlebar_layout; + _layout = "ITMC"; + python_get_string("titlebar_layout", &_layout); // register all of the windows with the event dispatcher Window *w = allWindows(); diff --git a/src/openbox.cc b/src/openbox.cc index 951d4ab4..d2ce85fd 100644 --- a/src/openbox.cc +++ b/src/openbox.cc @@ -134,6 +134,7 @@ Openbox::Openbox(int argc, char **argv) // initialize all the screens _focused_screen = 0; + _managed_count = 0; for (int i = 0, max = ScreenCount(**otk::display); i < max; ++i) { Screen *screen; @@ -148,77 +149,52 @@ Openbox::Openbox(int argc, char **argv) _screens.push_back(screen); if (!_focused_screen) // set this to the first screen managed _focused_screen = screen; + _managed_count++; } else { delete screen; _screens.push_back(0); } } - if (_screens.empty()) { + if (!_managed_count) { printf(_("No screens were found without a window manager. Exiting.\n")); ::exit(1); } assert(_focused_screen); - ScreenList::iterator it, end = _screens.end(); - - // run the user's script or the system defaults if that fails - bool pyerr, doretry; - do { - // initialize scripting - python_init(argv[0]); - - // load all of the screens' configs here so we have a theme set if - // we decide to show the dialog below - for (it = _screens.begin(); it != end; ++it) - (*it)->config().load(); // load the defaults from config.py - - pyerr = doretry = false; - - // reset all the python stuff - _bindings->removeAllKeys(); - _bindings->removeAllButtons(); - _bindings->removeAllEvents(); - - int ret = python_exec(_scriptfilepath.c_str()); - if (ret == 2) - pyerr = true; - - if (ret) { - // reset all the python stuff - _bindings->removeAllKeys(); - _bindings->removeAllButtons(); - _bindings->removeAllEvents(); - - if (python_exec(SCRIPTDIR"/defaults.py")) // system default bahaviors - pyerr = true; - } + // initialize scripting + python_init(argv[0]); - if (pyerr) { + // load the theme XXX TEMP SHIT + otk::RenderStyle::setStyle(0, ""); + + int ret = python_exec(_scriptfilepath.c_str()); + if (ret == 2) { std::string msg; msg += _("An error occured while executing the python scripts."); msg += "\n\n"; msg += _("See the exact error message in Openbox's output for details."); otk::MessageDialog dia(this, _("Python Error"), msg); otk::DialogButton ok(_("Okay"), true); - otk::DialogButton retry(_("Retry")); + otk::DialogButton retry(_("Restart")); dia.addButton(ok); dia.addButton(retry); dia.show(); dia.focus(); const otk::DialogButton &res = dia.run(); if (res == retry) { - doretry = true; - python_destroy(); // kill all the python modules so they reinit right + _restart = _shutdown = true; + return; } - } - } while (pyerr && doretry); - - for (it = _screens.begin(); it != end; ++it) { - (*it)->config().load(); // load the config as the scripts may change it - (*it)->manageExisting(); } + + if (ret) + python_exec(SCRIPTDIR"/defaults.py"); // system default bahaviors + + ScreenList::iterator it, end = _screens.end(); + for (it = _screens.begin(); it != end; ++it) + if (*it) (*it)->manageExisting(); // grab any keys set up before the screens existed //_bindings->grabKeys(true); diff --git a/src/openbox.hh b/src/openbox.hh index eb3914aa..460caa0c 100644 --- a/src/openbox.hh +++ b/src/openbox.hh @@ -102,6 +102,9 @@ private: //! A list of all the managed screens ScreenList _screens; + + //! The number of managed screens + int _managed_count; //! The action interface through which all user-available actions occur Actions *_actions; @@ -175,10 +178,22 @@ public: */ inline Screen *screen(int num) { assert(num >= 0); assert(num < (signed)ScreenCount(**otk::display)); - if (num >= (signed)_screens.size()) return 0; + if (num < 0 || num >= (signed)_screens.size()) return 0; return _screens[num]; } + inline int managedScreenCount() const { return _managed_count; } + + inline Screen *managedScreen(int num) { + assert(num >= 0); assert(num < _managed_count); + if (num < 0 || num >= _managed_count) return 0; + ScreenList::iterator it, end = _screens.end(); + int i = -1; + for (it = _screens.begin(); it != end; ++it) + if (*it && ++i == num) + return *it; + } + //! Returns the mouse cursors used throughout Openbox inline const Cursors &cursors() const { return _cursors; } diff --git a/src/python.cc b/src/python.cc index 0741f2da..75b7cc13 100644 --- a/src/python.cc +++ b/src/python.cc @@ -17,6 +17,8 @@ extern "C" { namespace ob { +static PyObject *get = NULL; + void python_init(char *argv0) { // start the python engine @@ -28,6 +30,21 @@ void python_init(char *argv0) PyRun_SimpleString(const_cast(("sys.path.insert(0, '" + otk::expandTilde("~/.openbox/python") + "')").c_str())); + + return; + PyObject *obmodule = PyImport_ImportModule("config"); + if (obmodule == NULL) { + PyErr_Print(); + return; + } + PyObject *configdict = PyModule_GetDict(obmodule); + Py_DECREF(obmodule); + + get = PyDict_GetItemString(configdict, "get"); + if (get == NULL) { + PyErr_Print(); + return; + } } void python_destroy() @@ -63,4 +80,62 @@ int python_exec(const std::string &path) return ret; } +bool python_get_long(const char *name, long *value) +{ + return false; + if (get == NULL) return false; + bool ret = false; + + PyObject *val = PyObject_CallFunction(get, "ss", "openbox", name); + if (val == NULL) + PyErr_Print(); + else if (PyInt_Check(val)) { + *value = PyInt_AsLong(val); + ret = true; + } else if (PyLong_Check(val)) { + *value = PyLong_AsLong(val); + ret = true; + } + Py_XDECREF(val); + return ret; +} + +bool python_get_string(const char *name, otk::ustring *value) +{ + return false; + if (get == NULL) return false; + bool ret = false; + + PyObject *val = PyObject_CallFunction(get, "ss", "openbox", name); + if (val == NULL) + PyErr_Print(); + else if (PyString_Check(val)) { + *value = std::string(PyString_AsString(val), PyString_Size(val)); + ret = true; + } + Py_XDECREF(val); + return ret; +} + +bool python_get_stringlist(const char *name, std::vector *value) +{ + return false; + if (get == NULL) return false; + bool ret = false; + + PyObject *val = PyObject_CallFunction(get, "ss", "openbox", name); + if (val == NULL) + PyErr_Print(); + else if (PyList_Check(val)) { + for (int i = 0, end = PyList_Size(val); i < end; ++i) { + PyObject *str = PyList_GET_ITEM(val, i); + if (PyString_Check(str)) + value->push_back(std::string(PyString_AsString(str), + PyString_Size(str))); + } + } + Py_XDECREF(val); + return ret; +} + } diff --git a/src/python.hh b/src/python.hh index 6acc538b..c1655503 100644 --- a/src/python.hh +++ b/src/python.hh @@ -19,6 +19,7 @@ extern "C" { #include #include + namespace ob { class Client; @@ -232,6 +233,10 @@ void python_destroy(); //! Returns 0 for success, 1 for failing to open the file, 2 for an exception int python_exec(const std::string &path); +bool python_get_long(const char *name, long *value); +bool python_get_string(const char *name, otk::ustring *value); +bool python_get_stringlist(const char *name, std::vector *value); + } diff --git a/src/screen.cc b/src/screen.cc index ff78e572..16e37df6 100644 --- a/src/screen.cc +++ b/src/screen.cc @@ -40,8 +40,7 @@ namespace ob { Screen::Screen(int screen) - : _number(screen), - _config(screen) + : _number(screen) { assert(screen >= 0); assert(screen < ScreenCount(**otk::display)); _info = otk::display->screenInfo(screen); @@ -80,7 +79,7 @@ Screen::Screen(int screen) _desktop = 0; - changeNumDesktops(1); // set the hint + changeNumDesktops(4); // set the hint changeDesktop(0); // set the hint // don't start in showing-desktop mode diff --git a/src/screen.hh b/src/screen.hh index 2f026083..20ba0a63 100644 --- a/src/screen.hh +++ b/src/screen.hh @@ -10,7 +10,6 @@ extern "C" { #include } -#include "config.hh" #include "otk/strut.hh" #include "otk/rect.hh" #include "otk/screeninfo.hh" @@ -67,9 +66,6 @@ private: //! Information about this screen const otk::ScreenInfo *_info; - //! Configuration options from the user scripts - Config _config; - //! Area usable for placement etc (total - struts), one per desktop, //! plus one extra for windows on all desktops RectList _area; @@ -160,9 +156,6 @@ public: */ inline bool managed() const { return _managed; } - //! Returns the config options set by the user scripts - Config& config() { return _config; } - //! An offscreen window which gets focus when nothing else has it inline Window focuswindow() const { return _focuswindow; } //! Returns the desktop being displayed diff --git a/wrap/ob_python.i b/wrap/ob_python.i index 66fd83e9..6236779e 100644 --- a/wrap/ob_python.i +++ b/wrap/ob_python.i @@ -17,7 +17,11 @@ namespace ob { %ignore python_init(char*); %ignore python_destroy(); -%ignore python_exec(const std::string &); +%ignore python_exec(const std::string&); + +%ignore python_get_long(const char*, long*); +%ignore python_get_string(const char*, otk::ustring*); +%ignore python_get_stringlist(const char*, std::vector*); } diff --git a/wrap/ob_screen.i b/wrap/ob_screen.i index b182d2d7..20ca9828 100644 --- a/wrap/ob_screen.i +++ b/wrap/ob_screen.i @@ -88,7 +88,6 @@ namespace ob { %ignore Screen::~Screen(); %ignore Screen::focuswindow() const; %ignore Screen::managed() const; -%ignore Screen::config(); %rename(ignored_showDesktop) Screen::showDesktop(bool show); %ignore Screen::ignored_showDesktop(bool show); %ignore Screen::updateStruts(); -- 2.39.2