Skip to content

Commit d18ddd9

Browse files
committed
Documentation improvements. Small improvements to window flashing. Still issues with portal chrome not being cleaned up on layout change.
1 parent 0172e42 commit d18ddd9

14 files changed

+216
-38
lines changed

CHANGES.md

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* Added "resizable" flag for applications.
88
* Added "snap" option to portals.
99
* detect-monitors command now supports different output formats.
10+
* Added user extensions directory support.
11+
* Changed how you specify the layout argument at the command line.
1012
* Documentation improvements.
1113
* Bug fixes.
1214

@@ -28,6 +30,18 @@
2830
* Run with "-f (format)" to output in a different format.
2931
* Supported formats are: `yaml`, `json`, and `py`.
3032
* Format now defaults to `yaml` instead of python.
33+
* Added user extensions directory support.
34+
* You can either use the `%PETRONIA_USER_DIR%` environment variable, or
35+
the `-e` command-line argument, to specify where Petronia should look
36+
for user extensions. This allows for adding in components to your
37+
configuration.
38+
* Changed how you specify the layout argument at the command line.
39+
* It used to be that the layout name was the optional second argument. Now,
40+
you must specify it with the `-l` or `--layout` argument.
41+
* The check-layout command is unchanged.
42+
* Command-line parsing for cmd and main now use argparse to grant the
43+
ability to have more complex arguments. Unfortunately, this lead to
44+
the backwards incompatibility with the layout name.
3145
* Documentation improvements.
3246
* Cleaning up to better reflect the current version.
3347
* Bug fixes.

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ Once you have your configuration setup, you just run in a cmd prompt:
115115
```cmd
116116
> set Path=(python 3.5.2 directory);%Path%
117117
> cd (petronia dir)\src
118-
> python -u -m petronia.cmd my_config_file.py
118+
> python -u -m petronia.cmd my_config_file.yaml
119119
```
120120

121121
Press <kbd>&crarr; Enter</kbd> in the command window to stop the program.

docs/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Words for Users
22

33
* [Getting Started](user-getting-started.md)
4+
* [Guide to Executables](user-execs.md)
45
* Configuration:
56
* [Configuration Guide](user-configuration.md)
67
* [Configuration Through Python](user-configuration-py.md)

docs/dev-distribution.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
Petronia has been designed to be a simple to distribute project.
44

5+
## Release Preparation
6+
7+
1. Make sure `version.py` is set to the right version number.
8+
2. Make sure all commits are in github.
9+
3. CHANGES.md has the change notes for the version.
10+
11+
512
## Source Release
613

