lfp


Logs | Files | README | README | LICENSE | GitLab


1
commit b9eae69963f6748a30a8c172102d76b9a8420164
2
Author: Connor Etherington <[email protected]>
3
Date:   Mon Oct 2 08:55:39 2023 +0200
4
5
    Auto-Commit Update - 20231002
6
---
7
 BUILD/.pkg.pkg.tar.xz                              | Bin 0 -> 78520 bytes
8
 BUILD/.pkg/Makefile                                |  92 ++++
9
 BUILD/.pkg/README.md                               | 157 ++++++
10
 BUILD/.pkg/lfp.install                             |   9 +
11
 .../lfpreviewer/X.cpython-310-x86_64-linux-gnu.so  | Bin 0 -> 105296 bytes
12
 .../lfpreviewer/X.cpython-311-x86_64-linux-gnu.so  | Bin 0 -> 34696 bytes
13
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.c           |  36 ++
14
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.h           |   7 +
15
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.c        | 296 +++++++++++
16
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.h        |   7 +
17
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/display.c     | 276 ++++++++++
18
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/display.h     |  29 ++
19
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/math.h        |   7 +
20
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/python.h      |  11 +
21
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/util.h        |  28 +
22
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/window.c      | 316 +++++++++++
23
 BUILD/.pkg/lfpreviewer/lfpreviewer/X/window.h      |   7 +
24
 BUILD/.pkg/lfpreviewer/lfpreviewer/__init__.py     |   7 +
25
 BUILD/.pkg/lfpreviewer/lfpreviewer/__main__.py     |  59 +++
26
 BUILD/.pkg/lfpreviewer/lfpreviewer/action.py       | 294 +++++++++++
27
 BUILD/.pkg/lfpreviewer/lfpreviewer/batch.py        | 267 ++++++++++
28
 BUILD/.pkg/lfpreviewer/lfpreviewer/conversion.py   |  14 +
29
 BUILD/.pkg/lfpreviewer/lfpreviewer/files.py        |  47 ++
30
 BUILD/.pkg/lfpreviewer/lfpreviewer/geometry.py     |  20 +
31
 BUILD/.pkg/lfpreviewer/lfpreviewer/layer.py        | 253 +++++++++
32
 BUILD/.pkg/lfpreviewer/lfpreviewer/lib/__init__.py |   0
33
 BUILD/.pkg/lfpreviewer/lfpreviewer/lib/lib.sh      |  69 +++
34
 .../lfpreviewer/lfpreviewer/lib/v0/__init__.py     | 323 ++++++++++++
35
 BUILD/.pkg/lfpreviewer/lfpreviewer/library.py      |   8 +
36
 BUILD/.pkg/lfpreviewer/lfpreviewer/loading.py      | 494 ++++++++++++++++++
37
 BUILD/.pkg/lfpreviewer/lfpreviewer/parser.py       | 145 ++++++
38
 BUILD/.pkg/lfpreviewer/lfpreviewer/pattern.py      |  12 +
39
 BUILD/.pkg/lfpreviewer/lfpreviewer/process.py      | 134 +++++
40
 .../.pkg/lfpreviewer/lfpreviewer/query_windows.py  |  99 ++++
41
 BUILD/.pkg/lfpreviewer/lfpreviewer/scaling.py      | 264 ++++++++++
42
 BUILD/.pkg/lfpreviewer/lfpreviewer/terminal.py     | 109 ++++
43
 BUILD/.pkg/lfpreviewer/lfpreviewer/thread.py       |  48 ++
44
 BUILD/.pkg/lfpreviewer/lfpreviewer/tmux_util.py    | 103 ++++
45
 BUILD/.pkg/lfpreviewer/lfpreviewer/ui.py           | 177 +++++++
46
 BUILD/.pkg/lfpreviewer/lfpreviewer/version.py      |   5 +
47
 BUILD/.pkg/lfpreviewer/lfpreviewer/xutil.py        | 144 +++++
48
 BUILD/.pkg/lfpreviewer/setup.py                    |  28 +
49
 BUILD/.pkg/usr/bin/lfp                             |   4 +
50
 BUILD/.pkg/usr/share/doc/lfp/README.md             | 157 ++++++
51
 BUILD/.pkg/usr/share/lfp/cleaner                   |   8 +
52
 BUILD/.pkg/usr/share/lfp/lfp                       |  14 +
53
 BUILD/.pkg/usr/share/lfp/lfp-icons                 | 117 +++++
54
 BUILD/.pkg/usr/share/lfp/lfpcd                     |   7 +
55
 BUILD/.pkg/usr/share/lfp/lfprc                     | 579 +++++++++++++++++++++
56
 BUILD/.pkg/usr/share/lfp/ocr/ocr.js                |  56 ++
57
 BUILD/.pkg/usr/share/lfp/ocr/package-lock.json     |  27 +
58
 BUILD/.pkg/usr/share/lfp/ocr/package.json          |  19 +
59
 BUILD/.pkg/usr/share/lfp/ocr/tesseract/LICENSE     |  21 +
60
 .../.pkg/usr/share/lfp/ocr/tesseract/package.json  |  37 ++
61
 BUILD/.pkg/usr/share/lfp/ocr/tesseract/readme.md   |  96 ++++
62
 .../usr/share/lfp/ocr/tesseract/src/index.d.ts     | 195 +++++++
63
 .../.pkg/usr/share/lfp/ocr/tesseract/src/index.js  |  58 +++
64
 BUILD/.pkg/usr/share/lfp/ocr/yarn.lock             |   8 +
65
 BUILD/.pkg/usr/share/lfp/scope                     |  42 ++
66
 BUILD/.pkg/usr/share/licenses/lfp/LICENSE          |  27 +
67
 BUILD/.pkg/usr/share/man/man1/lfp.1.gz             | Bin 0 -> 1916 bytes
68
 BUILD/PKGBUILD                                     |  54 ++
69
 BUILD/lfp.install                                  |   9 +
70
 BUILD/lfp/HEAD                                     |   1 +
71
 BUILD/lfp/config                                   |   8 +
72
 BUILD/lfp/description                              |   1 +
73
 BUILD/lfp/hooks/applypatch-msg.sample              |  15 +
74
 BUILD/lfp/hooks/commit-msg.sample                  |  24 +
75
 BUILD/lfp/hooks/fsmonitor-watchman.sample          | 174 +++++++
76
 BUILD/lfp/hooks/post-update.sample                 |   8 +
77
 BUILD/lfp/hooks/pre-applypatch.sample              |  14 +
78
 BUILD/lfp/hooks/pre-commit.sample                  |  49 ++
79
 BUILD/lfp/hooks/pre-merge-commit.sample            |  13 +
