Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle files passed as arguments on the command line in a Gjs Gtk.Application

I am trying to write a GNOME GTK3 application using Gjs which handles files passed as command line arguments. In order to do so I connect the open signal of the Gtk.Application and set the Gio.ApplicationFlags.HANDLES_OPEN flag:

#!/usr/bin/gjs

const Gio = imports.gi.Gio
const Gtk = imports.gi.Gtk
const Lang = imports.lang

const MyApplication = new Lang.Class({
  Name: 'MyApplication',

  _init: function() {
    this.application = new Gtk.Application({
      application_id: 'com.example.my-application',
      flags: Gio.ApplicationFlags.HANDLES_OPEN
    })

    this.application.connect('startup', this._onStartup.bind(this))
    this.application.connect('open', this._onOpen.bind(this))
    this.application.connect('activate', this._onActivate.bind(this))
  },

  _onStartup: function() {
    log('starting application')
  },

  _onOpen: function(application, files) {
    log('opening ' + files.length + ' files')
    this._onStartup()
  },

  _onActivate: function() {
    log('activating application')
  }
})

let app = new MyApplication()
app.application.run(ARGV)

When I run the program with a file argument I expect _onOpen to be called with the GFile passed in. But instead _onActivate is called, like if I'd run it without any file arguments:

$ ./open-files.js open-files.js 
Gjs-Message: JS LOG: starting application
Gjs-Message: JS LOG: activating application

I am running [email protected].

like image 509
Johannes J. Schmidt Avatar asked Sep 11 '25 09:09

Johannes J. Schmidt


1 Answers

There's a discrepancy in how GJS's ARGV is defined, relative to the conventions in other languages. In, for example, C, argv[0] is the name of the program, and the first argument starts at argv[1]. In GJS, the name of the program is System.programInvocationName, and the first argument is ARGV[0].

Unfortunately, being part of a C library, Gtk.Application expects you to pass in arguments according to the C convention. You can do this:

ARGV.unshift(System.programInvocationName);

What was happening was that ./open-files.js open-files.js was putting ['open-files.js'] as ARGV, which Gtk.Application was interpreting as the name of the program, with no other arguments. If you'd run the program with two file arguments, you would have seen that it "opened" only the second file.

PS. Unfortunately there seems to be a bug in GJS 1.44 that prevents the open signal from working correctly. For now, I'd suggest working around the problem by subclassing Gtk.Application instead of proxying it. Your program would look like this:

const Gio = imports.gi.Gio
const Gtk = imports.gi.Gtk
const Lang = imports.lang
const System = imports.system

const MyApplication = new Lang.Class({
  Name: 'MyApplication',
  Extends: Gtk.Application,

  _init: function(props={}) {
    this.parent(props)
  },

  vfunc_startup: function() {
    log('starting application')
    this.parent()
  },

  vfunc_open: function(files, hint) {
    log('opening ' + files.length + ' files')
  },

  vfunc_activate: function() {
    log('activating application')
    this.parent()
  }
})

let app = new MyApplication({
  application_id: 'com.example.my-application',
  flags: Gio.ApplicationFlags.HANDLES_OPEN
})
ARGV.unshift(System.programInvocationName)
app.run(ARGV)
like image 78
ptomato Avatar answered Sep 13 '25 23:09

ptomato