gjs/gnome-shell-extension: Updating button text every 60 seconds - gnome

I want to write a simple gnome extension that prints some text on my top bar from a text file. I managed to print the text but i'm having trouble with updating it every 60 seconds. Is it even possible with gjs?
this is what i came up with:
const {St, Clutter} = imports.gi;
const Main = imports.ui.main;
const GLib = imports.gi.GLib;
let panelButton;
function init () {
// Create a Button with "Hello World" text
panelButton = new St.Bin({
style_class : "panel-button",
});
let fileContents = String(GLib.file_get_contents("path/to/myfile.txt")[1]);
let panelButtonText = new St.Label({
text : fileContents,
y_align: Clutter.ActorAlign.CENTER,
});
panelButton.set_child(panelButtonText);
}
function enable () {
// Add the button to the panel
Main.panel._centerBox.insert_child_at_index(panelButton, 2);
}
function disable () {
// Remove the added button from panel
Main.panel._centerBox.remove_child(panelButton);
}

You'll need to use GLib.timeout_add_seconds():
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 60, () => {
updateLabel(newText);
return GLib.SOURCE_CONTINUE;
});
By the way, you should probably use ByteArray.toString() to convert the Uint8Array that you get from the file into a string.

If you want to have your extension approved by ego you'd possibly want to do it more like this (destroy in disable what you create in enable):
in extension.js:
let timeout = null;
in function enable():
timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, (60 * 1000), () => {
// ...do the right thing...
return GLib.SOURCE_CONTINUE;
});
in function disable():
if (timeout) {
GLib.Source.remove(timeout);
timeout = null;
}

Related

How do you get the current terminal cursor position?