80
 BUILD/lfp/hooks/pre-push.sample                    |  53 ++
81
 BUILD/lfp/hooks/pre-rebase.sample                  | 169 ++++++
82
 BUILD/lfp/hooks/pre-receive.sample                 |  24 +
83
 BUILD/lfp/hooks/prepare-commit-msg.sample          |  42 ++
84
 BUILD/lfp/hooks/push-to-checkout.sample            |  78 +++
85
 BUILD/lfp/hooks/sendemail-validate.sample          |  77 +++
86
 BUILD/lfp/hooks/update.sample                      | 128 +++++
87
 BUILD/lfp/info/exclude                             |   6 +
88
 ...ck-8e07188bfd62ea160e5d9f224baaab63a0e44c15.idx | Bin 0 -> 88628 bytes
89
 ...k-8e07188bfd62ea160e5d9f224baaab63a0e44c15.pack | Bin 0 -> 56751126 bytes
90
 ...ck-8e07188bfd62ea160e5d9f224baaab63a0e44c15.rev | Bin 0 -> 12560 bytes
91
 BUILD/lfp/packed-refs                              |   2 +
92
 BUILD/pkg/lfp/usr/bin/lfp                          |   4 +
93
 BUILD/pkg/lfp/usr/bin/lfpreviewer                  |  33 ++
94
 .../lfpreviewer-0.1.0-py3.10.egg-info/PKG-INFO     |  12 +
95
 .../lfpreviewer-0.1.0-py3.10.egg-info/SOURCES.txt  |  35 ++
96
 .../dependency_links.txt                           |   1 +
97
 .../entry_points.txt                               |   2 +
98
 .../lfpreviewer-0.1.0-py3.10.egg-info/requires.txt |   3 +
99
 .../top_level.txt                                  |   1 +
100
 .../lfpreviewer/X.cpython-310-x86_64-linux-gnu.so  | Bin 0 -> 111600 bytes
101
 .../lib/python3.10/site-packages/lfpreviewer/X/X.c |  36 ++
102
 .../python3.10/site-packages/lfpreviewer/X/Xshm.c  | 296 +++++++++++
103
 .../site-packages/lfpreviewer/X/display.c          | 276 ++++++++++
104
 .../site-packages/lfpreviewer/X/window.c           | 316 +++++++++++
105
 .../site-packages/lfpreviewer/__init__.py          |   7 +
106
 .../site-packages/lfpreviewer/__main__.py          |  59 +++
107
 .../__pycache__/__init__.cpython-310.opt-1.pyc     | Bin 0 -> 508 bytes
108
 .../__pycache__/__init__.cpython-310.pyc           | Bin 0 -> 508 bytes
109
 .../__pycache__/__main__.cpython-310.opt-1.pyc     | Bin 0 -> 1973 bytes
110
 .../__pycache__/__main__.cpython-310.pyc           | Bin 0 -> 1973 bytes
111
 .../__pycache__/action.cpython-310.opt-1.pyc       | Bin 0 -> 9651 bytes
112
 .../lfpreviewer/__pycache__/action.cpython-310.pyc | Bin 0 -> 9651 bytes
113
 .../__pycache__/batch.cpython-310.opt-1.pyc        | Bin 0 -> 10837 bytes
114
 .../lfpreviewer/__pycache__/batch.cpython-310.pyc  | Bin 0 -> 10837 bytes
115
 .../__pycache__/conversion.cpython-310.opt-1.pyc   | Bin 0 -> 530 bytes
116
 .../__pycache__/conversion.cpython-310.pyc         | Bin 0 -> 530 bytes
117
 .../__pycache__/files.cpython-310.opt-1.pyc        | Bin 0 -> 1786 bytes
118
 .../lfpreviewer/__pycache__/files.cpython-310.pyc  | Bin 0 -> 1786 bytes
119
 .../__pycache__/geometry.cpython-310.opt-1.pyc     | Bin 0 -> 1121 bytes
120
 .../__pycache__/geometry.cpython-310.pyc           | Bin 0 -> 1121 bytes
121
 .../__pycache__/layer.cpython-310.opt-1.pyc        | Bin 0 -> 8537 bytes
122
 .../lfpreviewer/__pycache__/layer.cpython-310.pyc  | Bin 0 -> 8537 bytes
123
 .../__pycache__/library.cpython-310.opt-1.pyc      | Bin 0 -> 430 bytes
124
 .../__pycache__/library.cpython-310.pyc            | Bin 0 -> 430 bytes
125
 .../__pycache__/loading.cpython-310.opt-1.pyc      | Bin 0 -> 17686 bytes
126
 .../__pycache__/loading.cpython-310.pyc            | Bin 0 -> 17686 bytes
127
 .../__pycache__/parser.cpython-310.opt-1.pyc       | Bin 0 -> 5227 bytes
128
 .../lfpreviewer/__pycache__/parser.cpython-310.pyc | Bin 0 -> 5227 bytes
129
 .../__pycache__/pattern.cpython-310.opt-1.pyc      | Bin 0 -> 795 bytes
130
 .../__pycache__/pattern.cpython-310.pyc            | Bin 0 -> 795 bytes
131
 .../__pycache__/process.cpython-310.opt-1.pyc      | Bin 0 -> 3521 bytes
132
 .../__pycache__/process.cpython-310.pyc            | Bin 0 -> 3521 bytes
133
 .../query_windows.cpython-310.opt-1.pyc            | Bin 0 -> 2661 bytes
134
 .../__pycache__/query_windows.cpython-310.pyc      | Bin 0 -> 2661 bytes
135
 .../__pycache__/scaling.cpython-310.opt-1.pyc      | Bin 0 -> 10006 bytes
136
 .../__pycache__/scaling.cpython-310.pyc            | Bin 0 -> 10006 bytes
137
 .../__pycache__/terminal.cpython-310.opt-1.pyc     | Bin 0 -> 2958 bytes
138
 .../__pycache__/terminal.cpython-310.pyc           | Bin 0 -> 2958 bytes
139
 .../__pycache__/thread.cpython-310.opt-1.pyc       | Bin 0 -> 1842 bytes
140
 .../lfpreviewer/__pycache__/thread.cpython-310.pyc | Bin 0 -> 1842 bytes
141
 .../__pycache__/tmux_util.cpython-310.opt-1.pyc    | Bin 0 -> 3183 bytes
142
 .../__pycache__/tmux_util.cpython-310.pyc          | Bin 0 -> 3183 bytes
143
 .../__pycache__/ui.cpython-310.opt-1.pyc           | Bin 0 -> 6572 bytes
