Skip to content

Commit ba04a54

Browse files
authored
Merge pull request #1 from juanmagdev/develop
Fix UI Bug: State Not Updating After Cancel
2 parents 47a58cd + 50ddc1d commit ba04a54

File tree

2 files changed

+70
-20
lines changed

2 files changed

+70
-20
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ The extension interacts directly with the kernel to manage the LED states via co
1717
- **Morse Code Message**: You can input a text message, and the extension will flash the LED according to Morse code, providing a fun and functional way to communicate messages via the LED light.
1818

1919
## Known Bugs
20-
- **UI Update on Cancel**: If a state is selected and then canceled, the interface updates to reflect the selection even though no action is taken.
21-
- **Cannot Read LED State**: Currently, the extension cannot read the current state of the LED, meaning the state might not always be accurately reflected in the UI.
2220
- **State Reset After Restart**: After a system reboot, the LED state is reset to the default (LED On).
2321

2422
## TODOs
2523
- **Support for Uppercase and Special Characters in Morse Code**: Currently, only lowercase letters and numbers are supported for Morse code. Uppercase letters and special characters need to be handled.
26-
- **Fix Known Bugs**: Resolve issues with UI state updates, reading LED state, and state resetting after a reboot.
24+
- **Fix Known Bugs**: Resolve issues related with the state resetting after a reboot.
2725
- **Interface to Control Morse Code Blinking Speed**: Implement a user interface to allow users to customize the speed of the Morse code blinking.
2826

2927
## How It Works

extension.js

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import GObject from 'gi://GObject';
22
import GLib from 'gi://GLib';
3+
import Gio from 'gi://Gio';
34
import St from 'gi://St';
45
import Clutter from 'gi://Clutter';
56
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
@@ -13,6 +14,7 @@ const OFF_COMAND = "modprobe -r ec_sys && modprobe ec_sys write_support=1 && pri
1314
const ON_COMAND = "modprobe -r ec_sys && modprobe ec_sys write_support=1 && printf '\\x8a' | dd of=/sys/kernel/debug/ec/ec0/io bs=1 seek=12 count=1 conv=notrunc";
1415
const BLINK_COMAND = "modprobe -r ec_sys && modprobe ec_sys write_support=1 && printf '\\xca' | dd of=/sys/kernel/debug/ec/ec0/io bs=1 seek=12 count=1 conv=notrunc";
1516