714
Some users are okay with installing and running Python. For those them, they
@@ -29,4 +36,5 @@ In a command prompt, you would run:
2936
```
3037

3138
This will create the `buildsrc\work\petronia-x86.zip` and
32-
`buildsrc\work\petronia-x64.zip` files.
39+
`buildsrc\work\petronia-x64.zip` files. Rename these to include the
40+
version number (like `petronia-3.4-x64.zip`).

docs/user-custom-commands.md

+27-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11

2-
## Custom Commands
2+
# Custom Commands
3+
4+
The hotkeys allow for running commands, but these commands are limited to
5+
the built-in set of commands provided by Petronia. However, it is possible
6+
to write your own commands.
7+
8+
Additional commands are added to the configuration, and can be referenced
9+
by the hotkey configuration like any other built-in command.
10+
11+
12+
## Adding Commands through Plugins
13+
14+
If you're using the [components](user-components.md), you can add in a
15+
singleton component that registers new commands.
16+
17+
The plugins themselves would add in the command to the configuration object
18+
passed to the `get_factory` function, as `config.commands.add_command(cmd)`.
19+
20+
21+
## Adding Commands through the Python Configuration
22+
23+
*TODO* This is described below, but needs to be restructured.
24+
25+
26+
## Writing Commands
327

428
The *command* configuration object (`CommandConfig`) allows for registering
529
custom commands. Each command must be a `petronia.script.command.Command`
@@ -24,10 +48,10 @@ base_config.add_command(Command('set-log-level', change_log_level))
2448
Then you can write a hotkey definition:
2549

2650
```
27-
alt + shift + f1 => set-log-level debug
51+
"alt + shift + f1": ['set-log-level', 'debug']
2852
```
2953

30-
If you're writing a command that interacts with the interworkings of Petronia,
54+
If you're writing a command that interacts with the inner workings of Petronia,
3155
then you'll have to send signals through the event bus.
3256

3357
If you really want to get funky, you can reach into the bowels of the winapi

docs/user-execs.md

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Guide to the Executables
2+
3+
Petronia supplies several executable files. This tells you what they do, and
4+
how to use them.
5+
6+
7+
## `petronia.exe` and `petronia-cli.exe`
8+
9+
**Usage:** `petronia [-h] [-l LAYOUT] [-v] [-e EXTENSIONS] configfile`
10+
11+
Window tiling manager for Windows.
12+
13+
The `petronia-cli` is identical to `petronia`, but runs as a terminal program,
14+
for easier monitoring. Additionally, you need to press <kbd>&crarrow; Enter</kbd> in
15+
the console window to end the program.
16+
17+
**Positional arguments:**
18+
19+
* `configfile` - Configuration file for setting up the display.
20+
Supported formats are: yaml, json, and py.
21+
22+
**Optional arguments:**
23+
24+
* `-h`, `--help` - Show this help message and exit
25+
* `-l` LAYOUT, `--layout` LAYOUT - Initial layout. If not given, the layout
26+
named `default` is used.
27+
* `-v`, `--version` - Show the version and quit.
28+
* `-e` EXTENSIONS, `--extensions` EXTENSIONS - Directory where the user
29+
extensions are stored. Defaults to environment variable `%PETRONIA_USER_DIR%`
30+
31+
**Python Source**
32+
33+
* `petronia.exe` == `python -m petronia.main`
34+
* `petronia-cli.exe` == `python -m petronia.cmd`
35+
36+
37+
## `detect-apps.exe`
38+
39+
**Usage:** `detect-apps`
40+
41+
A cli application.
42+
43+
Displays the information that Petronia uses to match applications against the
44+
[application configuration](user-configuration.md#application-configuration).
45+
46+
47+
## `detect-monitors.exe`
48+
49+
**Usage:** `detect-monitors [-f {yaml|json|py}]`
50+
51+
A cli application.
52+
53+
Displays information, in the given format (defaults to `yaml`), about the
54+
currently active monitors connected to your computer. If you are running in
55+
a remote desktop environment, it will display information about the remote
56+
desktop display.
57+
58+
**Optional arguments:**
59+
60+
* `-f` - Set the output format, for easy cut-and-paste into your configuration
61+
file. Acceptable formats are `yaml`, `json`, and `py`.
62+
63+
64+
## `detect-keys.exe`
65+
66+
**Usage:** `detect-keys`
67+
68+
A cli application.
69+
70+
Reports the key names, as known by Petronia for use in the
71+
[hotkey configuration](user-configuration.md#hotkey-configuration).
72+
73+
Press <kbd>&#x2732; Ctrl</kbd><kbd>C</kbd> to stop the application.

docs/user-extending.md

-2
This file was deleted.

src/petronia/arch/funcs_any_win.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,14 @@ def window__wait_gui_thread_idle(hwnd):
325325

326326

327327
def window__send_message(hwnd, key, arg1, arg2):
328+
if _attach_message_queue_to_thread(hwnd):
329+
return True
328330
return windll.user32.SendMessageW(hwnd, key, arg1, arg2)
329331

330332

331333
def window__post_message(hwnd, key, arg1, arg2):
334+
if _attach_message_queue_to_thread(hwnd):
335+
return True
332336
res = windll.user32.PostMessageW(hwnd, key, arg1, arg2)
333337
return res != 0
334338

@@ -540,8 +544,8 @@ def window__activate(hwnd):
540544
# if the results are non-zero. Some of these will not succeed due to
541545
# attributes of the window, rather than the window not existing.
542546
windll.user32.SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE)
543-
windll.user32.SetForegroundWindow(hwnd)
544547
windll.user32.AttachThreadInput(thread_process_id, current_thread_id, False)
548+
windll.user32.SetForegroundWindow(hwnd)
545549
windll.user32.SetFocus(hwnd)
546550
windll.user32.SetActiveWindow(hwnd)
547551
return True
@@ -1406,3 +1410,17 @@ def process__get_window_state(thread_pid):
14061410

14071411
def process__get_current_pid():
14081412
return windll.kernel32.GetCurrentProcessId()
1413+
1414+
1415+
def _attach_message_queue_to_thread(current_hwnd):
1416+
current_thread_id = windll.kernel32.GetCurrentThreadId()
1417+
thread_process_id = windll.user32.GetWindowThreadProcessId(current_hwnd, None)
1418+
if thread_process_id != current_thread_id:
1419+
res = windll.user32.AttachThreadInput(thread_process_id, current_thread_id, True)
1420+
# ERROR_INVALID_PARAMETER means that the two threads are already attached.
1421+
if res == 0 and GetLastError() != ERROR_INVALID_PARAMETER:
1422+
# TODO better logging
1423+
print("WARN: could not attach thread input to thread {0} ({1})".format(thread_process_id, GetLastError()))
1424+
return True
1425+
windll.user32.AttachThreadInput(thread_process_id, current_thread_id, False)
1426+
return False

src/petronia/main.py

+48-9
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
from petronia.script.read_config import read_user_configuration
1515
from petronia.tests.bus_logger import log_events
1616
from petronia.script.script_logger import create_stdout_logger
17+
from petronia.version import VERSION
1718

1819
import sys
20+
import os
21+
import argparse
1922

2023

2124
def setup(config_file, layout_name):
@@ -40,15 +43,51 @@ def setup(config_file, layout_name):
4043

4144

4245
def main_setup():
43-
if len(sys.argv) <= 1:
44-
print("Usage: arg 1: the user configuration file")
45-
sys.exit(1)
46-
47-
layout_name = None
48-
if len(sys.argv) >= 3:
49-
layout_name = sys.argv[2]
50-
51-
setup(sys.argv[1], layout_name)
46+
parser = argparse.ArgumentParser()
47+
parser.description = "Window tiling manager for Windows."
48+
parser.add_argument(
49+
"configfile",
50+
help="Configuration file for setting up the display. Supported formats are: yaml, json, and py.")
51+
parser.add_argument(
52+
"-l", "--layout",
53+
required=False,
54+
help="Initial layout. If not given, the layout named `default' is used.")
55+
parser.add_argument(
56+
"-v", "--version",
57+
help="Show the version and quit.",
58+
action="store_true"
59+
)
60+
parser.add_argument(
61+
"-e", "--extensions",
62+
help="Directory where the user extensions are stored. Defaults to environment variable %%PETRONIA_USER_DIR%%"
63+
)
64+
65+
# argparse doesn't allow for the "-v" by itself.
66+
for arg in sys.argv[1:]:
67+
if arg == '-v' or arg == '--version':
68+
parser.exit(message="Petronia v{0}\nRunning Python {1}".format(VERSION, sys.version))
69+
70+
args = parser.parse_args()
71+
72+
if args.version:
73+
parser.exit(message="Petronia v{0}\nRunning Python {1}".format(VERSION, sys.version))
74+
75+
if args.extensions:
76+
if os.path.isdir(args.extensions):
77+
sys.path.append(args.extensions)
78+
else:
79+
print("User extensions directory ({0}) does not exist. Skipping.".format(args.extensions))
80+
elif 'PETRONIA_USER_DIR' in os.environ:
81+
if os.path.isdir(os.environ['PETRONIA_USER_DIR']):
82+
sys.path.append(os.environ['PETRONIA_USER_DIR'])
83+
else:
84+
print("User extensions directory from environment ({0}) does not exist. Skipping.".format(
85+
os.environ['PETRONIA_USER_DIR']))
86+
87+
if not args.configfile or not os.path.isfile(args.configfile):
88+
parser.error("Missing configuration file. Use `-h' to see the full usage.")
89+
90+
setup(args.configfile, args.layout)
5291