144
 .../lfpreviewer/__pycache__/ui.cpython-310.pyc     | Bin 0 -> 6572 bytes
145
 .../__pycache__/version.cpython-310.opt-1.pyc      | Bin 0 -> 303 bytes
146
 .../__pycache__/version.cpython-310.pyc            | Bin 0 -> 303 bytes
147
 .../__pycache__/xutil.cpython-310.opt-1.pyc        | Bin 0 -> 5092 bytes
148
 .../lfpreviewer/__pycache__/xutil.cpython-310.pyc  | Bin 0 -> 5092 bytes
149
 .../python3.10/site-packages/lfpreviewer/action.py | 294 +++++++++++
150
 .../python3.10/site-packages/lfpreviewer/batch.py  | 267 ++++++++++
151
 .../site-packages/lfpreviewer/conversion.py        |  14 +
152
 .../python3.10/site-packages/lfpreviewer/files.py  |  47 ++
153
 .../site-packages/lfpreviewer/geometry.py          |  20 +
154
 .../python3.10/site-packages/lfpreviewer/layer.py  | 253 +++++++++
155
 .../site-packages/lfpreviewer/lib/__init__.py      |   0
156
 .../lib/__pycache__/__init__.cpython-310.opt-1.pyc | Bin 0 -> 154 bytes
157
 .../lib/__pycache__/__init__.cpython-310.pyc       | Bin 0 -> 154 bytes
158
 .../site-packages/lfpreviewer/lib/lib.sh           |  69 +++
159
 .../site-packages/lfpreviewer/lib/v0/__init__.py   | 323 ++++++++++++
160
 .../v0/__pycache__/__init__.cpython-310.opt-1.pyc  | Bin 0 -> 11202 bytes
161
 .../lib/v0/__pycache__/__init__.cpython-310.pyc    | Bin 0 -> 11202 bytes
162
 .../site-packages/lfpreviewer/library.py           |   8 +
163
 .../site-packages/lfpreviewer/loading.py           | 494 ++++++++++++++++++
164
 .../python3.10/site-packages/lfpreviewer/parser.py | 145 ++++++
165
 .../site-packages/lfpreviewer/pattern.py           |  12 +
166
 .../site-packages/lfpreviewer/process.py           | 134 +++++
167
 .../site-packages/lfpreviewer/query_windows.py     |  99 ++++
168
 .../site-packages/lfpreviewer/scaling.py           | 264 ++++++++++
169
 .../site-packages/lfpreviewer/terminal.py          | 109 ++++
170
 .../python3.10/site-packages/lfpreviewer/thread.py |  48 ++
171
 .../site-packages/lfpreviewer/tmux_util.py         | 103 ++++
172
 .../lib/python3.10/site-packages/lfpreviewer/ui.py | 177 +++++++
173
 .../site-packages/lfpreviewer/version.py           |   5 +
174
 .../python3.10/site-packages/lfpreviewer/xutil.py  | 144 +++++
175
 BUILD/pkg/lfp/usr/share/doc/lfp/README.md          | 157 ++++++
176
 BUILD/pkg/lfp/usr/share/lfp/cleaner                |   8 +
177
 BUILD/pkg/lfp/usr/share/lfp/lfp                    |  14 +
178
 BUILD/pkg/lfp/usr/share/lfp/lfp-icons              | 117 +++++
179
 BUILD/pkg/lfp/usr/share/lfp/lfpcd                  |   7 +
180
 BUILD/pkg/lfp/usr/share/lfp/lfprc                  | 579 +++++++++++++++++++++
181
 BUILD/pkg/lfp/usr/share/lfp/ocr/ocr.js             |  56 ++
182
 BUILD/pkg/lfp/usr/share/lfp/ocr/package.json       |  19 +
183
 BUILD/pkg/lfp/usr/share/lfp/scope                  |  42 ++
184
 BUILD/pkg/lfp/usr/share/licenses/lfp/LICENSE       |  27 +
185
 BUILD/pkg/lfp/usr/share/man/man1/lfp.1.gz          | Bin 0 -> 1916 bytes
186
 BUILD/src/lfp                                      |   1 +
187
 BUILD/tmpPKGB                                      |  45 ++
188
 BUILD/zst.sums                                     |   8 +
189
 182 files changed, 12012 insertions(+)
