~gnome-shell-extensions/gnome-shell-extensions/appindicator-support-head

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */

const Lang = imports.lang;
const St = imports.gi.St;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Clutter = imports.gi.Clutter;
const Cogl = imports.gi.Cogl;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const byteArray = imports.byteArray;

/*
 * UtilMixin:
 * Mixes in the given properties in _mixin into the object
 *
 */
const Mixin = new Lang.Class({
    Name: 'UtilMixin',
    
    _init: function() {
        this._lateMixin = {};
    },
    
    _mixin: {},
    
    _conserve: [],
    
    attach: function(o) {
        if (!this._mixin) return;
        if (this._conserve && this._conserve.forEach) {
            o._conserved = {};
            this._conserve.forEach(function(e) {
                    if (e in o) {
                        o._conserved[e] = o[e];
                    } else if (o.prototype && e in o.prototype) {
                        o._conserved[e] = o.prototype[e];
                    } else {
                        log("WARNING: attempted to conserve property '"+e+"' but not found.");
                    }
            });
        }
        for (var i in this._mixin) {
            o[i] = this._mixin[i];
        }
        for (var i in this._lateMixin) {
            o[i] = this._lateMixin[i]
        }
        if (this._mixinInit) {
            this._mixinInit.apply(o, Array.prototype.slice.call(arguments, 1));
        }
    }
});

/*
 * AsyncTaskQueue:
 * Schedules asynchrouns tasks which may not overlap during execution
 *
 * The scheduled functions are required to take a callback as their last arguments, and all other arguments
 * need to be bound using Function.prototype.bind
 */
const AsyncTaskQueue = new Lang.Class({
    Name: 'AsyncTaskQueue',
    
    _init: function() {
        this._taskList = [];
    },
    
    // shedule the async task for execution or execute right away if there's no current task
    add: function(task, callback, context) {
        this._taskList.push({task: task, callback: callback, context: context});
        if (this._taskList.length == 1) this._executeNext();
    },
    
    _executeNext: function() {
        this._taskList[0].task.call(null, (function() {
            if (this._taskList[0].callback) this._taskList[0].callback.apply(this._taskList[0].context, arguments);
            this._taskList.shift();
            if (this._taskList.length) this._executeNext();
        }).bind(this));
    }
});

const createActorFromPixmap = function(pixmap, icon_size) {
    if (!(pixmap && pixmap.length)) return null;
    // pixmap is actually an array of icons, so that hosts can pick the
    // best size (here considered as the area covered by the icon)
    // XXX: should we use sum of width and height instead? or consider
    // only one dimension?
    let best = 0;
    let bestHeight = pixmap[0][1];
    let goal = icon_size;
    for (let i = 1; i < pixmap.length; i++) {
        let height = pixmap[i][1];
        if (Math.abs(goal - height) < Math.abs(goal - bestHeight)) {
            best = i;
            bestHeight = height;
        }
    }
    let [width, height, imageData] = pixmap[best];
    // each image is ARGB32
    // XXX: we're not getting a rowstride! let's hope images are compressed enough
    let rowstride = width * 4;
    return St.TextureCache.get_default().load_from_raw(imageData, imageData.length,
                                                       true, width, height, rowstride,
                                                       icon_size);
};

//data: GBytes
const createActorFromMemoryImage = function(data) {
    var stream = Gio.MemoryInputStream.new_from_bytes(data);
    var pixbuf = GdkPixbuf.Pixbuf.new_from_stream(stream, null);
    return new St.Icon({ gicon: pixbuf, icon_size: pixbuf.get_width() });
}

//HACK: GLib.Variant.prototype.get_data_as_bytes only exists in recent gjs versions
const variantToGBytes = function(variant) {
    if (typeof(GLib.Variant.prototype.get_data_as_bytes) != "undefined") {
        return variant.get_data_as_bytes();
    } else {
        //FIXME: this is very very inefficient. we're sorry.
        var data = variant.deep_unpack(); //will create an array of doubles...
        var data_length = data.length;
        var array = new imports.byteArray.ByteArray(data_length);
        for (var i = 0; i < data_length; i++) {
            array[i] = data[i];
        }
        return GLib.ByteArray.free_to_bytes(array); //this can't be correct but it suprisingly works like a charm.
    }
}