5392

5493
if __name__ == '__main__':

src/petronia/shell/control/active_portal_manager.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def _move_portal_window_to_other_portal(self, event_id, target_id, event_obj):
8989
active = self._find_active_portal_cid()
9090
if active is not None:
9191
# This is the temporary movement handler until navigation can handle it better.
92-
self._log_verbose("Moving active window in {0} {1}".format(
92+
self._log_debug("Moving active window in {0} {1}".format(
9393
active, event_obj['direction']
9494
))
9595
direction = event_obj['direction']

src/petronia/shell/control/split_layout.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
107107
if direction == DIR_PARENT:
108108
if can_visit_parent:
109109
# We're done.
110-
self._log_verbose("Navigate to parent as final destination")
110+
self._log_debug("Navigate to parent as final destination")
111111
return self._fire_negotiation_target(self.parent_cid, event_obj)
112112

113113
if dest_type == PORTAL_TYPE:
@@ -120,7 +120,7 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
120120

121121
# The parent can't be visited, and the destination isn't supposed to be a portal.
122122
# So we're the target.`
123-
self._log_verbose("Navigate to self, as the parent, as the final destination")
123+
self._log_debug("Navigate to self, as the parent, as the final destination")
124124
return self._fire_negotiation_target(self.cid, event_obj)
125125

126126
if direction not in _ORIENT_DIRS[self.__layout_config.orientation]:
@@ -132,10 +132,10 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
132132
# Just end it already
133133
if dest_type == PORTAL_TYPE:
134134
# redirect to origin
135-
self._log_verbose("Redirect to origin from the bad direction")
135+
self._log_debug("Redirect to origin from the bad direction")
136136
self._fire_negotiation_target(origin_cid, event_obj)
137137
# just use self
138-
self._log_verbose("Redirect to self from the bad direction")
138+
self._log_debug("Redirect to self from the bad direction")
139139
return self._fire_negotiation_target(self.cid, event_obj)
140140

141141
index_change = _ORIENT_DIRS[self.__layout_config.orientation][direction]
@@ -145,7 +145,7 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
145145
# Determine if we can handle this direction, based on our orientation.
146146
if _MOVE_PARENT_DIR == index_change:
147147
# move to the parent
148-
self._log_verbose("Cannot move in direction for this split, telling parent to handle it")
148+
self._log_debug("Cannot move in direction for this split, telling parent to handle it")
149149
return self._fire_negotiation_discover(event_obj)
150150

151151
# We came from a child, so discover the child's index.
@@ -159,15 +159,15 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
159159
# Just end it already
160160
if dest_type == PORTAL_TYPE:
161161
# redirect to origin
162-
self._log_verbose("Split with no children cannot redirect to a child portal,"+
163-
" so pushing back to origin")
162+
self._log_debug("Split with no children cannot redirect to a child portal," +
163+
" so pushing back to origin")
164164
self._fire_negotiation_target(origin_cid, event_obj)
165165
# just use self
166-
self._log_verbose("Split with no children cannot go deeper, so using self as target.")
166+
self._log_debug("Split with no children cannot go deeper, so using self as target.")
167167
return self._fire_negotiation_target(self.cid, event_obj)
168168

169169
# just choose something
170-
self._log_verbose("Split can't find previous child cid as a child, so just choosing one.")
170+
self._log_debug("Split can't find previous child cid as a child, so just choosing one.")
171171
source_pos = 0
172172

173173
# Rotate through the children indices.
@@ -182,7 +182,7 @@ def __direction_negotiate(self, event_obj, can_visit_parent):
182182
return self._fire_negotiation_discover(event_obj, False)
183183

184184
# Go down into the children
185-
self._log_verbose("Redirecting movement to the next child index {0}".format(next_pos))
185+
self._log_debug("Redirecting movement to the next child index {0}".format(next_pos))
186186
self._fire_negotiation_descend(child_cids[next_pos], event_obj)
187187

188188
def _fire_negotiation_target(self, target_cid, event_obj):

src/petronia/shell/native/gui_window.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def message_pumper():
102102

103103
shell__pump_messages(on_exit_callback)
104104

105-
print("window quit")
105+
self._log_info("Window {0} quit".format(self.cid))
106106
self.__has_quit = True
107107

108108
pump_thread = threading.Thread(
@@ -124,6 +124,7 @@ def close(self):
124124
try:
125125
self.__removing = True
126126
if not self.__has_quit:
127+
self._log_verbose("Sending quit message to window {0} / {1}".format(self.cid, self.__hwnd))
127128
window__send_message(self.__hwnd, windows_constants.WM_QUIT, 0, 0)
128129
finally:
129130
super().close()

0 commit comments

Comments
 (0)