# HG changeset patch # User drewp@bigasterisk.com # Date 1678301831 28800 # Node ID 3d7fc94a404ab189d4b0e8fc32a035780eb3f827 # Parent 0ecb388a0b90288a04abc6313c8749519c3c9391 a few more inputs; daemonize diff -r 0ecb388a0b90 -r 3d7fc94a404a current_window_title_linux.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/current_window_title_linux.py Wed Mar 08 10:57:11 2023 -0800 @@ -0,0 +1,13 @@ +import Xlib +import Xlib.display + +_disp = None + + +def get_current_window_title(): + global _disp + if _disp is None: + _disp = Xlib.display.Display() + window = _disp.get_input_focus().focus + # get_wm_name returns '': https://github.com/python-xlib/python-xlib/issues/212 + return window.get_property(_disp.get_atom("WM_NAME"), _disp.get_atom("UTF8_STRING"), 0, 1024).value.decode('utf8') \ No newline at end of file diff -r 0ecb388a0b90 -r 3d7fc94a404a current_window_title_osx.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/current_window_title_osx.py Wed Mar 08 10:57:11 2023 -0800 @@ -0,0 +1,15 @@ +from osascript import osascript +#https://stackoverflow.com/questions/5292204/macosx-get-foremost-window-title + + +def get_current_window_title(): + _, title, _ = osascript(''' + tell application "System Events" + set frontApp to name of first application process whose frontmost is true + end tell + + tell application frontApp + set window_name to name of front window + end tell + ''') + return title \ No newline at end of file diff -r 0ecb388a0b90 -r 3d7fc94a404a pdm.lock --- a/pdm.lock Sat Mar 04 12:10:36 2023 -0800 +++ b/pdm.lock Wed Mar 08 10:57:11 2023 -0800 @@ -1,3 +1,6 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + [[package]] name = "anyio" version = "3.6.2" @@ -9,6 +12,24 @@ ] [[package]] +name = "appnope" +version = "0.1.3" +summary = "Disable App Nap on macOS >= 10.9" + +[[package]] +name = "asttokens" +version = "2.2.1" +summary = "Annotate AST trees with source code positions" +dependencies = [ + "six", +] + +[[package]] +name = "backcall" +version = "0.2.0" +summary = "Specifications for callback functions passed in to an API" + +[[package]] name = "background-loop" version = "1.7.0" requires_python = ">=3.9" @@ -41,6 +62,27 @@ summary = "Cross-platform colored terminal text." [[package]] +name = "daemonocle" +version = "1.2.3" +requires_python = ">=3.6" +summary = "A Python library for creating super fancy Unix daemons" +dependencies = [ + "click", + "psutil", +] + +[[package]] +name = "decorator" +version = "5.1.1" +requires_python = ">=3.5" +summary = "Decorators for Humans" + +[[package]] +name = "executing" +version = "1.2.0" +summary = "Get the currently executing AST node of a frame, and other information" + +[[package]] name = "h11" version = "0.14.0" requires_python = ">=3.7" @@ -53,6 +95,44 @@ summary = "Internationalized Domain Names in Applications (IDNA)" [[package]] +name = "ipython" +version = "8.11.0" +requires_python = ">=3.8" +summary = "IPython: Productive Interactive Computing" +dependencies = [ + "appnope; sys_platform == \"darwin\"", + "backcall", + "colorama; sys_platform == \"win32\"", + "decorator", + "jedi>=0.16", + "matplotlib-inline", + "pexpect>4.3; sys_platform != \"win32\"", + "pickleshare", + "prompt-toolkit!=3.0.37,<3.1.0,>=3.0.30", + "pygments>=2.4.0", + "stack-data", + "traitlets>=5", +] + +[[package]] +name = "jedi" +version = "0.18.2" +requires_python = ">=3.6" +summary = "An autocompletion tool for Python that can be used for text editors." +dependencies = [ + "parso<0.9.0,>=0.8.0", +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +requires_python = ">=3.5" +summary = "Inline Matplotlib backend for Jupyter" +dependencies = [ + "traitlets", +] + +[[package]] name = "osascript" version = "2020.12.3" summary = "" @@ -61,18 +141,51 @@ ] [[package]] +name = "parso" +version = "0.8.3" +requires_python = ">=3.6" +summary = "A Python Parser" + +[[package]] +name = "pexpect" +version = "4.8.0" +summary = "Pexpect allows easy control of interactive console applications." +dependencies = [ + "ptyprocess>=0.5", +] + +[[package]] +name = "pickleshare" +version = "0.7.5" +summary = "Tiny 'shelve'-like database with concurrency support" + +[[package]] name = "prometheus-client" version = "0.16.0" requires_python = ">=3.6" summary = "Python client for the Prometheus monitoring system." [[package]] +name = "prompt-toolkit" +version = "3.0.38" +requires_python = ">=3.7.0" +summary = "Library for building powerful interactive command lines in Python" +dependencies = [ + "wcwidth", +] + +[[package]] name = "psutil" version = "5.9.4" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "Cross-platform lib for process and system monitoring in Python." [[package]] +name = "ptyprocess" +version = "0.7.0" +summary = "Run a subprocess in a pseudo terminal" + +[[package]] name = "pulsectl" version = "22.3.2" summary = "Python high-level interface and ctypes-based bindings for PulseAudio (libpulse)" @@ -87,12 +200,23 @@ ] [[package]] +name = "pure-eval" +version = "0.2.2" +summary = "Safely evaluate AST nodes without side effects" + +[[package]] name = "pycparser" version = "2.21" requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "C parser in Python" [[package]] +name = "pygments" +version = "2.14.0" +requires_python = ">=3.6" +summary = "Pygments is a syntax highlighting package written in Python." + +[[package]] name = "python-xlib" version = "0.33" summary = "Python X Library" @@ -101,11 +225,6 @@ ] [[package]] -name = "quartz" -version = "0.0.1.dev0" -summary = "" - -[[package]] name = "runcmd" version = "2020.12.3" summary = "" @@ -126,6 +245,16 @@ summary = "Sniff out which async library your code is running under" [[package]] +name = "stack-data" +version = "0.6.2" +summary = "Extract data from python stack frames and tracebacks for informative displays" +dependencies = [ + "asttokens>=2.1.0", + "executing>=1.2.0", + "pure-eval", +] + +[[package]] name = "starlette" version = "0.23.1" requires_python = ">=3.7" @@ -144,6 +273,12 @@ ] [[package]] +name = "traitlets" +version = "5.9.0" +requires_python = ">=3.7" +summary = "Traitlets Python configuration system" + +[[package]] name = "uvicorn" version = "0.20.0" requires_python = ">=3.7" @@ -154,6 +289,11 @@ ] [[package]] +name = "wcwidth" +version = "0.2.6" +summary = "Measures the displayed width of unicode strings in a terminal" + +[[package]] name = "xprintidle" version = "0.2" summary = "A cffi wrapper around xprintidle." @@ -162,14 +302,26 @@ ] [metadata] -lock_version = "4.0" -content_hash = "sha256:86695308ac1a377a8f1950ba4dce042957aee65d99007a48771f71b52557f734" +lock_version = "4.1" +content_hash = "sha256:5f775ca5777bad86a2fa325b5d6912065c225f8c4afcbaa5b15a741a89d44ba7" [metadata.files] "anyio 3.6.2" = [ {url = "https://files.pythonhosted.org/packages/77/2b/b4c0b7a3f3d61adb1a1e0b78f90a94e2b6162a043880704b7437ef297cad/anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, {url = "https://files.pythonhosted.org/packages/8b/94/6928d4345f2bc1beecbff03325cad43d320717f51ab74ab5a571324f4f5a/anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, ] +"appnope 0.1.3" = [ + {url = "https://files.pythonhosted.org/packages/41/4a/381783f26df413dde4c70c734163d88ca0550a1361cb74a1c68f47550619/appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {url = "https://files.pythonhosted.org/packages/6a/cd/355842c0db33192ac0fc822e2dcae835669ef317fe56c795fb55fcddb26f/appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] +"asttokens 2.2.1" = [ + {url = "https://files.pythonhosted.org/packages/c8/e3/b0b4f32162621126fbdaba636c152c6b6baec486c99f48686e66343d638f/asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, + {url = "https://files.pythonhosted.org/packages/f3/e1/64679d9d0759db5b182222c81ff322c2fe2c31e156a59afd6e9208c960e5/asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, +] +"backcall 0.2.0" = [ + {url = "https://files.pythonhosted.org/packages/4c/1c/ff6546b6c12603d8dd1070aa3c3d273ad4c07f5771689a7b69a550e8c951/backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {url = "https://files.pythonhosted.org/packages/a2/40/764a663805d84deee23043e1426a9175567db89c8b3287b5c2ad9f71aa93/backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] "background-loop 1.7.0" = [ {url = "https://projects.bigasterisk.com/background-loop/background-loop-1.7.0.tar.gz", hash = "sha256:0f21badaeb68d4c9ef8742b265f5498cc961f79b02dd116f04b8fb2b841bf975"}, ] @@ -247,6 +399,17 @@ {url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +"daemonocle 1.2.3" = [ + {url = "https://files.pythonhosted.org/packages/ad/df/37c5b547fceba26e623cc41cc8416d9eff1cb8bcb57ed063ef5f004b692d/daemonocle-1.2.3.tar.gz", hash = "sha256:741ce6abc37c85ca396379a0d45710ac27ab5f17eeaadbc64a0f5cc4c4f666ea"}, +] +"decorator 5.1.1" = [ + {url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, + {url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, +] +"executing 1.2.0" = [ + {url = "https://files.pythonhosted.org/packages/28/3c/bc3819dd8b1a1588c9215a87271b6178cc5498acaa83885211f5d4d9e693/executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, + {url = "https://files.pythonhosted.org/packages/8f/ac/89ff37d8594b0eef176b7cec742ac868fef853b8e18df0309e3def9f480b/executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, +] "h11 0.14.0" = [ {url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -255,13 +418,41 @@ {url = "https://files.pythonhosted.org/packages/8b/e1/43beb3d38dba6cb420cefa297822eac205a277ab43e5ba5d5c46faf96438/idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, {url = "https://files.pythonhosted.org/packages/fc/34/3030de6f1370931b9dbb4dad48f6ab1015ab1d32447850b9fc94e60097be/idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, ] +"ipython 8.11.0" = [ + {url = "https://files.pythonhosted.org/packages/a7/e1/7c277c1dd0b0520bf07070c3921ff1986a2eb3db704ad91f0ac46fb42d8f/ipython-8.11.0.tar.gz", hash = "sha256:735cede4099dbc903ee540307b9171fbfef4aa75cfcacc5a273b2cda2f02be04"}, + {url = "https://files.pythonhosted.org/packages/ac/91/23e08c442657cf493598b0222008437c9e0aef0709a8fd65a5d5d68ffa21/ipython-8.11.0-py3-none-any.whl", hash = "sha256:5b54478e459155a326bf5f42ee4f29df76258c0279c36f21d71ddb560f88b156"}, +] +"jedi 0.18.2" = [ + {url = "https://files.pythonhosted.org/packages/15/02/afd43c5066de05f6b3188f3aa74136a3289e6c30e7a45f351546cab0928c/jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, + {url = "https://files.pythonhosted.org/packages/6d/60/4acda63286ef6023515eb914543ba36496b8929cb7af49ecce63afde09c6/jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, +] +"matplotlib-inline 0.1.6" = [ + {url = "https://files.pythonhosted.org/packages/d9/50/3af8c0362f26108e54d58c7f38784a3bdae6b9a450bab48ee8482d737f44/matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {url = "https://files.pythonhosted.org/packages/f2/51/c34d7a1d528efaae3d8ddb18ef45a41f284eacf9e514523b191b7d0872cc/matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] "osascript 2020.12.3" = [ {url = "https://files.pythonhosted.org/packages/15/21/36f43fde613eefa23e8f569a4020964c5417cd8fad7b069383717d2a57b0/osascript-2020.12.3.tar.gz", hash = "sha256:36d3f30fb6c43c868c70d1fb06e4c970844a5cbe0a481b9fbd5059398916e069"}, ] +"parso 0.8.3" = [ + {url = "https://files.pythonhosted.org/packages/05/63/8011bd08a4111858f79d2b09aad86638490d62fbf881c44e434a6dfca87b/parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {url = "https://files.pythonhosted.org/packages/a2/0e/41f0cca4b85a6ea74d66d2226a7cda8e41206a624f5b330b958ef48e2e52/parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] +"pexpect 4.8.0" = [ + {url = "https://files.pythonhosted.org/packages/39/7b/88dbb785881c28a102619d46423cb853b46dbccc70d3ac362d99773a78ce/pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {url = "https://files.pythonhosted.org/packages/e5/9b/ff402e0e930e70467a7178abb7c128709a30dfb22d8777c043e501bc1b10/pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +"pickleshare 0.7.5" = [ + {url = "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] "prometheus-client 0.16.0" = [ {url = "https://files.pythonhosted.org/packages/5b/8e/6a546e439b4366ab9eab0a736876eb1e1916dd93b4a1fa560ef711d24f8c/prometheus_client-0.16.0-py3-none-any.whl", hash = "sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab"}, {url = "https://files.pythonhosted.org/packages/d0/55/9e34c73e1e490b105b4cd13d08497b1f7cb086a260e4161b7b7c2928b196/prometheus_client-0.16.0.tar.gz", hash = "sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48"}, ] +"prompt-toolkit 3.0.38" = [ + {url = "https://files.pythonhosted.org/packages/4b/bb/75cdcd356f57d17b295aba121494c2333d26bfff1a837e6199b8b83c415a/prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, + {url = "https://files.pythonhosted.org/packages/87/3f/1f5a0ff475ae6481f4b0d45d4d911824d3218b94ee2a97a8cb84e5569836/prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, +] "psutil 5.9.4" = [ {url = "https://files.pythonhosted.org/packages/1d/80/e1502ba4ff65390bd17b4612010762075f64f5a0e7c28e889c4820bd95a9/psutil-5.9.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ff89f9b835100a825b14c2808a106b6fdcc4b15483141482a12c725e7f78549"}, {url = "https://files.pythonhosted.org/packages/25/6e/ba97809175c90cbdcd33b470e466ebf0854d15d1506e605cc0ddd284d5b6/psutil-5.9.4-cp36-abi3-win_amd64.whl", hash = "sha256:fd8522436a6ada7b4aad6638662966de0d61d241cb821239b2ae7013d41a43d4"}, @@ -278,6 +469,10 @@ {url = "https://files.pythonhosted.org/packages/a5/73/35cea01aad1baf901c915dc95ea33a2f271c8ff8cf2f1c73b7f591f1bdf1/psutil-5.9.4-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:54d5b184728298f2ca8567bf83c422b706200bcbbfafdc06718264f9393cfeb7"}, {url = "https://files.pythonhosted.org/packages/ec/be/b8df2071eda861e65a1b2cec35770bb1f4523737e84a10aa41c53e39e9bc/psutil-5.9.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6b92c532979bafc2df23ddc785ed116fced1f492ad90a6830cf24f4d1ea27d24"}, ] +"ptyprocess 0.7.0" = [ + {url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, + {url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, +] "pulsectl 22.3.2" = [ {url = "https://files.pythonhosted.org/packages/38/b5/7fb041271e9db6ad54a74eecbc84f8c9a9faf696c267b63dfc9fbbf63d49/pulsectl-22.3.2.tar.gz", hash = "sha256:cc174e1ceebd4e6222c5b78f4f415f12e80753c9ab75ab354159b4cb5950b084"}, {url = "https://files.pythonhosted.org/packages/e7/dc/53454b4d9f52f53aa5d95b0c5aa1adaf80e324f760e9086db4e2de2dab24/pulsectl-22.3.2-py2.py3-none-any.whl", hash = "sha256:6fded96a8e6a9ac6c387ed3a9bdb1afb20d80256b0c9df4b98b58461d74ba0c4"}, @@ -286,17 +481,22 @@ {url = "https://files.pythonhosted.org/packages/02/98/b5618e70db2279c9d7f604283df21eadd0d0a5e878bc5a0b9fd85e14fc96/pulsectl-asyncio-1.0.0.tar.gz", hash = "sha256:46ef1573a17fb064196e8d8f0d9031a9377f717cde26769d97959ce4b9395af0"}, {url = "https://files.pythonhosted.org/packages/41/4d/b973e74ed465735fc230089cc9167152f692d7f6221ffccf35a3b3156e7f/pulsectl_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:5842fe428da9ede62077469f15dedd7c19f1e0085a641f22e2266449fd394dd2"}, ] +"pure-eval 0.2.2" = [ + {url = "https://files.pythonhosted.org/packages/2b/27/77f9d5684e6bce929f5cfe18d6cfbe5133013c06cb2fbf5933670e60761d/pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {url = "https://files.pythonhosted.org/packages/97/5a/0bc937c25d3ce4e0a74335222aee05455d6afa2888032185f8ab50cdf6fd/pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] "pycparser 2.21" = [ {url = "https://files.pythonhosted.org/packages/5e/0b/95d387f5f4433cb0f53ff7ad859bd2c6051051cebbb564f139a999ab46de/pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, {url = "https://files.pythonhosted.org/packages/62/d5/5f610ebe421e85889f2e55e33b7f9a6795bd982198517d912eb1c76e1a53/pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, ] +"pygments 2.14.0" = [ + {url = "https://files.pythonhosted.org/packages/0b/42/d9d95cc461f098f204cd20c85642ae40fbff81f74c300341b8d0e0df14e0/Pygments-2.14.0-py3-none-any.whl", hash = "sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717"}, + {url = "https://files.pythonhosted.org/packages/da/6a/c427c06913204e24de28de5300d3f0e809933f376e0b7df95194b2bb3f71/Pygments-2.14.0.tar.gz", hash = "sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297"}, +] "python-xlib 0.33" = [ {url = "https://files.pythonhosted.org/packages/86/f5/8c0653e5bb54e0cbdfe27bf32d41f27bc4e12faa8742778c17f2a71be2c0/python-xlib-0.33.tar.gz", hash = "sha256:55af7906a2c75ce6cb280a584776080602444f75815a7aff4d287bb2d7018b32"}, {url = "https://files.pythonhosted.org/packages/fc/b8/ff33610932e0ee81ae7f1269c890f697d56ff74b9f5b2ee5d9b7fa2c5355/python_xlib-0.33-py2.py3-none-any.whl", hash = "sha256:c3534038d42e0df2f1392a1b30a15a4ff5fdc2b86cfa94f072bf11b10a164398"}, ] -"quartz 0.0.1.dev0" = [ - {url = "https://files.pythonhosted.org/packages/31/f2/4fac785ec7690dd2b9f87506aa52e702ed5d98dedc06a2e940c4a076136a/quartz-0.0.1.dev0.tar.gz", hash = "sha256:95d6adb994aa2713dc51fa9f1799227e247c826b71216ebf25cecab884685d35"}, -] "runcmd 2020.12.3" = [ {url = "https://files.pythonhosted.org/packages/41/b7/08c18bc2944c0443aa77672e5610e861c6946f93f247bef0aa8d7737b248/runcmd-2020.12.3.tar.gz", hash = "sha256:1f2652c2e51404bb0faaa83610f23fdafd4c246b46e361a5dd6d4334d013fc24"}, ] @@ -308,6 +508,10 @@ {url = "https://files.pythonhosted.org/packages/c3/a0/5dba8ed157b0136607c7f2151db695885606968d1fae123dc3391e0cfdbf/sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, {url = "https://files.pythonhosted.org/packages/cd/50/d49c388cae4ec10e8109b1b833fd265511840706808576df3ada99ecb0ac/sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +"stack-data 0.6.2" = [ + {url = "https://files.pythonhosted.org/packages/6a/81/aa96c25c27f78cdc444fec27d80f4c05194c591465e491a1358d8a035bc1/stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, + {url = "https://files.pythonhosted.org/packages/db/18/aa7f2b111aeba2cd83503254d9133a912d7f61f459a0c8561858f0d72a56/stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, +] "starlette 0.23.1" = [ {url = "https://files.pythonhosted.org/packages/0c/d2/a2898deb36d12e40e84e83b7728628a04013cb0cfc545932c4605185bf2d/starlette-0.23.1.tar.gz", hash = "sha256:8510e5b3d670326326c5c1d4cb657cc66832193fe5d5b7015a51c7b1e1b1bf42"}, {url = "https://files.pythonhosted.org/packages/a3/1d/b23984c05e39ddab35bbba33a3828dc4f896250220dcbd946c0fcad1e934/starlette-0.23.1-py3-none-any.whl", hash = "sha256:ec69736c90be8dbfc6ec6800ba6feb79c8c44f9b1706c0b2bb27f936bcf362cc"}, @@ -316,10 +520,18 @@ {url = "https://files.pythonhosted.org/packages/0c/28/5ef1e05bd24984d508e93d4a0319b7f63f8ce5d3c33dd1515507c024f76e/starlette_exporter-0.15.1-py3-none-any.whl", hash = "sha256:24eeaef01f05ef973984704427f6e6a93d468f487b8b26ad77548d963affc9fe"}, {url = "https://files.pythonhosted.org/packages/7c/6d/72fbe02fd9dec57c8990f466255082a26a7b298ccf8a6960f79f3286b746/starlette_exporter-0.15.1.tar.gz", hash = "sha256:3bde5d863effb26684210fe016a1ebf2b383efedf21a4b2f28585ec5064f4033"}, ] +"traitlets 5.9.0" = [ + {url = "https://files.pythonhosted.org/packages/39/c3/205e88f02959712b62008502952707313640369144a7fded4cbc61f48321/traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, + {url = "https://files.pythonhosted.org/packages/77/75/c28e9ef7abec2b7e9ff35aea3e0be6c1aceaf7873c26c95ae1f0d594de71/traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, +] "uvicorn 0.20.0" = [ {url = "https://files.pythonhosted.org/packages/95/3c/9f4650ff609370456f796bb96a355dcddef1ded67e05d1b4eb3481088329/uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"}, {url = "https://files.pythonhosted.org/packages/96/f3/f39ac8ac3bdf356b4934b8f7e56173e96681f67ef0cd92bd33a5059fae9e/uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"}, ] +"wcwidth 0.2.6" = [ + {url = "https://files.pythonhosted.org/packages/20/f4/c0584a25144ce20bfcf1aecd041768b8c762c1eb0aa77502a3f0baa83f11/wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {url = "https://files.pythonhosted.org/packages/5e/5f/1e4bd82a9cc1f17b2c2361a2d876d4c38973a997003ba5eb400e8a932b6c/wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] "xprintidle 0.2" = [ {url = "https://files.pythonhosted.org/packages/19/64/5ca302f96fd499e65f3b3d49ca5d41b8d9fac7a70ae5a7094baf93db380a/xprintidle-0.2.tar.gz", hash = "sha256:86695d81a4b2399752946f7f79f4a5d48abbac6c325c0f34b3e82a9bff063453"}, ] diff -r 0ecb388a0b90 -r 3d7fc94a404a power_linux.py --- a/power_linux.py Sat Mar 04 12:10:36 2023 -0800 +++ b/power_linux.py Wed Mar 08 10:57:11 2023 -0800 @@ -8,7 +8,7 @@ def is_screen_on(): _display.sync() - return _display.dpms_info().state== 1 # untested + return _display.dpms_info().state == 1 # untested def go_lock_screen(): @@ -23,4 +23,3 @@ def go_sleep_mode(): subprocess.run(['systemctl', 'suspend']) - diff -r 0ecb388a0b90 -r 3d7fc94a404a power_osx.py --- a/power_osx.py Sat Mar 04 12:10:36 2023 -0800 +++ b/power_osx.py Wed Mar 08 10:57:11 2023 -0800 @@ -1,10 +1,18 @@ import subprocess -import Quartz +import ast + def is_screen_on(): - d=Quartz.CGSessionCopyCurrentDictionary() - print(d) - + screen_is_locked = ast.literal_eval( + subprocess.check_output( + [ + '/usr/bin/python', # system one with Quartz available + '-sc', + 'import Quartz; ' + + 'print Quartz.CGSessionCopyCurrentDictionary().get("CGSSessionScreenIsLocked", False)' + ], + encoding='ascii')) + return not screen_is_locked def go_lock_screen(): @@ -18,4 +26,5 @@ def go_sleep_mode(): subprocess.run(['pmset', 'sleepnow']) + is_screen_on() \ No newline at end of file diff -r 0ecb388a0b90 -r 3d7fc94a404a progs_all.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/progs_all.py Wed Mar 08 10:57:11 2023 -0800 @@ -0,0 +1,29 @@ +import psutil +from typing import Dict, List, Optional + + +def progname(cmdline: List[str]) -> Optional[str]: + if len(cmdline) < 1: + return None + if cmdline[-1].endswith('/steam'): + return 'steam' + if cmdline[0].endswith('/minecraft-launcher'): + return 'minecraft-launcher' + if cmdline[0].endswith('/java') and '--versionType' in cmdline: + return 'minecraft' + if cmdline[0].endswith('/RobloxPlayer'): + return 'roblox' + + +def get_running_progs() -> Dict[str, int]: + progs = set() + for proc in psutil.process_iter(['pid', 'name']): + try: + prog = progname(proc.cmdline()) + if prog: + progs.add(prog) + except (psutil.AccessDenied, psutil.NoSuchProcess): + pass + return dict( + (p, p in progs) + for p in ['steam', 'minecraft-launcher', 'minecraft', 'roblox']) diff -r 0ecb388a0b90 -r 3d7fc94a404a pyproject.toml --- a/pyproject.toml Sat Mar 04 12:10:36 2023 -0800 +++ b/pyproject.toml Wed Mar 08 10:57:11 2023 -0800 @@ -11,11 +11,16 @@ "psutil>=5.9.4", "background-loop>=1.7.0", "starlette-exporter>=0.15.1", + "daemonocle>=1.2.3", + "python-xlib>=0.33", ] requires-python = ">=3.10" license = {text = "MIT"} [project.optional-dependencies] +dev = [ + "ipython>=8.11.0", +] linux = [ "xprintidle>=0.2", "pulsectl-asyncio>=1.0.0", @@ -23,7 +28,7 @@ ] osx = [ "osascript>=2020.12.3", - "quartz>=0.0.1.dev0" + # "quartz>=0.0.1.dev0" ] [tool.pdm] diff -r 0ecb388a0b90 -r 3d7fc94a404a racc.py --- a/racc.py Sat Mar 04 12:10:36 2023 -0800 +++ b/racc.py Wed Mar 08 10:57:11 2023 -0800 @@ -1,70 +1,80 @@ -from starlette.applications import Starlette -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.responses import JSONResponse, HTMLResponse -from starlette.staticfiles import StaticFiles -from starlette.routing import Route, Mount -from starlette.templating import Jinja2Templates -import uvicorn -import psutil -from starlette_exporter import PrometheusMiddleware, handle_metrics -from prometheus_client import Summary, Gauge -import background_loop -from typing import List, Optional import logging +import os import socket import sys + +import background_loop +import daemonocle +import psutil +import uvicorn +from prometheus_client import Gauge +from starlette.applications import Starlette +from starlette.requests import Request +from starlette.responses import HTMLResponse +from starlette.routing import Route +from starlette.staticfiles import StaticFiles +from starlette_exporter import PrometheusMiddleware, handle_metrics +'' +import progs_all as progs + if psutil.OSX: + import current_window_title_osx as current_window_title import idle_osx as idle - import volume_osx as volume import power_osx as power + import volume_osx as volume elif psutil.LINUX: + import current_window_title_linux as current_window_title import idle_linux as idle + import power_linux as power import volume_linux as volume - import power_linux as power else: raise NotImplementedError(repr(sys.implementation)) -hostname = socket.gethostname().split('.')[0] +DEBUG = os.environ.get('RACC_DEBUG', False) logging.basicConfig(level=logging.INFO) log = logging.getLogger() - - -def progname(cmdline: List[str]) -> Optional[str]: - if len(cmdline) < 1: - return None - if cmdline[-1].endswith('/steam'): - return 'steam' - if cmdline[0].endswith('/minecraft-launcher'): - return 'minecraft-launcher' - if cmdline[0].endswith('/java') and '--versionType' in cmdline: - return 'minecraft' - - +hostname = socket.gethostname().split('.')[0] RACC_RUNNING = Gauge("racc_running", "program is running", ['host', 'prog']) RACC_IDLE = Gauge("racc_idle", "desktop mouse/kb idle seconds", ['host']) -RACC_SCREEN = Gauge("racc_screen", "screen in unlocked/on mode", ['host']) +RACC_SCREEN = Gauge("racc_screen_on", "screen in unlocked/on mode", ['host']) +RACC_CURRENT_WINDOW = Gauge("racc_current_window", + "label carries title; site is last part", + ['host', 'title', 'site']) -def update_progs(first_run): - out = [] - progs = set() - for proc in psutil.process_iter(['pid', 'name']): - try: - prog = progname(proc.cmdline()) - if prog: - progs.add(prog) - except (psutil.AccessDenied, psutil.NoSuchProcess): - pass +def update(first_run): + try: + for p, val in progs.get_running_progs().items(): + RACC_RUNNING.labels(host=hostname, prog=p).set(val) + except Exception: + if DEBUG: raise + + try: + RACC_IDLE.labels(host=hostname).set(idle.get_idle_seconds()) + except Exception: + if DEBUG: raise + + try: + RACC_SCREEN.labels(host=hostname).set(power.is_screen_on()) + except Exception: + if DEBUG: raise - for p in [ - 'minecraft', - 'minecraft-launcher', - 'steam', - ]: - RACC_RUNNING.labels(host=hostname, prog=p).set(p in progs) - RACC_IDLE.labels(host=hostname).set(idle.get_idle_seconds()) - RACC_SCREEN.labels(host=hostname).set(power.is_screen_on()) + try: + title = current_window_title.get_current_window_title() + # chrome, at least on osx, adds icon to window title when it's playing audio + title = title.rstrip('🔊').strip() + # some websites choose to title like ' - ' + last_section = title.split(' - ')[-1].strip() + if last_section in {'YouTube', 'Google Search', 'Google Chrome', 'Visual Studio Code', 'Roblox'}: + site = last_section.lower().replace(' ', '_') + else: + site = '' + RACC_CURRENT_WINDOW.clear() + RACC_CURRENT_WINDOW.labels(host=hostname, title=title, + site=site).set(1) + except Exception: + if DEBUG: raise + async def root(req: Request) -> HTMLResponse: vol = await volume.get_volume() @@ -77,13 +87,23 @@ Route('/', root), ], on_startup=[ - lambda: background_loop.loop_forever(update_progs, 3), + lambda: background_loop.loop_forever(update, 5), ]) app.add_middleware(PrometheusMiddleware, app_name='racc') app.add_route("/metrics", handle_metrics) - return app + uvicorn.run(app, host='0.0.0.0', port=5150) if __name__ == "__main__": - uvicorn.run(main(), host='0.0.0.0', port=5150) + d = daemonocle.Daemon( + worker=main, + work_dir='.', + pid_file='/tmp/racc.pid', + detach=True, + ) + d.stop(timeout=1, force=True) + if DEBUG: + main() + else: + d.cli() diff -r 0ecb388a0b90 -r 3d7fc94a404a run.sh --- a/run.sh Sat Mar 04 12:10:36 2023 -0800 +++ b/run.sh Wed Mar 08 10:57:11 2023 -0800 @@ -1,9 +1,11 @@ #!/bin/sh +cd `dirname $0` + if [ `uname` = Linux ]; then - pdm run python racc.py + pdm run python racc.py restart elif [ `uname` = Darwin ]; then - ${HOME}/Library/Python/3.10/bin/pdm run python racc.py + ${HOME}/Library/Python/3.10/bin/pdm run python racc.py restart else echo `uname` unknown exit 1