diff --git a/config.js b/config.js index 4ad7fc2..43de8d4 100644 --- a/config.js +++ b/config.js @@ -23,10 +23,10 @@ DirectoryMonitorService.connect("changed", () => applyScss()); applyScss(); const workspaceMonitorMap = { - 0: [4,5], - 1: [6,7], - 2: [1,2,3,8,9,10], -} + 0: [4, 5], + 1: [6, 7], + 2: [1, 2, 3, 8, 9, 10], +}; App.config({ windows: [ @@ -42,4 +42,17 @@ App.config({ launcher: 350, bar0: 350, }, + onConfigParsed: () => + Utils.subprocess( + [ + "python3", + `${App.configDir}/services/bluetooth.py`, + ], + + // callback when the program outputs something to stdout + (output) => console.info(output), + + // callback on error + (err) => console.info(err), + ), }); diff --git a/modules/menus/bluetooth/index.js b/modules/menus/bluetooth/index.js index ee8780d..97dfe40 100644 --- a/modules/menus/bluetooth/index.js +++ b/modules/menus/bluetooth/index.js @@ -122,14 +122,16 @@ export default () => { class_name: "menu-icon-button-label delete bluetooth", label: "󰆴", }), - on_primary_click: () => + on_primary_click: () => { + // dev.setConnection(false); Utils.execAsync([ "bash", "-c", `bluetoothctl remove ${dev.address}`, ]).catch((err) => console.error("Bluetooth Remove", err), - ), + ); + }, }), ], }), @@ -188,29 +190,7 @@ export default () => { hexpand: true, class_name: `menu-button bluetooth ${device}`, on_primary_click: () => { - Utils.execAsync([ - "bash", - "-c", - `bluetoothctl pair ${device.address}`, - ]).catch((err) => { - console.error( - `bluetoothctl pair ${device.address}`, - err, - ); - - setTimeout(() => { - Utils.execAsync([ - "bash", - "-c", - `bluetoothctl connect ${device.address}`, - ]).catch((err) => - console.error( - `bluetoothctl connect ${device.address}`, - err, - ), - ); - }, 2000); - }); + device.setConnection(true); }, child: Widget.Box({ children: [ @@ -253,16 +233,6 @@ export default () => { return Widget.Box({ vertical: true, children: [ - Widget.Box({ - class_name: "menu-active-container bluetooth", - vertical: true, - // children: renderActivePlayback(), - }), - Widget.Box({ - class_name: "menu-active-container bluetooth", - vertical: true, - // children: renderActiveInput(), - }), Widget.Separator({ class_name: "menu-separator", }), diff --git a/modules/notifications/index.js b/modules/notifications/index.js index d2498bf..88c7a64 100644 --- a/modules/notifications/index.js +++ b/modules/notifications/index.js @@ -15,6 +15,7 @@ export default () => { class_name: "notification-card-container", setup: (self) => { self.hook(notifs, () => { + console.log(JSON.stringify(notifs.popups, null, 2)); if (notifs.dnd) { return; } @@ -25,10 +26,10 @@ export default () => { Widget.Box({ class_name: "notification-card-image-container", hpack: "center", - vexpand: true, + vexpand: false, child: Widget.Box({ hpack: "center", - vexpand: true, + vexpand: false, class_name: "notification-card-image", css: `background-image: url("${notif.image}")`, }), @@ -40,13 +41,7 @@ export default () => { }; return (self.children = notifs.popups.map((notif, index) => { - return Widget.Button({ - on_primary_click: () => { - notifs.CloseNotification(notif.id); - }, - on_secondary_click: () => { - notifs.CloseNotification(notif.id); - }, + return Widget.Box({ child: Widget.Box({ class_name: "notification-card", children: [ @@ -81,6 +76,29 @@ export default () => { }), ], }), + Widget.Box({ + class_name: "notification-card-actions", + children: notif.actions.map((action) => { + return Widget.Button({ + class_name: "notification-action-buttons", + on_primary_click: () => { + console.log(`clicked: ${action.id}`); + notif.invoke(action.id); + }, + child: Widget.Box({ + children: [ + Widget.Label({ + hpack: "center", + hexpand: true, + class_name: + "notification-action-buttons-label", + label: action.label, + }), + ], + }), + }); + }), + }), Widget.Box({ class_name: "notification-card-appname", children: [ diff --git a/scss/notifications/popups.scss b/scss/notifications/popups.scss index 1253bf1..fb89cf4 100644 --- a/scss/notifications/popups.scss +++ b/scss/notifications/popups.scss @@ -26,17 +26,18 @@ .notification-card-image-container { - margin: 0.5rem 0.75rem; + margin: 0.75rem 0.75rem; min-width: 4rem; min-height: 4rem; border-radius: 0.4rem; } .notification-card-image { + border: 0.2rem solid $base; border-radius: 0.4rem; min-width: 3rem; min-height: 3rem; padding: 1rem 1rem; - margin: 0.5rem 0rem; + // margin-top: 1rem; background-size: contain; background-repeat: no-repeat; } @@ -55,9 +56,28 @@ .notification-card-header-label { font-size: 1.15rem; + margin-bottom: 0.5rem; color: $lavender; } .notification-card-body-label { font-size: 1rem; + font-weight: 600; +} + +.notification-card-actions { + margin: 1rem 0rem; +} + +.notification-action-buttons { + color: $mantle; + background: $lavender; + margin-right: 2rem; + min-width: 5rem; + min-height: 2rem; + border-radius: 0.4rem; + + &:hover { + background: $pink; + } } diff --git a/services/bluetooth.py b/services/bluetooth.py new file mode 100755 index 0000000..bc26f77 --- /dev/null +++ b/services/bluetooth.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +import dbus +import dbus.service +import dbus.mainloop.glib +from gi.repository import GLib +import subprocess +import logging + +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +AGENT_PATH = "/test/agent" +CAPABILITY = "NoInputNoOutput" + +def send_notification_with_actions(title, message, actions, action_handler): + bus = dbus.SessionBus() + notification_object = bus.get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications") + notify_interface = dbus.Interface(notification_object, "org.freedesktop.Notifications") + bus.add_signal_receiver(action_handler, + dbus_interface="org.freedesktop.Notifications", + signal_name="ActionInvoked") + notify_interface.Notify("bluetooth_agent", 0, "", title, message, actions, {}, -1) + +class Agent(dbus.service.Object): + def __init__(self, bus): + dbus.service.Object.__init__(self, bus, AGENT_PATH) + + @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="") + def Release(self): + logging.info("Release") + + def get_device_name(self, device): + bus = dbus.SystemBus() + device_proxy = bus.get_object("org.bluez", device) + device_properties = dbus.Interface(device_proxy, "org.freedesktop.DBus.Properties") + return device_properties.Get("org.bluez.Device1", "Name") + + @dbus.service.method("org.bluez.Agent1", in_signature="o", out_signature="") + def RequestPinCode(self, device): + device_name = self.get_device_name(device) + logging.info(f"RequestPinCode {device_name} ({device})") + self.request_input("Enter PIN code", f"Enter PIN code for device {device_name}", device, "pin") + + @dbus.service.method("org.bluez.Agent1", in_signature="o", out_signature="u") + def RequestPasskey(self, device): + device_name = self.get_device_name(device) + logging.info(f"RequestPasskey {device_name} ({device})") + self.request_input("Enter Passkey", f"Enter passkey for device {device_name}", device, "passkey") + + @dbus.service.method("org.bluez.Agent1", in_signature="ou", out_signature="") + def DisplayPasskey(self, device, passkey): + device_name = self.get_device_name(device) + logging.info(f"DisplayPasskey {device_name} passkey {passkey}") + send_notification_with_actions("Bluetooth Pairing Request", f"Passkey for device {device_name} is {passkey}", [], lambda *args: None) + + @dbus.service.method("org.bluez.Agent1", in_signature="ou", out_signature="") + def RequestConfirmation(self, device, passkey): + device_name = self.get_device_name(device) + logging.info(f"RequestConfirmation {device_name} passkey {passkey}") + actions = ["confirm", "Confirm", "deny", "Deny"] + + def action_handler(notification_id, action_key): + if action_key == "confirm": + logging.info(f"Confirmed pairing for {device_name}") + self.send_reply(device) + elif action_key == "deny": + logging.info(f"Denied pairing for {device_name}") + self.send_error(device, "org.bluez.Error.Rejected") + + send_notification_with_actions("Bluetooth Pairing Request", + f"Confirm passkey {passkey} for device {device_name}", + actions, action_handler) + return + + @dbus.service.method("org.bluez.Agent1", in_signature="o", out_signature="") + def RequestAuthorization(self, device): + device_name = self.get_device_name(device) + logging.info(f"RequestAuthorization {device_name}") + actions = ["confirm", "Confirm", "deny", "Deny"] + + def action_handler(notification_id, action_key): + if action_key == "confirm": + logging.info(f"Authorized device {device_name}") + self.send_reply(device) + elif action_key == "deny": + logging.info(f"Denied authorization for {device_name}") + self.send_error(device, "org.bluez.Error.Rejected") + + send_notification_with_actions("Bluetooth Service Authorization", + f"Authorize device {device_name}", + actions, action_handler) + return + + @dbus.service.method("org.bluez.Agent1", in_signature="os", out_signature="") + def AuthorizeService(self, device, uuid): + device_name = self.get_device_name(device) + logging.info(f"AuthorizeService {device_name} uuid {uuid}") + actions = ["confirm", "Confirm", "deny", "Deny"] + + def action_handler(notification_id, action_key): + if action_key == "confirm": + logging.info(f"Authorized service {uuid} for device {device_name}") + self.send_reply(device) + elif action_key == "deny": + logging.info(f"Denied authorization for service {uuid} on device {device_name}") + self.send_error(device, "org.bluez.Error.Rejected") + + send_notification_with_actions("Bluetooth Service Authorization", + f"Authorize service {uuid} for device {device_name}", + actions, action_handler) + return + + @dbus.service.method("org.bluez.Agent1", in_signature="", out_signature="") + def Cancel(self): + logging.info("Cancel") + + def request_input(self, title, message, device, input_type): + def action_handler(notification_id, action_key): + if action_key == "input": + result = subprocess.run(["zenity", "--entry", "--title", title, "--text", message], capture_output=True, text=True) + user_input = result.stdout.strip() + if input_type == "pin": + self.handle_pin_input(device, user_input) + elif input_type == "passkey": + self.handle_passkey_input(device, user_input) + elif action_key == "cancel": + self.send_error(device, "org.bluez.Error.Rejected") + + actions = ["input", "Enter", "cancel", "Cancel"] + send_notification_with_actions(title, message, actions, action_handler) + + def handle_pin_input(self, device, pin_code): + logging.info(f"PIN code entered for {device}: {pin_code}") + self.send_reply(device) + + def handle_passkey_input(self, device, passkey): + logging.info(f"Passkey entered for {device}: {passkey}") + self.send_reply(device) + + def send_reply(self, device): + logging.info(f"Sending reply for {device}") + bus = dbus.SystemBus() + agent = bus.get_object("org.bluez", device) + agent_interface = dbus.Interface(agent, "org.bluez.Device1") + agent_interface.Pair(reply_handler=self.success_callback, error_handler=self.error_callback) + + def send_error(self, device, error): + logging.info(f"Sending error for {device}") + bus = dbus.SystemBus() + agent = bus.get_object("org.bluez", device) + agent_interface = dbus.Interface(agent, "org.bluez.Device1") + agent_interface.CancelPairing(reply_handler=self.success_callback, error_handler=self.error_callback) + + def success_callback(self): + logging.info("Operation succeeded") + + def error_callback(self, error): + logging.error(f"Operation failed: {error}") + +def register_agent(): + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"), "org.bluez.AgentManager1") + path = AGENT_PATH + agent = Agent(bus) + manager.RegisterAgent(path, CAPABILITY) + manager.RequestDefaultAgent(path) + logging.info("Agent registered") + +if __name__ == "__main__": + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + register_agent() + loop = GLib.MainLoop() + loop.run() diff --git a/style.css b/style.css index 2240cc7..a5e326f 100644 --- a/style.css +++ b/style.css @@ -837,18 +837,18 @@ window#powermenu .powermenu.box { } .notification-card-image-container { - margin: 0.5rem 0.75rem; + margin: 0.75rem 0.75rem; min-width: 4rem; min-height: 4rem; border-radius: 0.4rem; } .notification-card-image { + border: 0.2rem solid #1e1e2e; border-radius: 0.4rem; min-width: 3rem; min-height: 3rem; padding: 1rem 1rem; - margin: 0.5rem 0rem; background-size: contain; background-repeat: no-repeat; } @@ -867,11 +867,29 @@ window#powermenu .powermenu.box { .notification-card-header-label { font-size: 1.15rem; + margin-bottom: 0.5rem; color: #b4befe; } .notification-card-body-label { font-size: 1rem; + font-weight: 600; +} + +.notification-card-actions { + margin: 1rem 0rem; +} + +.notification-action-buttons { + color: #181825; + background: #b4befe; + margin-right: 2rem; + min-width: 5rem; + min-height: 2rem; + border-radius: 0.4rem; +} +.notification-action-buttons:hover { + background: #f5c2e7; } /*# sourceMappingURL=style.css.map */ diff --git a/style.css.map b/style.css.map index aa08b7d..16d639e 100644 --- a/style.css.map +++ b/style.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["scss/main.scss","scss/common/common.scss","scss/colors.scss","scss/common/widget-button.scss","scss/bar/menu.scss","scss/bar/audio.scss","scss/bar/media.scss","scss/bar/network.scss","scss/bar/bluetooth.scss","scss/bar/clock.scss","scss/bar/workspace.scss","scss/bar/window_title.scss","scss/bar/systray.scss","scss/bar/power.scss","scss/bar/bar.scss","scss/menus/menu.scss","scss/menus/power.scss","scss/common/floating-widget.scss","scss/menus/audiomenu.scss","scss/menus/bluetooth.scss","scss/notifications/popups.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;;;ACJF;EACE;EACA;EACA,kBCFgB;EDGhB;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI,kBChBW;;ADkBf;EACE,OCnBa;;ADqBf;EACE;EACA;EACA;EACA;;AAGJ;EACE;EACA;EACA,kBC/Be;;;ADmCnB;EACE;EACA;EACA;EACA,OCvCiB;;;AD0CnB;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA,kBC9DiB;ED+DjB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA,kBC9EgB;;;ADiFlB;AAAA;EAEE;EACA,kBCpFgB;;;ADuFlB;AAAA;AAAA;EAGE,OC1FgB;ED2FhB,kBC7CW;;;ADgDb;AAAA;AAAA;EAGE;EACA,kBCpDW;;;ADuDb;AAAA;AAAA;EAGE;EACA,kBC3DW;;;AD8Db;EACE;EACA;EACA;EACA;EACA,kBChHiB;EDiHjB;EACA;EACA;;;AAGF;EACE,kBC1EW;;;AD6Eb;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBCvIc;EDwId;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA,kBChKiB;;;ADmKnB;EACE;EACA,kBCxHW;;;AD2Hb;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA,OC1IW;ED2IX,kBCzLgB;ED0LhB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,kBC1MgB;ED2MhB,OC5Mc;ED6Md;;;AAGF;EACE;EACA;EACA,kBCjNiB;;;ADoNnB;EACE;IACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AE/NF;EACE,YDmCM;EClCN;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cD2BM;EC1BN;EACA;;AAEA;EACE;EACA,cDSO;ECRP;;AAGF;EAEE;EACA,cDEO;;ACCT;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","file":"style.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["scss/main.scss","scss/common/common.scss","scss/colors.scss","scss/common/widget-button.scss","scss/bar/menu.scss","scss/bar/audio.scss","scss/bar/media.scss","scss/bar/network.scss","scss/bar/bluetooth.scss","scss/bar/clock.scss","scss/bar/workspace.scss","scss/bar/window_title.scss","scss/bar/systray.scss","scss/bar/power.scss","scss/bar/bar.scss","scss/menus/menu.scss","scss/menus/power.scss","scss/common/floating-widget.scss","scss/menus/audiomenu.scss","scss/menus/bluetooth.scss","scss/notifications/popups.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;;;ACJF;EACE;EACA;EACA,kBCFgB;EDGhB;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACI,kBChBW;;ADkBf;EACE,OCnBa;;ADqBf;EACE;EACA;EACA;EACA;;AAGJ;EACE;EACA;EACA,kBC/Be;;;ADmCnB;EACE;EACA;EACA;EACA,OCvCiB;;;AD0CnB;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA,kBC9DiB;ED+DjB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA,kBC9EgB;;;ADiFlB;AAAA;EAEE;EACA,kBCpFgB;;;ADuFlB;AAAA;AAAA;EAGE,OC1FgB;ED2FhB,kBC7CW;;;ADgDb;AAAA;AAAA;EAGE;EACA,kBCpDW;;;ADuDb;AAAA;AAAA;EAGE;EACA,kBC3DW;;;AD8Db;EACE;EACA;EACA;EACA;EACA,kBChHiB;EDiHjB;EACA;EACA;;;AAGF;EACE,kBC1EW;;;AD6Eb;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA,kBCvIc;EDwId;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA,kBChKiB;;;ADmKnB;EACE;EACA,kBCxHW;;;AD2Hb;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA,OC1IW;ED2IX,kBCzLgB;ED0LhB;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE,kBC1MgB;ED2MhB,OC5Mc;ED6Md;;;AAGF;EACE;EACA;EACA,kBCjNiB;;;ADoNnB;EACE;IACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AE/NF;EACE,YDmCM;EClCN;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cD2BM;EC1BN;EACA;;AAEA;EACE;EACA,cDSO;ECRP;;AAGF;EAEE;EACA,cDEO;;ACCT;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","file":"style.css"} \ No newline at end of file