190
191
diff --git a/BUILD/.pkg.pkg.tar.xz b/BUILD/.pkg.pkg.tar.xz
192
new file mode 100644
193
index 0000000..c9fdb8d
194
Binary files /dev/null and b/BUILD/.pkg.pkg.tar.xz differ
195
diff --git a/BUILD/.pkg/Makefile b/BUILD/.pkg/Makefile
196
new file mode 100644
197
index 0000000..ac1b8bb
198
--- /dev/null
199
+++ b/BUILD/.pkg/Makefile
200
@@ -0,0 +1,92 @@
201
+
202
+PKG = lfp
203
+PREVIEWER = lfpreviewer
204
+
205
+INFO = $(shell uname -a)
206
+OST = $(shell lsb_release -d | sed 's|Description:\s*||')
207
+OS = $(shell lsb_release -d | sed 's|Description:\s*||'|cut -d\  -f1)
208
+
209
+ifeq ($(OS),Ubuntu)
210
+	PACKAGE_MANAGER = apt
211
+	INSTALL = sudo ${PACKAGE_MANAGER}-get install
212
+	QUERY = sudo dpkg -S >/dev/null 2>&1
213
+	PKGS = python3-pip python3-setuptools libxres1
214
+endif
215
+
216
+ifeq ($(OS),Debian)
217
+	PACKAGE_MANAGER = apt
218
+	INSTALL = sudo ${PACKAGE_MANAGER}-get install
219
+	QUERY = sudo dpkg -S >/dev/null 2>&1
220
+	PKGS = python3-pip python3-setuptools libxres1
221
+endif
222
+
223
+ifeq ($(OS),Arch)
224
+	PACKAGE_MANAGER = pacman
225
+	INSTALL = sudo ${PACKAGE_MANAGER} -Syy
226
+	QUERY = sudo ${PACKAGE_MANAGER} -Qq >/dev/null 2>&1
227
+	PKGS = python-pip python-setuptools libxres
228
+endif
229
+
230
+ifeq ($(PACKAGE_MANAGER),)
231
+$(error >   Unsupported OS: ${OST})
232
+endif
233
+
234
+all: info
235
+
236
+lfpreviewer:
237
+	@echo -e "\n\e[1;33m[+]\e[0m Installing ${PREVIEWER}...\n"
238
+	@rm -rf ${PREVIEWER}/build ${PREVIEWER}/${PREVIEWER}.egg-info ${PREVIEWER}/dist
239
+	@${QUERY} ${PKGS} || ${INSTALL} ${PKGS}
240
+	@python3 -m pip install --upgrade pip
241
+	@python3 -m pip install --upgrade setuptools
242
+	@cd ${PREVIEWER} && python3 ./setup.py install && cd .. || true
243
+	@which lfpreviewer >/dev/null 2>&1 || echo -e "\n\e[1;31m[-]\e[0m Please ensure you have sufficient premission to install ${PREVIEWER}."
244
+
245
+lfp:
246
+	@echo -e "\n\e[1;33m[+]\e[0m Installing ${PKG}...\n"
247
+	@sudo install -Dm755 usr/bin/* -t "${PKGDIR}/usr/bin" || true
248
+	@sudo install -Dm755 usr/share/${PKG}/{${PKG},${PKG}cd,cleaner,scope} -t "${PKGDIR}/usr/share/${PKG}" || true
249
+	@sudo install -Dm644 usr/share/${PKG}/{${PKG}-icons,${PKG}rc} -t "${PKGDIR}/usr/share/${PKG}" || true
250
+	@sudo install -Dm644 usr/share/licenses/${PKG}/LICENSE "${PKGDIR}/usr/share/licenses/${PKG}/LICENSE" || true
251
+	@sudo install -Dm644 usr/share/doc/${PKG}/README.md "${PKGDIR}/usr/share/doc/${PKG}/README.md" || true
252
+	@sudo install -Dm644 usr/share/man/man1/${PKG}.1.gz "${PKGDIR}/usr/share/man/man1/${PKG}.1.gz" || true
253
+	@[ ! -d /usr/share/lfp ] && echo -e "\n\e[1;31m[-]\e[0m Please ensure you have sufficient premission to install ${PKG}." || true
254
+
255
+install: lfpreviewer lfp cd-on-exit
256
+
257
+clean:
258
+	@echo -e "\n\e[1;33m[+]\e[0m Cleaning up..."
259
+	@sudo rm -rf ${PKGDIR}/lib/python3.*/site-packages/${PREVIEWER}*
260
+	@sudo rm -rf ${PKGDIR}/usr/bin/${PREVIEWER}
261
+	@sudo rm -rf ${PKGDIR}/usr/share/${PKG}
262
+	@sudo rm -rf ${PKGDIR}/usr/bin/${PKG}
263
+	@sudo rm -rf ${PKGDIR}/usr/bin/${PKG}cd
264
+	@sudo rm -rf ${PKGDIR}/usr/bin/cleaner
265
+	@sudo rm -rf ${PKGDIR}/usr/bin/scope
266
+	@sudo rm -rf ${PKGDIR}/usr/share/licenses/${PKG}
267
+	@sudo rm -rf ${PKGDIR}/usr/share/doc/${PKG}
268
+	@sudo rm -rf ${PKGDIR}/usr/share/man/man1/${PKG}.1.gz
269
+
270
+info:
271
+	@echo -e "\n\e[1;33m[*]\e[0m ${INFO}"
272
+
273
+help:
274
+	@echo -e "\n	Usage: make [all|install|uninstall|clean|distclean|preview|help]\n"
275
+	@echo "	     all: build $(PKG)"
276
+	@echo "	     install: install $(PKG)"
277
+	@echo "	     uninstall: uninstall $(PKG)"
278
+	@echo "	     clean: clean $(PKG)"
279
+	@echo "	     distclean: clean $(PKG) and remove $(PKG) directory"
280
+	@echo "	     preview: preview $(PKG)"
281
+	@echo "	     help: show this help"
282
+
283
+cd-on-exit:
284
+	@[ -d /usr/share/lfp ] && echo -e "\n\n\033[1;32m[+]\e[0;1m For on-exit directory changing, add the following line to your bash/zshrc\e[0m :\e[36m\n\n   source '/usr/share/lfp/lfpcd'\n\e[0m" || echo -e "\n\033[1;31m[!]\e[0;1m Installation Failed!"
285
+
286
+uninstall: clean
287
+	@echo -e "\n\e[1;32m[+]\e[0m ${PKG} successfully uninstalled"
288
+
289
+remove: uninstall
290
+
291
+.PHONY: info all lfpreviewer lfp install clean help cd-on-exit uninstall remove
292
+
293
diff --git a/BUILD/.pkg/README.md b/BUILD/.pkg/README.md
294
new file mode 100644
295
index 0000000..f3d345a
296
--- /dev/null
297
+++ b/BUILD/.pkg/README.md
298
@@ -0,0 +1,157 @@
299
+
300
+# An lf wrapper, with built-in previews and on-exit directory changing.
301
+
302
+### `lfp` is a simple wrapper for the `lf` file manager, with built-in file, image, and video previews, as well as on-exit directory changing.
303
+### The program makes use of `ueberzug` to preview images, documents, and video thumbnails. Preview behavior can be altered by editing the *scope* file.
304
+
305
+**Version: 4.1.0**
306
+
307
+***
308
+
309
+## Usage:
310
+
311
+Run `lfp`. Optionally, a startup directory can be specified by a second argument. By default, `lfp` will launch in your current working directory.
312
+Icons for relative file types can be changed by editing the lfp-icons file in *'/usr/share/lfp/lfp-icons'*.
313
+
314
+**CD on exit:**
315
+
316
+For on-exit directory changing, add the following line to your bash/zshrc:
317
+
318
+    source '/usr/share/lfp/lfpcd'
319
+
320
+
321
+**LFP OCR:**
322
+
323
+LFP OCR is a simple OCR script that uses tesseract to extract text from images. It can be used as a standalone script, or as a lfp action.
324
+All detected text is copied to your clipboard.
325
+
326
+
327
+
328
+
329
+## Installation
330
+
331
+### Arch Linux
332
+
333
+lfp is available in the AUR as `lfp` or `lfp-git`
334
+
335
+
336
+### Manual Installation
337
+
338
+    tmp=$(mktemp -d);
339
+    git clone https://gitlab.com/a4to/lfp.git ${tmp} &&
340
+    cd ${tmp} && sudo make clean install && cd - &&
341
+    rm -rf ${tmp} >/dev/null 2>&1 || true
342
+
343
+
344
+**To uninstall lfp:**
345
+
346
+    sudo make uninstall
347
+
348
+
349
+
350
+## Prerequisites:
351
+
352
+* `lf`: the file manager
353
+* `zsh`: for scripting
354
+* `ueberzug`: for image previews
355
+* `ghostscript`: for PDF previews
356
+* `graphicsmagick`: for SVG and GIF previews
357
+* `ffmpeg`: for video file thumbnail previews
358
+* `dialog`: required by many functions outside the scope of image and video previews
359
+* `dunst`: to receive notifications upon task completion and so forth
360
+* `fzf`: for lfp's *fzfp* (fuzzy-finding) and *pushto* (git push) capabilities
361
+* `bat`: for text file previews
362
+* `git`: for cloning repos from within lfp
363
+* `lolcat`: used in lfp's *fzfp* function
364
+
365
+## Optional Dependencies:
366
+
367
+* `nodejs`: to make use of lfps built in node actions manager
368
+* `python3`: to make use of lfps built in python actions manager
369
+
370
+
371
+
372
+## Keybindings
373
+
374
+### Navigation:
375
+
376
+* **h**: Left
377
+* **j**: Down
378
+* **k**: Up
379
+* **l**: Right
380
+
381
+
382
+### Main Features and Functions:
383
+
384
+* **.**: Activate NodeManager
385
+* **,**: Activate PyManager
386
+* **o**: Activate LFP OCR
387
+* **v**: Toggle select all file(s)
388
+* **y**: Copy selected file(s)
389
+* **c**: Cut selected file(s)
390
+* **d**: Delete selected file(s)
391
+* **p**: Paste selected file(s)
392
+* **d**: Delete selected file(s)
393
+* **s**: Edit file you're currently hovering over
394
+* **n**: Create new directory
395
+* **m**: Play with mpv
396
+* **f**: Activate fzfp (lfp's fuzzy-finder)
397
+* **b**: Open file in default browser
398
+* **e**: Prompts you for a filename to edit/create
399
+* **t**: Prompts you for a filename for blank file to create
400
+* **x**: Execute selected file(s)
401
+* **z**: Open in `sxiv` (an image viewer)
402
+* **q**: Quit lfp
403
+* **\<Space\>**: Select file(s)
404
+* **\<Period\>**: (.) - Activates Node Actions, various npm/yarn actions / quick command execution
405
+
406
+
407
+### NodeManager:
408
+
409
+The NodeManager function provides various actions to manage a Node.js project, including:
410
+
411
+* **↑ Arrow**: Initialize a package.json file.
412
+* **↓ Arrow**: Select a javascript file to run.
413
+* **← Arrow**: Specify package(s) to install from npm.
414
+* **→ Arrow**: Start the server (yarn start).
415
+* **[h|d|m]**:: Start the server in development mode (yarn dev).
416
+* **[b|n]**: Build the project (yarn build).
417
+* **[i|I|,]**: Initialize a new npm project.
418
+* **[r]**: Remove specified packages.
419
+* **.**: Update the server (yarn).
420
+* **/**: Create a webserver on port 3000 (requires serve).
421
+* **q**: Exit.
422
+
423
+
424
+### PyManager:
425
+
426
+The PyManager function provides various actions to manage a Python project, including:
427
+
428
+* **↑ Arrow**: Create a new Conda environment at "./.🐍".
429
+* **↓ Arrow**: Select a python script to run.
430
+* **← Arrow**: Select package(s) to install from pypi.
431
+* **→ Arrow**: Run the main.py script if found.
432
+* **q**: Exit.
433
+
434
+
435
+### Additional Actions and Features:
436
+
437
+* **B**: Bulk rename
438
+* **E**: Extract archive
439
+* **D**: (sudo) Delete selected file(s)
440
+* **L**: Symlink selected file(s) to current directory
441
+* **H**: Create hardlink of selected file(s) to current directory
442
+* **R**: Create relative symlink of selected file(s) to current directory
443
+* **E**: Extract archive (bz2 rar gz tar tar.* tbz2 tgz zip Z 7z deb)
444
+* **C**: CopyPath, this feature copies the absolute path of the file youre currently hovering over to your clipboard
445
+* **\<Ctrl+c\>**: Quick clone a git repository without leaving lfp
446
+* **\<Ctrl+g\>**: Activate pushto - select a branch to push to (only works inside a git repo)
447
+* **\<Ctrl+b\>**: Open file in browser
448
+* **\<Ctrl+6\>**: Chmod 644 selected file(s)
449
+* **\<Ctrl+7\>**: Chmod 755 selected file(s)
450
+* **\<Ctrl+8\>**: Chmod 700 selected file(s)
451
+* **\<Enter\>**: Enter shell command
452
+
453
+
454
+And many more.
455
+
456
diff --git a/BUILD/.pkg/lfp.install b/BUILD/.pkg/lfp.install
457
new file mode 100644
458
index 0000000..6c915df
459
--- /dev/null
460
+++ b/BUILD/.pkg/lfp.install
461
@@ -0,0 +1,9 @@
462
+post_install() {
463
+  echo -e "\n \033[1;33m[+]\e[0;1;32m For on-exit directory changing, add the following line to your bash/zshrc\e[0m :\e[36m\n\n     source '/usr/share/lfp/lfpcd'\n\n\n\e[0m"
464
+}
465
+
466
+post_upgrade() {
467
+  which yarn >/dev/null 2>&1 && yarn install /usr/share/lfp/ocr ||
468
+    which npm >/dev/null 2>&1 && npm install /usr/share/lfp/ocr ||
469
+    true
470
+}
471
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-310-x86_64-linux-gnu.so b/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-310-x86_64-linux-gnu.so
472
new file mode 100755
473
index 0000000..982026a
474
Binary files /dev/null and b/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-310-x86_64-linux-gnu.so differ
475
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-311-x86_64-linux-gnu.so b/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-311-x86_64-linux-gnu.so
476
new file mode 100755
477
index 0000000..fcdf720
478
Binary files /dev/null and b/BUILD/.pkg/lfpreviewer/lfpreviewer/X.cpython-311-x86_64-linux-gnu.so differ
479
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.c b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.c
480
new file mode 100644
481
index 0000000..ac2c09a
482
--- /dev/null
483
+++ b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.c
484
@@ -0,0 +1,36 @@
485
+#include "python.h"
486
+#include "display.h"
487
+#include "window.h"
488
+#include "Xshm.h"
489
+
490
+
491
+static PyModuleDef module = {
492
+    PyModuleDef_HEAD_INIT,
493
+    .m_name = "lfpreviewer.X",
494
+    .m_doc = "Modul which implements the interaction with the Xshm extension.",
495
+    .m_size = -1,
496
+};
497
+
498
+
499
+PyMODINIT_FUNC
500
+PyInit_X(void) {
501
+    PyObject *module_instance;
502
+    if (PyType_Ready(&DisplayType) < 0 ||
503
+            PyType_Ready(&WindowType) < 0 ||
504
+            PyType_Ready(&ImageType) < 0) {
505
+        return NULL;
506
+    }
507
+
508
+    module_instance = PyModule_Create(&module);
509
+    if (module_instance == NULL) {
510
+        return NULL;
511
+    }
512
+
513
+    Py_INCREF(&DisplayType);
514
+    Py_INCREF(&WindowType);
515
+    Py_INCREF(&ImageType);
516
+    PyModule_AddObject(module_instance, "Display", (PyObject*)&DisplayType);
517
+    PyModule_AddObject(module_instance, "OverlayWindow", (PyObject*)&WindowType);
518
+    PyModule_AddObject(module_instance, "Image", (PyObject*)&ImageType);
519
+    return module_instance;
520
+}
521
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.h b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.h
522
new file mode 100644
523
index 0000000..2523c59
524
--- /dev/null
525
+++ b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/X.h
526
@@ -0,0 +1,7 @@
527
+#ifndef __X_H__
528
+#define __X_H__
529
+#include "python.h"
530
+
531
+
532
+PyModuleDef module;
533
+#endif
534
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.c b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.c
535
new file mode 100644
536
index 0000000..b68919a
537
--- /dev/null
538
+++ b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.c
539
@@ -0,0 +1,296 @@
540
+#include "python.h"
541
+
542
+#include <stdbool.h>
543
+#include <sys/shm.h>
544
+#include <X11/Xlib.h>
545
+#include <X11/Xutil.h>
546
+#include <X11/extensions/XShm.h>
547
+
548
+#include "math.h"
549
+#include "util.h"
550
+#include "display.h"
551
+
552
+#define INVALID_SHM_ID -1
553
+#define INVALID_SHM_ADDRESS (char*)-1
554
+#define BYTES_PER_PIXEL 4
555
+
556
+
557
+typedef struct {
558
+    PyObject_HEAD
559
+    int width;
560
+    int height;
561
+    int buffer_size;
562
+    DisplayObject *display_pyobject;
563
+    XShmSegmentInfo segmentInfo;
564
+    XImage *image;
565
+} ImageObject;
566
+
567
+
568
+static inline Display *
569
+get_display(ImageObject *self) {
570
+    return self->display_pyobject->event_display;
571
+}
572
+
573
+static bool
574
+Image_init_shared_memory(ImageObject *self) {
575
+    self->segmentInfo.shmid = shmget(
576
+        IPC_PRIVATE,
577
+        self->buffer_size,
578
+        IPC_CREAT | 0600);
579
+    return self->segmentInfo.shmid != INVALID_SHM_ID;
580
+}
581
+
582
+static bool
583
+Image_map_shared_memory(ImageObject *self) {
584
+    // Map the shared memory segment into the address space of this process
585
+    self->segmentInfo.shmaddr = (char*)shmat(self->segmentInfo.shmid, 0, 0);
586
+
587
+    if (self->segmentInfo.shmaddr != INVALID_SHM_ADDRESS) {
588
+        self->segmentInfo.readOnly = true;
589
+        // Mark the shared memory segment for removal
590
+        // It will be removed even if this program crashes
591
+        shmctl(self->segmentInfo.shmid, IPC_RMID, 0);
592
+        return true;
593
+    }
594
+
595
+    return false;
596
+}
597
+
598
+static bool
599
+Image_create_shared_image(ImageObject *self) {
600
+    Display *display = get_display(self);
601
+    int screen = XDefaultScreen(display);
602
+    // Allocate the memory needed for the XImage structure
603
+    self->image = XShmCreateImage(
604
+        display, XDefaultVisual(display, screen),
605
+        DefaultDepth(display, screen), ZPixmap, 0,
606
+        &self->segmentInfo, 0, 0);
607
+
608
+    if (self->image) {
609
+        self->image->data = (char*)self->segmentInfo.shmaddr;
610
+        self->image->width = self->width;
611
+        self->image->height = self->height;
612
+
613
+        // Ask the X server to attach the shared memory segment and sync
614
+        XShmAttach(display, &self->segmentInfo);
615
+        XFlush(display);
616
+        return true;
617
+    }
618
+    return false;
619
+}
620
+
621
+static void
622
+Image_destroy_shared_image(ImageObject *self) {
623
+    if (self->image) {
624
+        XShmDetach(get_display(self), &self->segmentInfo);
625
+        XDestroyImage(self->image);
626
+        self->image = NULL;
627
+    }
628
+}
629
+
630
+static void
631
+Image_free_shared_memory(ImageObject *self) {
632
+    if(self->segmentInfo.shmaddr != INVALID_SHM_ADDRESS) {
633
+        shmdt(self->segmentInfo.shmaddr);
634
+        self->segmentInfo.shmaddr = INVALID_SHM_ADDRESS;
635
+    }
636
+}
637
+
638
+static void
639
+Image_finalise(ImageObject *self) {
640
+    Image_destroy_shared_image(self);
641
+    Image_free_shared_memory(self);
642
+    Py_CLEAR(self->display_pyobject);
643
+}
644
+
645
+static int
646
+Image_init(ImageObject *self, PyObject *args, PyObject *kwds) {
647
+    static char *kwlist[] = {"display", "width", "height", NULL};
648
+    PyObject *display_pyobject;
649
+
650
+    if (!PyArg_ParseTupleAndKeywords(
651
+            args, kwds, "O!ii", kwlist,
652
+            &DisplayType, &display_pyobject,
653
+            &self->width, &self->height)) {
654
+        Py_INIT_RETURN_ERROR;
655
+    }
656
+
657
+    if (self->display_pyobject) {
658
+        Image_finalise(self);
659
+    }
660
+
661
+    Py_INCREF(display_pyobject);
662
+    self->display_pyobject = (DisplayObject*)display_pyobject;
663
+    self->buffer_size = self->width * self->height * BYTES_PER_PIXEL;
664
+
665
+    if (!Image_init_shared_memory(self)) {
666
+        raiseInit(OSError, "could not init shared memory");
667
+    }
668
+
669
+    if (!Image_map_shared_memory(self)) {
670
+        raiseInit(OSError, "could not map shared memory");
671
+    }
672
+
673
+    if (!Image_create_shared_image(self)) {
674
+        Image_free_shared_memory(self);
675
+        raiseInit(OSError, "could not allocate the XImage structure");
676
+    }
677
+    
678
+    Py_INIT_RETURN_SUCCESS;
679
+}
680
+
681
+static void
682
+Image_dealloc(ImageObject *self) {
683
+    Image_finalise(self);
684
+    Py_TYPE(self)->tp_free((PyObject*)self);
685
+}
686
+
687
+static PyObject *
688
+Image_copy_to(ImageObject *self, PyObject *args, PyObject *kwds) {
689
+    // draws the image on the surface at x, y
690
+    static char *kwlist[] = {"drawable", "x", "y", "width", "height", NULL};
691
+    Drawable surface;
692
+    GC gc;
693
+    int x, y;
694
+    unsigned int width, height;
695
+    Display *display = get_display(self);
696
+
697
+    if (!PyArg_ParseTupleAndKeywords(
698
+            args, kwds, "kiiII", kwlist,
699
+            &surface, &x, &y, &width, &height)) {
700
+        Py_RETURN_ERROR;
701
+    }
702
+
703
+    gc = XCreateGC(display, surface, 0, NULL);
704
+    XShmPutImage(display, surface, gc,
705
+                 self->image, 0, 0,
706
+                 x, y, width, height, false);
707
+    XFreeGC(display, gc);
708
+
709
+    Py_RETURN_NONE;
710
+}
711
+
712
+static PyObject *
713
+Image_draw(ImageObject *self, PyObject *args, PyObject *kwds) {
714
+    // puts the pixels on the image at x, y
715
+    static char *kwlist[] = {"x", "y", "width", "height", "pixels", NULL};
716
+    int offset_x, offset_y;
717
+    int width, height;
718
+    int pixels_per_row;
719
+    int source_pixels_per_row;
720
+    int destination_pixels_per_row;
721
+    int destination_offset_x_bytes;
722
+    char *pixels;
723
+    Py_ssize_t pixels_size;
724
+
725
+    if (!PyArg_ParseTupleAndKeywords(
726
+            args, kwds, "iiiis#", kwlist,
727
+            &offset_x, &offset_y, &width, &height,
728
+            &pixels, &pixels_size)) {
729
+        Py_RETURN_ERROR;
730
+    }
731
+
732
+    Py_BEGIN_ALLOW_THREADS
733
+    destination_offset_x_bytes = max(0, offset_x) * BYTES_PER_PIXEL;
734
+    source_pixels_per_row = width * BYTES_PER_PIXEL;
735
+    destination_pixels_per_row = self->width * BYTES_PER_PIXEL;
736
+    pixels_per_row = min(width + min(offset_x, 0), self->width - max(offset_x, 0)) * BYTES_PER_PIXEL;
737
+
738
+    if (offset_x + width > 0 && offset_x < self->width) {
739
+        // < 0 -> start y = 0, min(surface.height, height - abs(offset))
740
+        // > 0 -> start y = offset, height = min(surface.height, height + offset)
741
+        for (int y = max(0, offset_y); y < min(self->height, height + offset_y); y++) {
742
+            // < 0 -> first row = abs(offset) => n row = y + abs(offset)
743
+            // > 0 -> first row = - offset    => n row = y - offset
744
+            // => n row = y - offset
745
+            int pixels_y = y - offset_y;
746
+            void *destination =
747
+                self->image->data + y * destination_pixels_per_row
748
+                + destination_offset_x_bytes;
749
+            void *source = pixels + pixels_y * source_pixels_per_row;
750
+
751
+            if (! ((uintptr_t)self->image->data <= (uintptr_t)destination)) {
752
+                raise(AssertionError,
753
+                      "The destination start address calculation went wrong.\n"
754
+                      "It points to an address which is before the start address of the buffer.\n"
755
+                      "%p not smaller than %p",
756
+                      self->image->data, destination);
757
+            }
758
+            if (! ((uintptr_t)destination + pixels_per_row
759
+                   <= (uintptr_t)self->image->data + self->buffer_size)) {
760
+                raise(AssertionError,
761
+                      "The destination end address calculation went wrong.\n"
762
+                      "It points to an address which is after the end address of the buffer.\n"
763
+                      "%p not smaller than %p",
764
+                      destination + pixels_per_row,
765
+                      self->image->data + self->buffer_size);
766
+            }
767
+            if (! ((uintptr_t)pixels <= (uintptr_t)source)) {
768
+                raise(AssertionError,
769
+                      "The source start address calculation went wrong.\n"
770
+                      "It points to an address which is before the start address of the buffer.\n"
771
+                      "%p not smaller than %p",
772
+                      pixels, source);
773
+            }
774
+            if (! ((uintptr_t)source + pixels_per_row
775
+                   <= (uintptr_t)pixels + pixels_size)) {
776
+                raise(AssertionError,
777
+                      "The source end address calculation went wrong.\n"
778
+                      "It points to an address which is after the end address of the buffer."
779
+                      "%p not smaller than %p",
780
+                      source + pixels_per_row,
781
+                      pixels + pixels_size);
782
+            }
783
+
784
+            memcpy(destination, source, pixels_per_row);
785
+        }
786
+    }
787
+    Py_END_ALLOW_THREADS
788
+
789
+    Py_RETURN_NONE;
790
+}
791
+
792
+static PyMethodDef Image_methods[] = {
793
+    {"copy_to", (PyCFunction)Image_copy_to,
794
+     METH_VARARGS | METH_KEYWORDS,
795
+     "Draws the image on the surface at the passed coordinate.\n"
796
+     "\n"
797
+     "Args:\n"
798
+     "    drawable (int): the surface to draw on\n"
799
+     "    x (int): the x position where this image should be placed\n"
800
+     "    y (int): the y position where this image should be placed\n"
801
+     "    width (int): the width of the area\n"
802
+     "                 which should be copied to the drawable\n"
803
+     "    height (int): the height of the area\n"
804
+     "                  which should be copied to the drawable"},
805
+    {"draw", (PyCFunction)Image_draw,
806
+     METH_VARARGS | METH_KEYWORDS,
807
+     "Places the pixels on the image at the passed coordinate.\n"
808
+     "\n"
809
+     "Args:\n"
810
+     "    x (int): the x position where the pixels should be placed\n"
811
+     "    y (int): the y position where the pixels should be placed\n"
812
+     "    width (int): amount of pixels per row in the passed data\n"
813
+     "    height (int): amount of pixels per column in the passed data\n"
814
+     "    pixels (bytes): the pixels to place on the image"},
815
+    {NULL}  /* Sentinel */
816
+};
817
+
818
+PyTypeObject ImageType = {
819
+    PyVarObject_HEAD_INIT(NULL, 0)
820
+    .tp_name = "lfpreviewer.X.Image",
821
+    .tp_doc = 
822
+        "An shared memory X11 Image\n"
823
+        "\n"
824
+        "Args:\n"
825
+        "    display (lfpreviewer.X.Display): the X11 display\n"
826
+        "    width (int): the width of this image\n"
827
+        "    height (int): the height of this image",
828
+    .tp_basicsize = sizeof(ImageObject),
829
+    .tp_itemsize = 0,
830
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
831
+    .tp_new = PyType_GenericNew,
832
+    .tp_init = (initproc)Image_init,
833
+    .tp_dealloc = (destructor) Image_dealloc,
834
+    .tp_methods = Image_methods,
835
+};
836
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.h b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.h
837
new file mode 100644
838
index 0000000..ab61b4d
839
--- /dev/null
840
+++ b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/Xshm.h
841
@@ -0,0 +1,7 @@
842
+#ifndef __XSHM_H__
843
+#define __XSHM_H__
844
+#include "python.h"
845
+
846
+
847
+extern PyTypeObject ImageType;
848
+#endif
849
diff --git a/BUILD/.pkg/lfpreviewer/lfpreviewer/X/display.c b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/display.c
850
new file mode 100644
851
index 0000000..c9e4eee
852
--- /dev/null
853
+++ b/BUILD/.pkg/lfpreviewer/lfpreviewer/X/display.c
854
@@ -0,0 +1,276 @@
855
+#include "python.h"
856
+
857
+#include <X11/Xlib.h>
858
+#include <X11/extensions/XRes.h>
859
+#include <X11/extensions/XShm.h>
860
+
861
+#include "util.h"
862
+#include "display.h"
863
+
864
+
865
+#define INVALID_PID (pid_t)-1
866
+
867
+
868
+#define REOPEN_DISPLAY(display) \
869
+    if (display != NULL) { \
870
+        XCloseDisplay(display); \
871
+    } \
872
+    display = XOpenDisplay(NULL);
873
+
874
+#define CLOSE_DISPLAY(display) \
875
+    if (display != NULL) { \
876
+        XCloseDisplay(display); \
877
+        display = NULL; \
878
+    }
879
+
880
+
881
+static int
882
+Display_init(DisplayObject *self, PyObject *args, PyObject *kwds) {
883
+    // Two connections are opened as
884
+    // a death lock can occur
885
+    // if you listen for events
886
+    // (this will happen in parallel in asyncio worker threads)
887
+    // and request information (e.g. XGetGeometry)
888
+    // simultaneously.
889
+    REOPEN_DISPLAY(self->event_display);
890
+    REOPEN_DISPLAY(self->info_display);
891
+
892
+    if (self->event_display == NULL ||
893
+            self->info_display == NULL) {
894
+        raiseInit(OSError, "could not open a connection to the X server");
895
+    }
896
+
897
+    int _;
898
+    if (!XResQueryExtension(self->info_display, &_, &_)) {
899
+        raiseInit(OSError, "the extension XRes is required");
900
+    }
901
+
902
+    if (!XShmQueryExtension(self->event_display)) {
903
+        raiseInit(OSError, "the extension Xext is required");
904
+    }
905
+
906
+    int screen = XDefaultScreen(self->info_display);
907
+    self->screen_width = XDisplayWidth(self->info_display, screen);
908
+    self->screen_height = XDisplayHeight(self->info_display, screen);
909
+    self->bitmap_pad = XBitmapPad(self->info_display);
910
+    self->bitmap_unit = XBitmapUnit(self->info_display);
911
+
912
+    self->wm_class = XInternAtom(self->info_display, "WM_CLASS", False);
913
+    self->wm_name = XInternAtom(self->info_display, "WM_NAME", False);
914
+    self->wm_locale_name = XInternAtom(self->info_display, "WM_LOCALE_NAME", False);
915
+    self->wm_normal_hints = XInternAtom(self->info_display, "WM_NORMAL_HINTS", False);
916
+
917
+    Py_INIT_RETURN_SUCCESS;
918
+}
919
+
920
+static void
921
+Display_dealloc(DisplayObject *self) {
922
+    CLOSE_DISPLAY(self->event_display);
923
+    CLOSE_DISPLAY(self->info_display);
924
+    Py_TYPE(self)->tp_free((PyObject*)self);
925
+}
926
+
927
+
928
+static int
929
+has_property(DisplayObject *self, Window window, Atom property) {
930
+    Atom actual_type_return;
931
+    int actual_format_return;
932
+    unsigned long bytes_after_return;
933
+    unsigned char* prop_to_return = NULL;
934
+    unsigned long nitems_return;
935
+
936
+    int status = XGetWindowProperty(
937
+        self->info_display, window, property, 0,
938
+        0L, False,
939
+        AnyPropertyType,
940
+        &actual_type_return,
941
+        &actual_format_return,
942
+        &nitems_return, &bytes_after_return, &prop_to_return);
943
+    if (status == Success && prop_to_return) {
944
+        XFree(prop_to_return);
945
+    }
946
+    return status == Success && !(actual_type_return == None && actual_format_return == 0);
947
+}
948
+
949
+
950
+static PyObject *
951
+Display_get_child_window_ids(DisplayObject *self, PyObject *args, PyObject *kwds) {
952
+    static char *kwlist[] = {"parent_id", NULL};
953
+    Window parent = XDefaultRootWindow(self->info_display);
954
+    Window _, *children;
955
+    unsigned int children_count;
956
+
957
+    if (!PyArg_ParseTupleAndKeywords(
958
+            args, kwds, "|k", kwlist,
959
+            &parent)) {
960
+        Py_RETURN_ERROR;
961
+    }
962
+
963
+    if (!XQueryTree(
964
+            self->info_display, parent,
965
+            &_, &_, &children, &children_count)) {
966
+        raise(OSError, "failed to query child windows of %lu", parent);
967
+    }
968
+
969
+    PyObject *child_ids = PyList_New(0);
970
+    if (children) {
971
+        for (unsigned int i = 0; i < children_count; i++) {
972
+            // assume that windows without essential properties
973
+            // like the window title aren't shown to the user
974
+            int is_helper_window = (
975
+                !has_property(self, children[i], self->wm_class) &&
976
+                !has_property(self, children[i], self->wm_name) &&
977
+                !has_property(self, children[i], self->wm_locale_name) &&
978
+                !has_property(self, children[i], self->wm_normal_hints));
979
+            if (is_helper_window) {
980
+                continue;
981
+            }
982
+            PyObject *py_window_id = Py_BuildValue("k", children[i]);
983
+            PyList_Append(child_ids, py_window_id);
984
+            Py_XDECREF(py_window_id);
985
+        }
986
+        XFree(children);
987
+    }
988
+
989
+    return child_ids;
990
+}
991
+
992
+static PyObject *
993
+Display_get_window_pid(DisplayObject *self, PyObject *args, PyObject *kwds) {
994
+    static char *kwlist[] = {"window_id", NULL};
995
+    Window window;
996
+    long num_ids;
997
+    int num_specs = 1;
998
+    XResClientIdValue *client_ids;
999
+    XResClientIdSpec client_specs[1];