diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1878045..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.venv/ -*.pyc -__pycache__/ diff --git a/KillSwitch b/KillSwitch deleted file mode 100755 index bfa711c..0000000 --- a/KillSwitch +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/python3 - -""" -* Copyright 2021 Pong Loong Yeat (https://github.com/pongloongyeat/killswitch) -* -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public -* License as published by the Free Software Foundation; either -* version 3 of the License, or (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* General Public License for more details. -* -* You should have received a copy of the GNU General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -* Boston, MA 02110-1301 USA -""" - -import os -import gi - -gi.require_version("Gtk", "3.0") -gi.require_version("Gio", "2.0") -gi.require_version("Granite", "1.0") -from gi.repository import Gtk -from gi.repository import Gio -from gi.repository import Granite - -class MainWindow(Gtk.Window): - def __init__(self): - Gtk.Window.__init__(self, title="KillSwitch") - - self.gtk_settings = Gtk.Settings.get_default() - self.granite_settings = Granite.Settings.get_default() - - self.set_colour_scheme() - self.granite_settings.connect("notify::prefers-color-scheme", self.set_colour_scheme) - - self.set_size_request(480, 320) - self.set_default_size(480, 320) - self.props.resizable = False - - welcome = Granite.WidgetsWelcome.new("KillSwitch", "Kills running processes") - welcome.append("application-default-icon", "Applications", "Kills all running user-installed applications.") - welcome.append("preferences-desktop-wallpaper", "Desktop components", "Kills the dock and top bar.") - - welcome.connect("activated", self.on_welcome_click) - - self.add(welcome) - - - def set_colour_scheme(self, *args, **kwargs): - self.gtk_settings.props.gtk_application_prefer_dark_theme = ( - self.granite_settings.props.prefers_color_scheme == Granite.SettingsColorScheme.DARK - ) - - - """ - Most work happens here. This is where we gather the list of apps - we want to kill. This just checks for any apps in - /usr/share/applications and adds them to a list to be attempted - to be killed. - """ - def get_app_list(self): - desktop_files = os.listdir("/usr/share/applications") - desktop_files.sort() - app_list = [] - exec_list = [] - - for desktop_file in desktop_files: - if ".desktop" in desktop_file: - try: - desktop_app_info = Gio.DesktopAppInfo.new(desktop_file) - - # Exclude apps that launch in terminal. - if not desktop_app_info.get_boolean("Terminal"): - app_info = AppInfo( - desktop_app_info.get_executable() - ) - - # Check to make sure no duplicate entries - if app_info.exec_name not in exec_list: - exec_list.append(app_info.exec_name) - app_list.append(app_info) - except: - pass - - return app_list - - def show_message_dialog(self, on_response, primary_text="", secondary_text=""): - message_dialog = Granite.MessageDialog.with_image_from_icon_name( - primary_text=primary_text, - secondary_text=secondary_text, - image_icon_name="dialog-warning", - buttons=Gtk.ButtonsType.CANCEL - ) - - suggested_button = Gtk.Button.new_with_label("Proceed") - suggested_button.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION) - message_dialog.add_action_widget(suggested_button, Gtk.ResponseType.ACCEPT) - - message_dialog.props.transient_for = self - - message_dialog.connect("response", on_response) - message_dialog.show_all() - - def on_welcome_click(self, widget, index, *args): - if index == 0: - self.show_message_dialog( - self.on_app_response, - "Are you sure you want to proceed?", - "This will kill all running applications. Please save your work before proceeding." - ) - elif index == 1: - self.show_message_dialog( - self.on_desktop_response, - "Are you sure you want to proceed", - "This will kill the dock and top bar. If they are killed again within the next 120s, they will not automatically relaunch." - ) - else: - pass - - def on_app_response(self, dialog, response_id): - if response_id == Gtk.ResponseType.ACCEPT: - for app_info in self.get_app_list(): - if not app_info.is_desktop_component(): - app_info.kill() - - dialog.destroy() - - def on_desktop_response(self, dialog, response_id): - if response_id == Gtk.ResponseType.ACCEPT: - for app_info in self.get_app_list(): - if app_info.is_desktop_component(): - app_info.kill() - - dialog.destroy() - -class AppInfo: - DEFAULT_NO_KILL = [ - "evolution-alarm-notify", - "gala", - "mutter", - "pantheon-parental-controls-client", - ] - - def __init__(self, exec_raw): - self.exec_raw = exec_raw # Un-sanitised "Exec" parameter - self.exec_name = self.sanitise_exec(exec_raw) - - def sanitise_exec(self, exec_raw): - # Remove args and path - return exec_raw.split(" ")[0].split("/")[-1] - - def is_elementary(self): - return ("io.elementary" in self.exec_name or - "pantheon" in self.exec_name or - self.exec_name in ["gala", "plank"]) - - def is_lib(self): - return "lib" in self.exec_name - - def is_agent(self): - return "agent" in self.exec_name - - def is_daemon(self): - return "daemon" in self.exec_name - - # Not sure if naming is right here - def is_service(self): - return "bus" in self.exec_name - - def is_desktop_component(self): - return self.exec_name in ["io.elementary.wingpanel", "plank"] - - def should_kill(self): - if self.exec_name in self.DEFAULT_NO_KILL: - return False - - # Ignore libraries, agents and daemons - if self.is_lib() or self.is_agent() or self.is_daemon() or self.is_service(): - return False - - return True - - def kill(self): - if self.should_kill() is True: - print("Killing {}".format(self.exec_name)) - os.system("pkill {}".format(self.exec_name)) - -win = MainWindow() -win.connect("destroy", Gtk.main_quit) -win.show_all() -Gtk.main() diff --git a/COPYING b/LICENSE similarity index 99% rename from COPYING rename to LICENSE index 10926e8..f288702 100644 --- a/COPYING +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,12 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. - +. diff --git a/README.md b/README.md index 8acff21..82835be 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,8 @@

