piano/App.js

225 lines
5.4 KiB
JavaScript

/*jshint node:true, nomen:true */
'use strict';
var app = require('app');
var BrowserWindow = require('browser-window');
var ipc = require('ipc');
var net = require('net');
var util = require('util');
var events = require('events');
var path = require('path');
var os = require('os');
var fs = require('fs');
/**
* App
*
* Controls the app flow
*/
function App() {
this.windows = [];
this.ready = false;
this.visible = true;
app.on('ready', function () {
this.ready = true;
}.bind(this));
app.on('window-all-closed', function () {
app.quit();
});
events.EventEmitter.call(this);
}
util.inherits(App, events.EventEmitter);
module.exports = App;
// create pipe on Windows and socks on Linux-based environments
App.socket = (process.platform === 'win32') ?
'\\\\.\\pipe\\piano-sock' : path.join(os.tmpdir(), 'piano.sock');
/**
* Add a new window
*
* Open a window and add it to the
* window stack. Returns window instance.
*
* @param {options: Object} Window options (see atom-shell docs)
*/
App.prototype.addWindow = function (options) {
var window = new BrowserWindow(options),
index = this.windows.length;
window.on('closed', function () {
this.windows.splice(index, 1);
}.bind(this));
this.windows.push(window);
return window;
};
/**
* Fetch options
*
* Gather options in a litteral from
* argv parameters.
*/
App.prototype.fetchOptions = function () {
var argv, i, arg, options = {};
argv = process.argv.slice(1);
for (i in argv) {
if (argv.hasOwnProperty(i)) {
arg = argv[i];
if (arg[0] !== '-') {
options.file = arg;
}
}
}
this.emit('options', options);
return options;
};
/**
* Chain options
*
* Pass current options to the instance that
* is already opened, if there is one.
* Otherwise, code in callback is executed,
* and server is launched.
*
* @param {callback: function} Function to call if this is the first instance
*/
App.prototype.chainOptions = function (callback) {
var client;
client = net.connect({
path: App.socket
}, function () {
// if the connection is established,
// an instance is already running.
// Pass on parameters and close app.
client.write(
JSON.stringify(this.fetchOptions()),
function () {
client.end();
app.terminate();
}
);
}.bind(this));
client.on('error', function (err) {
// if an error occurred, that means no
// server was created yet: this is the
// first instance
this.startServer();
if (this.ready) {
callback();
} else {
app.on('ready', callback);
}
}.bind(this));
};
/**
* Start app server
*
* Start a server on App.socket path, waiting
* for new instances to pass their options.
*/
App.prototype.startServer = function () {
var server;
if (process.platform !== 'win32') {
// try to unlink older socket if it exists, if it doesn't,
// ignore ENOENT errors
try {
fs.unlinkSync(App.socket);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
}
server = net.createServer(function (connection) {
connection.on('data', function (data) {
this.windows[0].focus();
this.emit('options', JSON.parse(data));
}.bind(this));
}.bind(this));
server.listen(App.socket);
};
/**
* Add a switch
*
* @param {switch: string} Switch name
* @param {value: mixed} Switch value
*/
App.prototype.addSwitch = function (name, value) {
app.commandLine.appendSwitch(name, value);
};
/**
* Start the app
*
* Check if another instance is already opened,
* if so, transmit options and close instantly.
* Otherwise, open main window.
*/
App.prototype.start = function () {
this.chainOptions(function () {
// enable MIDI support
this.addSwitch('enable-web-midi');
// create main window
var window = this.addWindow({
title: 'Piano',
icon: __dirname + '/images/logos/logo32.png',
'min-width': 750,
'min-height': 400,
width: 937,
height: 500,
show: false
});
window.loadUrl('file://' + __dirname + '/index.html');
// FIXME: atom-shell currently doesn't have a
// minimize/restore event, so we poll
//
// https://github.com/atom/atom-shell/issues/73
setInterval(function () {
if (this.visible && window.isMinimized()) {
this.visible = false;
window.webContents.send('visible', false);
}
if (!this.visible && !window.isMinimized()) {
this.visible = true;
window.webContents.send('visible', true);
}
}.bind(this), 500);
// start sending options
this.on('options', function (options) {
window.webContents.send('options', options);
});
ipc.on('ready', function () {
window.show();
window.focus();
window.webContents.send('visible', true);
this.fetchOptions();
}.bind(this));
}.bind(this));
};