17+
1618
const LedControlMenu = GObject.registerClass(
1719
class LedControlMenu extends QuickSettings.QuickMenuToggle {
1820
/**
@@ -85,28 +87,61 @@ class LedControlMenu extends QuickSettings.QuickMenuToggle {
8587
this._updateCheckState(this._currentCheckedIndex);
8688
}
8789

88-
8990
/**
90-
* Runs a shell command asynchronously.
91+
* Runs a shell command asynchronously and checks its output.
9192
* @param {Array} command - The command to execute, passed as an array of strings.
92-
* @returns {Promise} A promise that resolves when the command executes successfully or rejects if it fails.
93+
* @returns {Promise} Resolves if the command executes successfully, rejects otherwise.
9394
*/
9495
_runCommand(command) {
9596
return new Promise((resolve, reject) => {
9697
try {
97-
const [success, pid] = GLib.spawn_async(
98-
null,
99-
command,
100-
null,
101-
GLib.SpawnFlags.SEARCH_PATH,
102-
null
98+
let [success, pid, stdin, stdout, stderr] = GLib.spawn_async_with_pipes(
99+
null,
100+
command,
101+
null,
102+
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
103+
null
103104
);
104105

105-
if (success) {
106-
resolve();
107-
} else {
108-
reject(new Error('Failed to run the command.'));
106+
if (!success) {
107+
reject(new Error('Failed to start the command.'));
108+
return;
109109
}
110+
111+
// Read the output of the command
112+
let stdoutStream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: stdout, close_fd: true }) });
113+
let stderrStream = new Gio.DataInputStream({ base_stream: new Gio.UnixInputStream({ fd: stderr, close_fd: true }) });
114+
115+
let output = "";
116+
let errorOutput = "";
117+
118+
function readStream(stream, callback) {
119+
stream.read_line_async(GLib.PRIORITY_DEFAULT, null, (source, res) => {
120+
let [line, length] = source.read_line_finish(res);
121+
if (line) {
122+
let text = ByteArray.toString(line);
123+
callback(text);
124+
readStream(stream, callback);
125+
}
126+
});
127+
}
128+
129+
readStream(stdoutStream, (text) => { output += text + "\n"; });
130+
readStream(stderrStream, (text) => { errorOutput += text + "\n"; });
131+
132+
// Monitor the command's exit status
133+
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, (pid, status) => {
134+
if (GLib.spawn_check_exit_status(status)) {
135+
if (errorOutput.includes("Error executing command as another user: Request dismissed")) {
136+
reject(new Error("User cancelled the authentication."));
137+
} else {
138+
resolve();
139+
}
140+
} else {
141+
reject(new Error(`Command failed with status ${status}\n${errorOutput}`));
142+
}
143+
});
144+
110145
} catch (error) {
111146
console.error('Error running the command:', error);
112147
reject(error);
@@ -134,7 +169,7 @@ class LedControlMenu extends QuickSettings.QuickMenuToggle {
134169
* Opens a dialog window that allows the user to input text which will be converted to Morse code
135170
* and used to control the LED in a Morse code pattern.
136171
*
137-
* The user can input text and upon clicking "Aceptar", the text is converted into a set of shell
172+
* The user can input text and upon clicking "Accept", the text is converted into a set of shell
138173
* commands that control the LED's on/off state to blink in Morse code.
139174
*/
140175
_openMorseDialog() {
@@ -145,7 +180,7 @@ class LedControlMenu extends QuickSettings.QuickMenuToggle {
145180

146181
let contentLayout = dialog.contentLayout;
147182

148-
let label = new St.Label({ text: 'Enter the text to emit in Morse:' });
183+
let label = new St.Label({ text: 'Enter the text to emit in Morse (0-9a-z):' });
149184
contentLayout.add_child(label);
150185

151186
let entry = new St.Entry({ name: 'text-entry' });
@@ -161,6 +196,7 @@ class LedControlMenu extends QuickSettings.QuickMenuToggle {
161196
dialog.addButton({
162197
label: 'Accept',
163198
action: () => {
199+
dialog.close(global.get_current_time());
164200
const morseText = entry.get_text();
165201
log('Texto en Morse:', morseText);
166202

@@ -251,13 +287,29 @@ class LedControlMenu extends QuickSettings.QuickMenuToggle {
251287

252288
return new Promise((resolve, reject) => {
253289
try {
290+
this._updateCheckState(0);
291+
this.iconName = 'keyboard-brightness-medium-symbolic';
292+
this.menu.setHeader('keyboard-brightness-medium-symbolic', _('ThinkPad Red Led Control'), _(''));
293+
this._indicator.icon_name = 'keyboard-brightness-medium-symbolic';
294+
this.subtitle = 'Morsing... ';
295+
254296
this._runCommand([
255297
"pkexec",
256298
"bash",
257299
"-c",
258300
morseCommands
259-
]);
260-
resolve();
301+
]).then(() => {
302+
this._updateCheckState(1);
303+
this.iconName = 'keyboard-brightness-high-symbolic';
304+
this.menu.setHeader('keyboard-brightness-high-symbolic', _('ThinkPad Red Led Control'), _(''));
305+
this._indicator.icon_name = 'keyboard-brightness-high-symbolic';
306+
resolve();
307+
308+
}).catch((error) => {
309+
Main.notify(_('Error'), _('Could not run the command. Check your credentials.'));
310+
console.error('Error running the command:', error);
311+
});
312+
261313
} catch (error) {
262314
console.error('Error running the command:', error);
263315
reject(error);

0 commit comments

Comments
 (0)