KillSwitch

-![screenshot](screenshot.png?raw=true) +![screenshot](app_screenshot.png?raw=true) Inspired by [killall](https://www.reddit.com/r/MacOS/comments/lt1vlh/a_simple_automator_app_with_an_intuitive_icon/). Special thanks to [hanaral](https://github.com/hanaral) for the icon! -Kills all applications. An attempt at making a simple one file app in Python. - -## Running - -Requires [GTK+-3.0](https://www.gtk.org/), [Granite](https://github.com/elementary/granite/), [PyGObject](https://pygobject.readthedocs.io/en/latest/) and [Python3](https://www.python.org/). Right click on KillSwitch and select 'Run' or run via `python3 KillSwitch`. - -## Adding to Applications Menu - -With [AppEditor](https://github.com/donadigo/appeditor) (recommended): - -1. Install [AppEditor](https://github.com/donadigo/appeditor). -2. Download KillSwitch (store it somewhere where it won't be accidentally deleted, i.e. in `~/Scripts` for instance). -3. Right click on KillSwitch and select "Create a Menu Entry". -4. Configure it however you want. - -Manual: - -1. Download KillSwitch (store it somewhere where it won't be accidentally deleted, i.e. in `~/Scripts` for instance). -2. Create a `com.github.pongloongyeat.killswitch.desktop` file in `~/.local/share/applications` with the following - -``` -[Desktop Entry] -Type=Application -Name=KillSwitch -Exec=/path/to/KillSwitch -Icon=/path/to/icon.svg -``` - -## Debugging - -Under the `AppInfo` class, comment out `os.system("pkill {}".format(self.exec_name))` and run via Terminal. Maybe a future, more feature-rich version that's not intended to be written in a single file can have a debugging flag/mode. +A simple app to kill all applications. diff --git a/app_screenshot.png b/app_screenshot.png new file mode 100644 index 0000000..c050cf9 Binary files /dev/null and b/app_screenshot.png differ diff --git a/icons/icon.svg b/data/icons/128/com.github.pongloongyeat.killswitch.svg similarity index 100% rename from icons/icon.svg rename to data/icons/128/com.github.pongloongyeat.killswitch.svg diff --git a/data/icons/16/com.github.pongloongyeat.killswitch.svg b/data/icons/16/com.github.pongloongyeat.killswitch.svg new file mode 100644 index 0000000..7126e73 --- /dev/null +++ b/data/icons/16/com.github.pongloongyeat.killswitch.svg @@ -0,0 +1,703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/24/com.github.pongloongyeat.killswitch.svg b/data/icons/24/com.github.pongloongyeat.killswitch.svg new file mode 100644 index 0000000..7126e73 --- /dev/null +++ b/data/icons/24/com.github.pongloongyeat.killswitch.svg @@ -0,0 +1,703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/32/com.github.pongloongyeat.killswitch.svg b/data/icons/32/com.github.pongloongyeat.killswitch.svg new file mode 100644 index 0000000..7126e73 --- /dev/null +++ b/data/icons/32/com.github.pongloongyeat.killswitch.svg @@ -0,0 +1,703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/48/com.github.pongloongyeat.killswitch.svg b/data/icons/48/com.github.pongloongyeat.killswitch.svg new file mode 100644 index 0000000..7126e73 --- /dev/null +++ b/data/icons/48/com.github.pongloongyeat.killswitch.svg @@ -0,0 +1,703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/64/com.github.pongloongyeat.killswitch.svg b/data/icons/64/com.github.pongloongyeat.killswitch.svg new file mode 100644 index 0000000..7126e73 --- /dev/null +++ b/data/icons/64/com.github.pongloongyeat.killswitch.svg @@ -0,0 +1,703 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/killswitch.appdata.xml b/data/killswitch.appdata.xml new file mode 100644 index 0000000..9a39e8a --- /dev/null +++ b/data/killswitch.appdata.xml @@ -0,0 +1,11 @@ + + + + com.github.pongloongyeat.killswitch + CC0 + KillSwitch + One app to kill them all + +

Kill all applications or desktop components with just a single click.

+
+
diff --git a/data/killswitch.desktop b/data/killswitch.desktop new file mode 100644 index 0000000..f2ea3e6 --- /dev/null +++ b/data/killswitch.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=KillSwitch +GenericName=Kill dock, wingpanel and apps +Comment=Kills Running Processes +Categories=Utility;System; +Exec=com.github.pongloongyeat.killswitch +Icon=application-default-icon +Terminal=false +Type=Application +Keywords=Apps;Kill;Application;Applications;Close;Quit; diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..c085530 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,12 @@ +icon_sizes = ['16', '24', '32', '48', '64', '128'] + +foreach i : icon_sizes + install_data( + join_paths('icons', i, meson.project_name() + '.svg'), + install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps') + ) + install_data( + join_paths('icons', i, meson.project_name() + '.svg'), + install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps') + ) +endforeach diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..7127777 --- /dev/null +++ b/meson.build @@ -0,0 +1,29 @@ +# project name and programming language +project('com.github.pongloongyeat.killswitch', 'vala', 'c') + +# Create a new executable, list the files we want to compile, list the dependencies we need, and install +executable( + meson.project_name(), + 'src/Application.vala', + 'src/MainWindow.vala', + dependencies: [ + dependency('gtk+-3.0'), + dependency('granite'), + dependency('libhandy-1') + ], + install: true +) + +# Install our .desktop file so the Applications Menu will see it +install_data( + join_paths('data', 'killswitch.desktop'), + install_dir: join_paths(get_option('datadir'), 'applications'), + rename: meson.project_name() + '.desktop' +) + +# Install our .appdata.xml file so AppCenter will see it +install_data( + join_paths('data', 'killswitch.appdata.xml'), + install_dir: join_paths(get_option('datadir'), 'metainfo'), + rename: meson.project_name() + '.appdata.xml' +) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b5d9635..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pycairo==1.20.0 -PyGObject==3.38.0 diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index cfc706c..0000000 Binary files a/screenshot.png and /dev/null differ diff --git a/src/Application.vala b/src/Application.vala new file mode 100644 index 0000000..87a8457 --- /dev/null +++ b/src/Application.vala @@ -0,0 +1,25 @@ +public class KillSwitch.Application : Gtk.Application { + public Application () { + Object ( + application_id: "com.github.pongloongyeat.killswitch", + flags: ApplicationFlags.FLAGS_NONE + ); + } + + protected override void activate () { + var granite_settings = Granite.Settings.get_default (); + var gtk_settings = Gtk.Settings.get_default (); + + gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; + granite_settings.notify["prefers-color-scheme"].connect (() => { + gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK; + }); + + var main_window = new KillSwitch.MainWindow(this); + main_window .show_all (); + } + + public static int main (string[] args) { + return new KillSwitch.Application ().run (args); + } +} diff --git a/src/MainWindow.vala b/src/MainWindow.vala new file mode 100644 index 0000000..fd71b78 --- /dev/null +++ b/src/MainWindow.vala @@ -0,0 +1,78 @@ +public class KillSwitch.MainWindow : Hdy.ApplicationWindow { + public MainWindow (Gtk.Application application) { + Object( + application: application, + resizable: false, + width_request: 480, + height_request: 320 + ); + } + + construct { + Hdy.init (); + + var headerbar = new Hdy.HeaderBar () { + show_close_button = true, + decoration_layout ="close:" + }; + headerbar.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + headerbar.get_style_context ().add_class ("default-decoration"); + + var applications_icon = new Gtk.Image () { + gicon = new ThemedIcon ("application-default-icon") + }; + + var desktop_components_icon = new Gtk.Image () { + gicon = new ThemedIcon ("preferences-desktop-wallpaper") + }; + + var title_label = new Gtk.Label ("KillSwitch") { + justify = Gtk.Justification.CENTER, + hexpand = true + }; + title_label.get_style_context ().add_class (Granite.STYLE_CLASS_H1_LABEL); + + var subtitle_label = new Gtk.Label ("Kills Running Applications") { + justify = Gtk.Justification.CENTER, + hexpand = true + }; + subtitle_label.get_style_context ().add_class (Granite.STYLE_CLASS_H2_LABEL); + + var options = new Gtk.Grid () { + expand = true, + margin = 12, + orientation = Gtk.Orientation.VERTICAL, + halign = Gtk.Align.CENTER, + margin_top = 24 + }; + + var applications_button = new Granite.Widgets.WelcomeButton (applications_icon, + "Applications", "Kills all running user-installed applications"); + + var desktop_components_button = new Granite.Widgets.WelcomeButton (desktop_components_icon, + "Desktop components", "Kills the dock and top bar"); + + options.add (applications_button); + options.add (desktop_components_button); + + var content = new Gtk.Grid () { + expand = true, + margin = 12, + orientation = Gtk.Orientation.VERTICAL, + valign = Gtk.Align.CENTER + }; + + content.add (title_label); + content.add (subtitle_label); + content.add (options); + + var grid = new Gtk.Grid (); + grid.attach (headerbar, 0, 0); + grid.attach (content, 0, 1); + + var handle = new Hdy.WindowHandle (); + handle.add (grid); + + add (handle); + } +}