I can determine the terminal window size with process.stdout.columns, process.stdout.rows, and can recalculate my positioning with the resize event.
I am struggling to find out how to get my current cursor position for doing something like updating a specific location on my terminal, then returning to the previous location.
Does Node offer something like process.stdeout.x, process.stdeout.y to tell me where I currently am?
I realise I there are some Linux specific work arounds, but is there something offered by Node that allows for this functionality in a cross platform way?
The position returned by readline is relative to the current cursor location, not absolute on the screen.
I was able to create a promise to get this information using the answer below containing the escape sequence for requesting terminal cursor position:
How can I get position of cursor in terminal?
const getCursorPos = () => new Promise((resolve) => {
const termcodes = { cursorGetPosition: '\u001b[6n' };
process.stdin.setEncoding('utf8');
process.stdin.setRawMode(true);
const readfx = function () {
const buf = process.stdin.read();
const str = JSON.stringify(buf); // "\u001b[9;1R"
const regex = /\[(.*)/g;
const xy = regex.exec(str)[0].replace(/\[|R"/g, '').split(';');
const pos = { rows: xy[0], cols: xy[1] };
process.stdin.setRawMode(false);
resolve(pos);
}
process.stdin.once('readable', readfx);
process.stdout.write(termcodes.cursorGetPosition);
})
const AppMain = async function () {
process.stdout.write('HELLO');
const pos = await getCursorPos();
process.stdout.write("\n");
console.log({ pos });
}
AppMain();

chrome.devtools.inspectedWindow.eval - frameURL

I am trying to get the selected element to the sidebar pane in my chrome extension.
It's working fine if the page has no frames when the element is in the frame, it's not working.
As per the document I have to pass the frameURL, but how do I get the frame or Iframe URL?
Thank you.
Note: This issue is duplicate that was opened in 3 years ago, but still no solution there, so re-opening it again.
In devtools.js
chrome.devtools.panels.elements.createSidebarPane(name, (panel) => {
// listen for the elements changes
function updatePanel() {
chrome.devtools.inspectedWindow.eval("parseDOM($0)", {
frameURL: // how to pass dynamic
useContentScriptContext: true
}, (result, exceptipon) => {
if (result) {
console.log(result)
}
if (exceptipon) {
console.log(exceptipon)
}
});
}
chrome.devtools.panels.elements.onSelectionChanged.addListener(updatePanel);
});
I ran into this as well. I ended up needing to add a content_script on each page/iframe and a background page to help pass messages between devtools and content scripts.
The key bit is that in the devtools page, we should ask the content_scripts to send back what their current url is. For every content script that was registered, we can then call chrome.devtools.inspectedWindow.eval("setSelectedElement($0)", { useContentScriptContext: true, frameURL: msg.iframe } );
Or in full:
chrome.devtools.panels.elements.createSidebarPane( "example", function( sidebar ) {
const port = chrome.extension.connect({ name: "example-name" });
// announce to content scripts that they should message back with their frame urls
port.postMessage( 'SIDEBAR_INIT' );
port.onMessage.addListener(function ( msg) {
if ( msg.iframe ) {
// register with the correct frame url
chrome.devtools.panels.elements.onSelectionChanged.addListener(
() => {
chrome.devtools.inspectedWindow.eval("setSelectedElement($0)", { useContentScriptContext: true, frameURL: msg.iframe } );
}
);
} else {
// otherwise assume other messages from content scripts should update the sidebar
sidebar.setObject( msg );
}
} );
}
);
Then in the content_script, we should only process the event if we notice that the last selected element ($0) is different, since each frame on the page will also handle this.
let lastElement;
function setSelectedElement( element ) {
// if the selected element is the same, let handlers in other iframe contexts handle it instead.
if ( element !== lastElement ) {
lastElement = element;
// Pass back the object we'd like to set on the sidebar
chrome.extension.sendMessage( nextSidebarObject( element ) );
}
}
There's a bit of setup, including manifest changes, so see this PR for a full example:
https://github.com/gwwar/z-context/pull/21
You can found url of the frame this way:
document.querySelectorAll('iframe')[0].src
Assuming there is at lease one iframe.
Please note, you cannot use useContentScriptContext: true, as it will make the script execute as a context page (per documentation) and it will be in a separate sandboxed environment.
I had a slightly different problem, but it might be helpful for your case too, I was dynamically inserting an iframe to a page, and then tried to eval a script in it. Here the code that worked:
let win = chrome.devtools.inspectedWindow
let code = `
(function () {
let doc = window.document
let insertFrm = doc.createElement('IFRAME')
insertFrm.src = 'about:runner'
body.appendChild(insertFrm)
})()`
win.eval(code, function (result, error) {
if (error) {
console.log('Eror in insertFrame(), result:', result)
console.error(error)
} else {
let code = `
(function () {
let doc = window.document
let sc = doc.createElement('script')
sc.src = '${chrome.runtime.getURL('views/index.js')}'
doc.head.appendChild(sc)
})()`
win.eval(code, { frameURL: 'about:bela-runner' }, function (result, error) {
if (error) {
console.log('Eror in insertFrame(), result:', result)
console.error(error)
}
})
}
})

Create Multiple Jabber Client Echo Bot

Hai Stackoverflowers,
i had try so many ways to fix my problem and i end up with stack over flow
Programming Language:
Typescript
my main goals:
create Client Class as many as i want and can turn on and turn off the Client, the client will be as Bot that echo text from the friendlist
I use node-xmpp-client Library :
https://github.com/chris-rock/node-xmpp-client
My Code :
import * as Client from 'node-xmpp-client'; // Jabber Helper
export class TerminalJabber {
private ter;
private client;
constructor(ter: any) {
this.ter = ter;
this.client = new Client({
jid: this.ter.settings.username,
password: this.ter.settings.password,
});
}
public turnOn(){
// Turn on Jabber
this.client.on('online', () => {
console.log('online');
const stanza = new Client.Element('presence', { })
.c('show').t('chat').up()
.c('status').t('Happily echoing your <message/> stanzas');
this.client.send(stanza);
});
this.client.on('stanza', (stanza) => {
// Message come here and will echo from here
});
// Catch error
this.client.on('error', (e) => {
console.log('------>', e);
});
}
// Turn Off Jabber
public turnOff () {
// Turn Off Jabber
this.client.end();
return true;
}
}
The Problem is, The Bot Can Start, but never will be disconnect when i call a new class and turnOn() and turnOff().
How I call My code:
// Hit URL
/turnOn/:terId
// Hit URL
app.router.get(/turnOn/:id, turnOnController); // for turn On
app.router.get(/turnOff/:id, turnOfController); // for turn Off
TurnOnController(){
// Get data from database using id
const data = Query ~ ;
const terminal = new TerminalId(data)
terminal.turnOn();
}
TurnOffController(){
// Get data from database using id
const data = Query ~ ;
const terminal = new TerminalID(data)
terminal.turnOff();
}
Maybe you have a solution of my problems ?
have been thinking and read so many post about xmpp, but i cant solve this things,
Please help
sincerely,
Thank you
You need to call the turnOff method on the terminal instance before you create new one.
This also means you need to have access to the instance from both controllers.
var terminals = {};
function turnOnController(){
var id = getIdSomehow();
terminal[id] = new TerminalId(data);
terminal[id].turnOn();
}
function turnOffController(){
var id = getIdSomehow();
if (terminal[id])
terminal[id].turnOff();
}

New Window Positioning in Electron

I need to know how to open new windows which is offset a little by the current window position (first window will be opened in the center)
My codes are as follows:
// index.js
const {app,BrowserWindow,Menu,MenuItem,dialog}=require('electron');
function window_open(path){
win = new BrowserWindow({show: false})
win.loadURL(path);
win.once('ready-to-show', () => {win.show()})
}
let win
app.on('ready',event=>{
'use strict';
window_open(`file://${__dirname}/index.html`)
});
This opens the initial window in the center. I am also passing this function in the new window command (cmd+n)
{
label: 'File',
submenu: [
{label: 'New Window', accelerator: 'CmdOrCtrl+N', click: () => (
window_open(`file://${__dirname}/index.html`))
},
The code works fine, except that every window is positioned the same, in the center. I would like each new windows to be offset a little.
What's the best way to achieve this?
I learned that I need these two things:
BrowserWindow.getFocusedWindow()
win.getPosition()
Combining with #pergy's response, I got the following code which finds the focused window if there is any and offsets a new window from its position, otherwise creates a new window in the center:
let win = null;
function window_open(path) {
const opts = { show: false };
if (BrowserWindow.getFocusedWindow()) {
current_win = BrowserWindow.getFocusedWindow();
const pos = current_win.getPosition();
Object.assign(opts, {
x: pos[0] + 22,
y: pos[1] + 22,
});
};
win = new BrowserWindow(opts);
win.loadURL(path);
win.once('ready-to-show', () => { win.show() });
};
app.once('ready', event => {
window_open(`file://${__dirname}/index.html`);
});
This does what I asked for in my original question, so I have decided to post this. However, I do feel that it is slow in spawning the new windows, so I won't mark this as an answer to see if there are faster approaches to this.
Update:
I have found out that waiting on 'ready-to-show' is what makes it slow, as it waits for the ready state. I have accepted this as the answer as I feel that the speed issue is dependent to the content and not the browser. Feel free to add comments on this as I am still open ears.
You can define the window's position in constructor option x and y. The currently active window's position can be retrieved with getPosition(), so you can define offset for the new window from that.
See this dummy app for example:
const { app, BrowserWindow } = require('electron')
let win = null
app.once('ready', () => {
const openWindow = () => {
const opts = {
show: false
}
if (win) {
const pos = win.getPosition()
Object.assign(opts, {
x: pos[0] + 10,
y: pos[1] + 10
})
}
win = new BrowserWindow(opts)
win.loadURL('http://google.com')
let thisWin = win
win.once('ready-to-show', () => {
thisWin.show()
})
}
setInterval(openWindow, 5000)
})

protractor timed out only when ignoreSynchronization is set to false

I couldn't figure out why protractor timed out when it reach to the code with ignoreSynchronization set to false;
this.countSubscribers = function () {
this.subscriberCount().then(function (count) {
totalSubscribers = count;
});
};
the method works correctly and totalSubscribers variable is getting correct value when
ignoreSynchronization = true;
Code below is a sample of my spec page:
describe("subscriber page test", function () {
"use strict";
var selectedCount = 10;
var subscriberCount;
describe("This test script selects no. of subscribers to be displayed on subscriber page", function () {
/**
* Step-1#Select number of items to be displayed
* Step-2#Get the count of subscribers displayed
* Step-3#check whether the number of subscribers displayed is equal to 10
*/
it("should select the no. of items to displayed and get the count", function () {
browser.ignoreSynchronization = false;
subscriber_page.selectItems(selectedCount);
subscriber_page.countSubscribers();
});
it("should check whether the selected no. of subscribers are displayed", function () {
expect(subscriber_page.getSubscribers()).toBe(10);
});
});
});
and the code below is a sample of my page object:
var SubscriberPage = function () {
"use strict";
this.subscriberCount = function() { return element...};
this.selectOption = function(count) {return element...};
var totalSubscribers;
/**
* This method selects items in list box
*/
this.selectItems = function (count) {
this.selectOption(count).click(); //selects the items from list box
};
/**
* This method sets the number of subscribers listed
*/
this.countSubscribers = function () {
this.subscriberCount().count().then(function (count) {
totalSubscribers = count;
});
};
/**
* This method returns the subscriber count
*/
this.getSubscribers = function () {
return totalSubscribers;//returns the count of subscribers.
};
};
module.exports = new SubscriberPage();
The code select items successfully, but it makes long pause afterward then produce
following error message:
Failed: Timed out waiting for Protractor to synchronize with the page after 5 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md
While waiting for element with locator - Locator: By(css selector, ...)
What I found out so far:
If I comment out following line from spec page countSubscribers method rather works correctly:
subscriber_page.selectItems(selectedCount);
or if I temporarily flip the ignoreSynchronization variable to true before countSubscribers method is executed, it works fine.
Can anyone explain what the protractor is doing at this point and what the best solution is while browser.ignoreSynchronization is set to false?
Keep a reference of "this" before your function. And then call it whenever you want.
Also if you do element.all(...) You'll be able to use the native .count() which will resolve the promise for you.
//Reference to this
var self = this;
//element.all with .count()
this.subscriberCount = function() { return element.all(...).Count()};
//This method returns the number of subscribers listed
this.countSubscribers = function () {
return self.subscriberCount();
});

Resources