Skip to content

Commit

Permalink
added cooperative event loop
Browse files Browse the repository at this point in the history
  • Loading branch information
Place1 committed Sep 18, 2017
1 parent c54a7d9 commit 22ac86b
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 21 deletions.
28 changes: 18 additions & 10 deletions demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ const app = new App({
height: 200
});

app.init().then((window) => {
const button = new Button({
name: 'Button 1'
});
button.onClick = () => {
console.log('Button was clicked');
};
button.attach(window);
app.render();
});
app.init()
.then((window) => {
const button = new Button({
name: 'Button 1'
});
button.onClick = () => {
console.log('Button was clicked');
};
button.attach(window);
console.log('creating a set timeout');
setTimeout(() => console.log('this will run after the application window closes'), 1000);
app.render();
})
.catch((error) => {
console.log('error');
})

console.log('I will run after the application window is closed!');
18 changes: 7 additions & 11 deletions src/app/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ffi from 'ffi';
import path from 'path';
import ref from 'ref';
import loop from './loop';
import { Window } from '../';

const type = {
Expand All @@ -23,22 +24,17 @@ export default class App {
this.width = width || 200;
this.height = height || 200;
this.window = null;
app.register_on_activate(ffi.Callback('void', [type.GtkWidgetPtr], (window) => {
this.window = new Window({ pointer: window });
this.onActivate(this.window);
}));
this.pointer = app.create(this.title, this.namespace, this.width, this.height);
}

init() {
loop.startLoop();
return new Promise((resolve, reject) => {
this.onActivate = resolve;
app.init.async(this.pointer, (err, status) => {
if (status !== 0) {
console.error(new Error('Program initialized with error'));
}
process.exit(status);
});
app.register_on_activate(ffi.Callback('void', [type.GtkWidgetPtr], (windowPtr) => {
this.window = new Window({ pointer: windowPtr });
resolve(this.window);
}));
app.init(this.pointer);
});
}

Expand Down
84 changes: 84 additions & 0 deletions src/app/loop/index.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (c) 2015-2016 Jasper St. Pierre, Andrea Giammarchi & Contributors
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <glib.h>
#include <uv.h>
#include <stdbool.h>
#include <stdio.h>
#include "index.h"

struct uv_loop_source {
GSource source;
uv_loop_t *loop;
};

static gboolean uv_loop_source_prepare (GSource *base, int *timeout) {
struct uv_loop_source *source = (struct uv_loop_source *) base;
uv_update_time (source->loop);

bool loop_alive = uv_loop_alive (source->loop);

/* If the loop is dead, we can simply sleep forever until a GTK+ source
* (presumably) wakes us back up again. */
if (!loop_alive) {
return FALSE;
}

/* Otherwise, check the timeout. If the timeout is 0, that means we're
* ready to go. Otherwise, keep sleeping until the timeout happens again. */
int t = uv_backend_timeout (source->loop);
*timeout = t;

if (t == 0)
return TRUE;
else
return FALSE;
}

static gboolean uv_loop_source_dispatch (GSource *base, GSourceFunc callback, gpointer user_data) {
struct uv_loop_source *source = (struct uv_loop_source *) base;
uv_run (source->loop, UV_RUN_NOWAIT);
return G_SOURCE_CONTINUE;
}

static GSourceFuncs uv_loop_source_funcs = {
uv_loop_source_prepare,
NULL,
uv_loop_source_dispatch,
NULL,
NULL,
NULL,
};

static GSource *uv_loop_source_new (uv_loop_t *loop) {
struct uv_loop_source *source = (struct uv_loop_source *) g_source_new (&uv_loop_source_funcs, sizeof (*source));
source->loop = loop;
g_source_add_unix_fd (&source->source,
uv_backend_fd (loop),
(GIOCondition) (G_IO_IN | G_IO_OUT | G_IO_ERR));
return &source->source;
}

void startLoop() {
GSource *source = uv_loop_source_new (uv_default_loop ());
g_source_attach (source, NULL);
}
6 changes: 6 additions & 0 deletions src/app/loop/index.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef GTK_LOOP_HEADER
#define GTK_LOOP_HEADER

void startLoop();

#endif
21 changes: 21 additions & 0 deletions src/app/loop/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ffi from 'ffi';
import path from 'path';
import ref from 'ref';

/**
* GTK runs applications in a GTK event loop.
* This is problematic in a NodeJS environment as
* a GTK application will block the calling
* NodeJS function.
* To solve this, we can nest the NodeJS LibUV event
* loop in the GTK event loop so that NodeJS events will
* continue to tick along concurrently with GTK events.
* @credit To WebReflection's prior work on NodeGTK
* bindings that solved this issue.
* @credit https://github.com/WebReflection/node-gtk/blob/master/src/loop.cc
*/
const loop = ffi.Library(path.resolve(__dirname, 'index'), {
startLoop: ['void', []],
});

export default loop;

0 comments on commit 22ac86b

Please sign in to comment.