I'm converting these topis to jsctypes for X11:
How do I bring a processes window to the foreground on X Windows? (C++)
How to get an X11 Window from a Process ID?
It's working OK. I'm just running into one problem, when I loop the code its crashing.
For testing Im only looping once yet it still crashes:
The problem code is here:
searchForPidStartingAtWindow(wChildElementCasted, _disp, targetPid, true);
as soon as i run that code it crashes. even tho it returns on second iteration of this function 4argument is true
Full code, can copy and paste into Scratchpad (Borwser>Environment) and run.
Cu.import('resource://gre/modules/ctypes.jsm');
function doit() {
try {
_x11 = ctypes.open('libX11.so.6');
} catch (e) {
try {
var libName = ctypes.libraryName('X11');
} catch (e) {
_x11 = false;
console.error('Integration: Could not get libX11 name; not activating');
return;
}
try {
_x11 = ctypes.open(libName);
} catch (e) {
_x11 = false;
console.error('Integration: Could not open ' + libName + '; not activating');
return;
}
}
//start - type constants
X11Atom = ctypes.unsigned_long;
X11Bool = ctypes.int;
X11Display = new ctypes.StructType('Display');
X11Window = ctypes.unsigned_long;
X11Status = ctypes.int;
//end - type constants
//start - constants
var XA_CARDINAL = 6; //https://github.com/foudfou/FireTray/blob/d0c49867ea7cb815647bf13f2f1edb26439506ff/src/modules/ctypes/linux/x11.jsm#L117
var None = 0; //https://github.com/foudfou/FireTray/blob/d0c49867ea7cb815647bf13f2f1edb26439506ff/src/modules/ctypes/linux/x11.jsm#L63
var Success = 0;
//end - constants
/*
* typedef struct {
* int type;
* unsigned long serial; / * # of last request processed by server * /
* Bool send_event; / * true if this came from a SendEvent request * /
* Display *display; / * Display the event was read from * /
* Window window;
* Atom message_type;
* int format;
* union {
* char b[20];
* short s[10];
* long l[5];
* } data;
* } XClientMessageEvent;
*/
XClientMessageEvent = new ctypes.StructType('XClientMessageEvent', [
{'type': ctypes.int},
{'serial': ctypes.unsigned_long},
{'send_event': X11Bool},
{'display': X11Display.ptr},
{'window': X11Window},
{'message_type': X11Atom},
{'format': ctypes.int},
{'l0': ctypes.long},
{'l1': ctypes.long},
{'l2': ctypes.long},
{'l3': ctypes.long},
{'l4': ctypes.long}
]);
/*
* Status XFetchName(
* Display* display,
* Window w,
* char** window_name_return
* );
*/
XFetchName = _x11.declare('XFetchName', ctypes.default_abi, X11Status,
X11Display.ptr, X11Window, ctypes.char.ptr.ptr);
/*
* Status XQueryTree(
* Display* display,
* Window w,
* Window* root_return,
* Window* parent_return,
* Window** children_return,
* unsigned int* nchildren_return
* );
*/
XQueryTree = _x11.declare('XQueryTree', ctypes.default_abi, X11Status,
X11Display.ptr, X11Window, X11Window.ptr, X11Window.ptr, X11Window.ptr.ptr,
ctypes.unsigned_int.ptr);
/*
* int XFree(
* void* data
* );
*/
XFree = _x11.declare('XFree', ctypes.default_abi, ctypes.int, ctypes.voidptr_t);
/*
* Display *XOpenDisplay(
* _Xconst char* display_name
* );
*/
XOpenDisplay = _x11.declare('XOpenDisplay', ctypes.default_abi, X11Display.ptr,
ctypes.char.ptr);
/*
* int XCloseDisplay(
* Display* display
* );
*/
XCloseDisplay = _x11.declare('XCloseDisplay', ctypes.default_abi, ctypes.int,
X11Display.ptr);
/*
* int XFlush(
* Display* display
* );
*/
XFlush = _x11.declare('XFlush', ctypes.default_abi, ctypes.int, X11Display.ptr);
/*
* Window XDefaultRootWindow(
* Display* display
* );
*/
XDefaultRootWindow = _x11.declare('XDefaultRootWindow', ctypes.default_abi,
X11Window, X11Display.ptr);
/*
* Atom XInternAtom(
* Display* display,
* _Xconst char* atom_name,
* Bool only_if_exists
* );
*/
XInternAtom = _x11.declare('XInternAtom', ctypes.default_abi, X11Atom,
X11Display.ptr, ctypes.char.ptr, X11Bool);
/*
* Status XSendEvent(
* Display* display,
* Window w,
* Bool propagate,
* long event_mask,
* XEvent* event_send
* );
*/
XSendEvent = _x11.declare('XSendEvent', ctypes.default_abi, X11Status,
X11Display.ptr, X11Window, X11Bool, ctypes.long, XClientMessageEvent.ptr);
/*
* int XMapRaised(
* Display* display,
* Window w
* );
*/
XMapRaised = _x11.declare('XMapRaised', ctypes.default_abi, ctypes.int,
X11Display.ptr, X11Window);
/*
* extern int XGetWindowProperty(
* Display* display,
* Window w,
* Atom property,
* long long_offset,
* long long_length,
* Bool delete,
* Atom req_type,
* Atom* actual_type_return,
* int* actual_format_return,
* unsigned long* nitems_return,
* unsigned long* bytes_after_return,
* unsigned char** prop_return
* );
*/
XGetWindowProperty = _x11.declare('XGetWindowProperty', ctypes.default_abi,
ctypes.int, X11Display.ptr, X11Window, X11Atom, ctypes.long, ctypes.long,
X11Bool, X11Atom, X11Atom.ptr, ctypes.int.ptr, ctypes.unsigned_long.ptr,
ctypes.unsigned_long.ptr, ctypes.char.ptr.ptr);
////////////////////////
////END DECLARATIONS
////////////////////////
var _x11Display = XOpenDisplay(null);
if (!_x11Display) {
console.error('Integration: Could not open display; not activating');
_x11 = false;
return;
}
var _x11RootWindow = XDefaultRootWindow(_x11Display);
if (!_x11RootWindow) {
console.error('Integration: Could not get root window; not activating');
_x11 = false;
return;
}
//start - WindowsMatchingPid from https://stackoverflow.com/questions/151407/how-to-get-an-x11-window-from-a-process-id
//start - searchForPidStartingAtWindow func
var _atomPIDInited = false;
var _atomPID;
var _matchingWins = [];
function searchForPidStartingAtWindow(w, _disp, targetPid, isRecurse) { // when you call must always leave isRecurse null or false, its only used by the function to identify when to clear out _matchingWins
if (!isRecurse) {
//user just called this function so clear _matchingWins
_matchingWins = [];
} else {
console.log('isRecurse so return');
}
console.log('h1');
//make sure to clear _matchingWins arr before running this
if (!_atomPIDInited) {
_atomPID = XInternAtom(_disp, '_NET_WM_PID', true);
console.log('_atomPID:', _atomPID, _atomPID.toString(), parseInt(_atomPID));
if(_atomPID == None) {
throw new Error('No such atom ("_NET_WM_PID"), _atomPID:', _atomPID);
}
_atomPIDInited = true;
}
var returnType = new X11Atom(),
returnFormat = new ctypes.int(),
nItemsReturned = new ctypes.unsigned_long(),
nBytesAfterReturn = new ctypes.unsigned_long(),
propData = new ctypes.char.ptr();
console.log('h2');
console.log('_disp:', _disp, 'w:', w, '_atomPID:', _atomPID);
var rez = XGetWindowProperty(_disp, w, _atomPID, 0, 1024, false, XA_CARDINAL, returnType.address(), returnFormat.address(), nItemsReturned.address(), nBytesAfterReturn.address(), propData.address());
console.log('h3');
console.log('XGetWindowProperty', 'rez:', rez, 'returnType:', returnType, 'nItemsReturned:', nItemsReturned, 'nBytesAfterReturn:', nBytesAfterReturn, 'propData:', propData);
console.log('propData:', ctypes.cast(propData, ctypes.unsigned_int).value);
if (rez == Success) {
var nElements = ctypes.cast(nItemsReturned, ctypes.unsigned_int).value;
if(nElements) {
var rezArr = [propData, nElements];
console.log('rezArr:', rezArr);
} else {
console.log('no elements for rezArr, nElements:', nElements);
}
var nPid = ctypes.cast(propData, ctypes.unsigned_int).value;
if (nPid != 0) {
_matchingWins.push(w);
console.log('h4');
var rez = XFree(propData);
console.log('rez of XFree on propData:', rez);
} else {
console.log('no pid on this window');
}
} else {
console.error('failed on XGetWindowProperty, rez:', rez);
}
if (isRecurse) {
return;
}
console.log('recurse into');
// recurse into child windows
var wRoot = new X11Window();
var wParent = new X11Window();
var wChild = new X11Window.ptr();
var nChildren = new ctypes.unsigned_int();
var rez = XQueryTree(_disp, w, wRoot.address(), wParent.address(), wChild.address(), nChildren.address());
if(rez != 0) { //can probably test this against `None` instead of `0`
var nChildrenCasted = ctypes.cast(nChildren, ctypes.unsigned_int).value;
var wChildCasted = ctypes.cast(wChild, ctypes.ArrayType(X11Window, nChildrenCasted).ptr).contents; //console.log('wChildCasted:', wChildCasted);
//var wChildElementCasted = ctypes.cast(wChildCasted.addressOfElement(0), X11Window).value;
//console.log('wChildElementCasted:', wChildElementCasted, 'w:', w);
//for(var i=0; i<wChildCasted.length; i++) {
for(var i=0; i<1; i++) {
var wChildElementCasted = ctypes.cast(wChildCasted.addressOfElement(i), X11Window).value;
console.log('wChildElementCasted:', wChildElementCasted, 'w:', w);
searchForPidStartingAtWindow(wChildElementCasted, _disp, targetPid, true);
}
} else {
console.warn('this window has no children, rez:', rez);
}
return _matchingWins;
}
//end - searchForPidStartingAtWindow func
var wins = searchForPidStartingAtWindow(_x11RootWindow, _x11Display, 12312132); //dont pass isRecurse here, important, otherwise if use this func multiple times, you'll have left over windows in the returned array from a previous run of this func
console.log('wins:', wins);
//end - WindowsMatchingPid
XCloseDisplay(_x11Display);
//_X11BringToForeground(win, intervalID);
}
doit();
Solved it, I had to use this code:
var wChildCasted = ctypes.cast(wChild, X11Window.array(nChildrenCasted).ptr).contents; //SAME AS: `var wChildCasted = ctypes.cast(wChild, ctypes.ArrayType(X11Window, nChildrenCasted).ptr).contents;`
for(var i=0; i<wChildCasted.length; i++) {
var wChildElementCasted = wChildCasted.addressOfElement(i).contents; //DO NOT DO `var wChildElementCasted = ctypes.cast(wChildCasted.addressOfElement(i), X11Window).value;`, it crashes on line 234 when passing as `w` into `XGetWindowProperty`
//console.log('wChildElementCasted:', wChildElementCasted, 'w:', w);
searchForPidStartingAtWindow(wChildElementCasted, _disp, targetPid, true);
}
notice how the wChildElementCasted is not longer casted, I simply read the .contents. this is the right way to do it for sure.
copy paste and fully working here: https://gist.github.com/Noitidart/5a24e8a4f8886ce7bbf6
Related
Hi i m new to xamarin forms and i have an app that allows you to take a picture through camera and then i have given a rectangular box for cropping the captured image...i have written code for xamarin android and all this works fine and i can finally save the image as JPG or PNG as well.
But i want to save the final image as a .bmp file.
private void SaveOutput(Bitmap croppedImage)
{
if (saveUri != null)
{
try
{
using var outputStream = ContentResolver.OpenOutputStream(saveUri);
if (outputStream != null)
{
croppedImage.Compress(Bitmap.CompressFormat.Jpeg, 75, outputStream);
}
}
In this function, the croppedImage bitmap is getting compressed to jpg.
Instead of that i want to save that bitmap as a .bmp file.i have looked alot, but havent found any solution yet.
Can someone please suggest what i can do.
Here is a similar issue with native Android . You could convert the solution to C# like following
public class AndroidBmpUtil
{
private readonly int BMP_WIDTH_OF_TIMES = 4;
private readonly int BYTE_PER_PIXEL = 3;
/**
* Android Bitmap Object to Window's v3 24bit Bmp Format File
* #param orgBitmap
* #param filePath
* #return file saved result
*/
public bool Save(Bitmap orgBitmap, string filePath)
{
if (orgBitmap == null)
{
return false;
}
if (filePath == null)
{
return false;
}
var isSaveSuccess = true;
//image size
var width = orgBitmap.Width;
var height = orgBitmap.Height;
//image dummy data size
//reason : bmp file's width equals 4's multiple
var dummySize = 0;
byte[] dummyBytesPerRow = null;
var hasDummy = false;
if (isBmpWidth4Times(width))
{
hasDummy = true;
dummySize = BMP_WIDTH_OF_TIMES - width%BMP_WIDTH_OF_TIMES;
dummyBytesPerRow = new byte[dummySize*BYTE_PER_PIXEL];
for (var i = 0; i < dummyBytesPerRow.Length; i++)
{
dummyBytesPerRow[i] = 0xFF;
}
}
var pixels = new int[width*height];
var imageSize = pixels.Length*BYTE_PER_PIXEL + height*dummySize*BYTE_PER_PIXEL;
var imageDataOffset = 0x36;
var fileSize = imageSize + imageDataOffset;
//Android Bitmap Image Data
orgBitmap.GetPixels(pixels, 0, width, 0, 0, width, height);
//ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
var buffer = ByteBuffer.Allocate(fileSize);
try
{
/**
* BITMAP FILE HEADER Write Start
**/
buffer.Put(0x42);
buffer.Put(0x4D);
//size
buffer.Put(WriteInt(fileSize));
//reserved
buffer.Put(WriteShort(0));
buffer.Put(WriteShort(0));
//image data start offset
buffer.Put(WriteInt(imageDataOffset));
/** BITMAP FILE HEADER Write End */
//*******************************************
/** BITMAP INFO HEADER Write Start */
//size
buffer.Put(WriteInt(0x28));
//width, height
buffer.Put(WriteInt(width));
buffer.Put(WriteInt(height));
//planes
buffer.Put(WriteShort(1));
//bit count
buffer.Put(WriteShort(24));
//bit compression
buffer.Put(WriteInt(0));
//image data size
buffer.Put(WriteInt(imageSize));
//horizontal resolution in pixels per meter
buffer.Put(WriteInt(0));
//vertical resolution in pixels per meter (unreliable)
buffer.Put(WriteInt(0));
buffer.Put(WriteInt(0));
buffer.Put(WriteInt(0));
/** BITMAP INFO HEADER Write End */
var row = height;
var col = width;
var startPosition = 0;
var endPosition = 0;
while (row > 0)
{
startPosition = (row - 1)*col;
endPosition = row*col;
for (var i = startPosition; i < endPosition; i++)
{
buffer.Put(Write24BitForPixcel(pixels[i]));
if (hasDummy)
{
if (isBitmapWidthLastPixcel(width, i))
{
buffer.Put(dummyBytesPerRow);
}
}
}
row--;
}
var fos = new FileOutputStream(filePath);
fos.Write(new byte[buffer.Remaining()]);
fos.Close();
}
catch (Exception e1)
{
isSaveSuccess = false;
}
finally
{
}
return isSaveSuccess;
}
/**
* Is last pixel in Android Bitmap width
* #param width
* #param i
* #return
*/
private bool isBitmapWidthLastPixcel(int width, int i)
{
return i > 0 && i%(width - 1) == 0;
}
/**
* BMP file is a multiples of 4?
* #param width
* #return
*/
private bool isBmpWidth4Times(int width)
{
return width%BMP_WIDTH_OF_TIMES > 0;
}
/**
* Write integer to little-endian
* #param value
* #return
* #throws IOException
*/
private byte[] WriteInt(int value)
{
var b = new byte[4];
b[0] = (byte) (value & 0x000000FF);
b[1] = (byte) ((value & 0x0000FF00) >> 8);
b[2] = (byte) ((value & 0x00FF0000) >> 16);
b[3] = (byte) ((value & 0xFF000000) >> 24);
return b;
}
/**
* Write integer pixel to little-endian byte array
* #param value
* #return
* #throws IOException
*/
private byte[] Write24BitForPixcel(int value)
{
var
b = new byte[3];
b[0] = (byte) (value & 0x000000FF);
b[1] = (byte) ((value & 0x0000FF00) >> 8);
b[2] = (byte) ((value & 0x00FF0000) >> 16);
return b;
}
/**
* Write short to little-endian byte array
* #param value
* #return
* #throws IOException
*/
private byte[] WriteShort(short value)
{
var
b = new byte[2];
b[0] = (byte) (value & 0x00FF);
b[1] = (byte) ((value & 0xFF00) >> 8);
return b;
}
}
I have a recursive descent parser here:
Parser.prototype.parse = function(str){
var val = this.visitExpression(str, this.grammar.root, this.grammar);
this.reset();
return val;
};
/**
* Parse expression.
*
* #param {String} str
* #param {Expression} exp
* #param {Grammar} grammar
*
* #api private
*/
Parser.prototype.visitExpression = function(str, exp, grammar){
var val;
this.expression = exp;
for (var i = 0, n = exp.rules.length; i < n; i++) {
val = this.visitRule(str, exp.rules[i], grammar, exp);
// blank string '' also counts
if (null != val) return val;
}
};
/**
* Parse rule.
*
* #param {String} str
* #param {Rule} rule
* #param {Grammar} grammar
*
* #api private
*/
Parser.prototype.visitRule = function(str, rule, grammar, exp){
var pos = this.pos;
var args = [];
var val;
this.rule = rule;
for (var i = 0, n = rule.symbols.length - 1; i < n; i++) {
val = this.visitSymbol(str, rule.symbols[i], grammar, exp);
if (null == val && rule.symbols[i].matchAndIgnore) {
args.push(val);
continue;
}
// if we didnt get a value, and we don't have a flag to skip if not match
if (null == val && !rule.symbols[i].notMatchAndIgnore) {
this.pos = pos; // reset
return;
}
args.push(val);
}
return rule.apply(this, args);
};
/**
* Parse symbol.
*
* #param {String} str
* #param {Symbol} symbol
* #param {Grammar} grammar
*
* #api private
*/
Parser.prototype.visitSymbol = function(str, symbol, grammar, parentExp){
this.symbol = symbol;
if (symbol.isExpression) {
return this.parseExpression(symbol, str, grammar, parentExp);
} else if (symbol.isString) {
return this.parseString(symbol, str);
} else if (symbol.isRegExp) {
return this.parseRegExp(symbol, str);
}
};
/**
* Parse expression.
*
* #param {Symbol} symbol
* #param {String} str
* #param {Object} parser
* #api private
*/
Parser.prototype.parseExpression = function(symbol, str, grammar, parentExp){
var prev = this.expression;
var exp = grammar.rules[symbol.expression];
if (!exp) throw error("Expression '%s' is undefined (referenced in '%s')", symbol.expression, parentExp.name);
if (symbol.many) {
var pos = this.pos;
var res = [];
var val;
while (val = this.visitExpression(str, exp, grammar)) {
res.push(val);
}
this.expression = prev;
if (symbol.onePlus && !res.length) {
this.pos = pos;
return;
}
return res;
} else if (symbol.optional) {
var res = this.visitExpression(str, exp, grammar) || '';
this.expression = prev;
return res;
} else {
var pos = this.pos;
var res = this.visitExpression(str, exp, grammar);
if (res && symbol.matchAndIgnore) {
this.pos = pos;
res = null;
}
this.expression = prev;
return res;
}
};
/**
* Parse string.
*
* #param {Symbol} symbol
* #param {String} str
* #api private
*/
Parser.prototype.parseString = function(symbol, str){
if (symbol.val === str.substr(this.pos, symbol.val.length)) {
this.pos += symbol.val.length;
return symbol.val;
}
};
/**
* Parse RegExp.
*
* #param {Symbol} symbol
* #param {String} str
* #api private
*/
Parser.prototype.parseRegExp = function(symbol, str){
if (symbol.many) {
var pos = this.pos;
var res = [];
while (symbol.pattern.test(str.charAt(this.pos))) {
res.push(str.charAt(this.pos));
this.pos++;
}
// reset if we want to match one or more but didn't find matches.
if (symbol.onePlus && !res.length) {
this.pos = pos;
return;
}
return res.join('');
} else if (symbol.pattern.test(str.charAt(this.pos))) {
return str.charAt(this.pos++);
} else if (symbol.optional) {
return '';
}
};
It's relatively simple as I could muster at the time. But I'm wondering nowadays if there is a way to simplify this and make it use a sort of iterator like you would see when implementing iteration. For example, with an iterator you can traverse a list, a tree, and a graph all the same way, without worrying about their unique oddities and implementation details.
for (var item of iterator) { ... }
Nice and simple. I would like to now apply this to parsers, but can't quite read this code:
class sequence_parser implements parser {
list parsers;
iterator parse(iterator input) {
foreach(p, parser) {
input = p.parse(input);
}
return input;
}
}
class choice_parser implements parser {
list parsers;
iterator parse(iterator input) {
foreach(p, parser) {
iterator output = p.parse(input);
if (output != null) return output;
}
return null;
}
}
class loop_parser implements parser {
parser other;
iterator parse(iterator input) {
input = other.parse(input);
return parse(input);
}
}
I can't imagine how it would handle backing up if it fails down a particular path, and stuff like that. Any ideas how to do this or if it's possible? What is the interface required basically, like the iterator example?
Whats the purpose of num in webSocket.sendTXT(num, "Connected"); or at any other place its used in the code, what function does it serve? Because it doesn't ever get defined as anything anywhere, but it is required to
be passed as a function argument for it to work.
In void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) it's set as a function parameter.
Links2004/arduinoWebSockets Library
/*
* WebSocketServer_LEDcontrol.ino
*
* Created on: 26.11.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#define LED_RED 15
#define LED_GREEN 12
#define LED_BLUE 13
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server = ESP8266WebServer(80);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
if(payload[0] == '#') {
// we get RGB data
// decode rgb data
uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
}
break;
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
USE_SERIAL.flush();
delay(1000);
}
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, 1);
digitalWrite(LED_GREEN, 1);
digitalWrite(LED_BLUE, 1);
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if(MDNS.begin("esp8266")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
});
server.begin();
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
digitalWrite(LED_RED, 0);
digitalWrite(LED_GREEN, 0);
digitalWrite(LED_BLUE, 0);
}
void loop() {
webSocket.loop();
server.handleClient();
}
Looking at the library's source code, it reveals that it's a client id, so you can differentiate between multiple clients, that are connected at the same time.
/*
* send text data to client
* #param num uint8_t client id
* #param payload uint8_t *
* #param length size_t
* #param headerToPayload bool (see sendFrame for more details)
* #return true if ok
*/
bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
if(length == 0) {
length = strlen((const char *) payload);
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
return sendFrame(client, WSop_text, payload, length, false, true, headerToPayload);
}
return false;
}
I need to set loader in assetmanager for Animation.class, i used Gifloader,that loads completly animation from GifDecoder(that creates animation from gif)
but get error:
https://i.stack.imgur.com/HkrWe.png
, error on build: https://i.stack.imgur.com/2Mpaz.png
setloader line:
manager.setLoader(Animation.class, new Gifloader(new InternalFileHandleResolver()))
Gifloader class:
package com.mygdx.testgame;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetLoaderParameters;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.Array;
public class Gifloader extends AsynchronousAssetLoader<Animation<TextureRegion>, Gifloader.GifloaderParameter> {
private com.badlogic.gdx.graphics.g2d.Animation<TextureRegion> animresult;
public Gifloader(FileHandleResolver resolver) {
super(resolver);
}
#Override
public void loadAsync(AssetManager manager, String fileName, FileHandle file, GifloaderParameter parameter) {
animresult = (com.holidaystudios.tools.GifDecoder.loadGIFAnimation(Animation.PlayMode.LOOP,file.read()));
}
#Override
public Animation loadSync(AssetManager manager, String fileName, FileHandle file, GifloaderParameter parameter) {
return animresult;
}
#Override
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, GifloaderParameter parameter) {
return null;
}
static public class GifloaderParameter extends AssetLoaderParameters<Animation<TextureRegion>> {
}
}
GifDecoder class:
/* Copyright by Johannes Borchardt */
/* LibGdx conversion 2014 by Anton Persson */
/* Released under Apache 2.0 */
/* https://code.google.com/p/animated-gifs-in-android/ */
package com.holidaystudios.tools;
import java.io.InputStream;
import java.util.Vector;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Animation.PlayMode;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.Array;
public class GifDecoder {
/**
* File read status: No errors.
*/
public static final int STATUS_OK = 0;
/**
* File read status: Error decoding file (may be partially decoded)
*/
public static final int STATUS_FORMAT_ERROR = 1;
/**
* File read status: Unable to open source.
*/
public static final int STATUS_OPEN_ERROR = 2;
/** max decoder pixel stack size */
protected static final int MAX_STACK_SIZE = 4096;
protected InputStream in;
protected int status;
protected int width; // full image width
protected int height; // full image height
protected boolean gctFlag; // global color table used
protected int gctSize; // size of global color table
protected int loopCount = 1; // iterations; 0 = repeat forever
protected int[] gct; // global color table
protected int[] lct; // local color table
protected int[] act; // active color table
protected int bgIndex; // background color index
protected int bgColor; // background color
protected int lastBgColor; // previous bg color
protected int pixelAspect; // pixel aspect ratio
protected boolean lctFlag; // local color table flag
protected boolean interlace; // interlace flag
protected int lctSize; // local color table size
protected int ix, iy, iw, ih; // current image rectangle
protected int lrx, lry, lrw, lrh;
protected DixieMap image; // current frame
protected DixieMap lastPixmap; // previous frame
protected byte[] block = new byte[256]; // current data block
protected int blockSize = 0; // block size last graphic control extension info
protected int dispose = 0; // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
protected int lastDispose = 0;
protected boolean transparency = false; // use transparent color
protected int delay = 0; // delay in milliseconds
protected int transIndex; // transparent color index
// LZW decoder working arrays
protected short[] prefix;
protected byte[] suffix;
protected byte[] pixelStack;
protected byte[] pixels;
protected Vector<GifFrame> frames; // frames read from current file
protected int frameCount;
private static class DixieMap extends Pixmap {
DixieMap(int w, int h, Pixmap.Format f) {
super(w, h, f);
}
DixieMap(int[] data, int w, int h, Pixmap.Format f) {
super(w, h, f);
int x, y;
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
int pxl_ARGB8888 = data[x + y * w];
int pxl_RGBA8888 =
((pxl_ARGB8888 >> 24) & 0x000000ff) | ((pxl_ARGB8888 << 8) & 0xffffff00);
// convert ARGB8888 > RGBA8888
drawPixel(x, y, pxl_RGBA8888);
}
}
}
void getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height) {
java.nio.ByteBuffer bb = getPixels();
int k, l;
for(k = y; k < y + height; k++) {
int _offset = offset;
for(l = x; l < x + width; l++) {
int pxl = bb.getInt(4 * (l + k * width));
// convert RGBA8888 > ARGB8888
pixels[_offset++] = ((pxl >> 8) & 0x00ffffff) | ((pxl << 24) & 0xff000000);
}
offset += stride;
}
}
}
private static class GifFrame {
public GifFrame(DixieMap im, int del) {
image = im;
delay = del;
}
public DixieMap image;
public int delay;
}
/**
* Gets display duration for specified frame.
*
* #param n
* int index of frame
* #return delay in milliseconds
*/
public int getDelay(int n) {
delay = -1;
if ((n >= 0) && (n < frameCount)) {
delay = frames.elementAt(n).delay;
}
return delay;
}
/**
* Gets the number of frames read from file.
*
* #return frame count
*/
public int getFrameCount() {
return frameCount;
}
/**
* Gets the first (or only) image read.
*
* #return BufferedPixmap containing first frame, or null if none.
*/
public Pixmap getPixmap() {
return getFrame(0);
}
/**
* Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitely.
*
* #return iteration count if one was specified, else 1.
*/
public int getLoopCount() {
return loopCount;
}
/**
* Creates new frame image from current data (and previous frames as specified by their disposition codes).
*/
protected void setPixels() {
// expose destination image's pixels as int array
int[] dest = new int[width * height];
// fill in starting image contents based on last image's dispose code
if (lastDispose > 0) {
if (lastDispose == 3) {
// use image before last
int n = frameCount - 2;
if (n > 0) {
lastPixmap = getFrame(n - 1);
} else {
lastPixmap = null;
}
}
if (lastPixmap != null) {
lastPixmap.getPixels(dest, 0, width, 0, 0, width, height);
// copy pixels
if (lastDispose == 2) {
// fill last image rect area with background color
int c = 0;
if (!transparency) {
c = lastBgColor;
}
for (int i = 0; i < lrh; i++) {
int n1 = (lry + i) * width + lrx;
int n2 = n1 + lrw;
for (int k = n1; k < n2; k++) {
dest[k] = c;
}
}
}
}
}
// copy each source line to the appropriate place in the destination
int pass = 1;
int inc = 8;
int iline = 0;
for (int i = 0; i < ih; i++) {
int line = i;
if (interlace) {
if (iline >= ih) {
pass++;
switch (pass) {
case 2:
iline = 4;
break;
case 3:
iline = 2;
inc = 4;
break;
case 4:
iline = 1;
inc = 2;
break;
default:
break;
}
}
line = iline;
iline += inc;
}
line += iy;
if (line < height) {
int k = line * width;
int dx = k + ix; // start of line in dest
int dlim = dx + iw; // end of dest line
if ((k + width) < dlim) {
dlim = k + width; // past dest edge
}
int sx = i * iw; // start of line in source
while (dx < dlim) {
// map color and insert in destination
int index = ((int) pixels[sx++]) & 0xff;
int c = act[index];
if (c != 0) {
dest[dx] = c;
}
dx++;
}
}
}
image = new DixieMap(dest, width, height, Pixmap.Format.RGBA8888);
//Pixmap.createPixmap(dest, width, height, Config.ARGB_4444);
}
/**
* Gets the image contents of frame n.
*
* #return BufferedPixmap representation of frame, or null if n is invalid.
*/
public DixieMap getFrame(int n) {
if (frameCount <= 0)
return null;
n = n % frameCount;
return ((GifFrame) frames.elementAt(n)).image;
}
/**
* Reads GIF image from stream
*
* #param is
* containing GIF file.
* #return read status code (0 = no errors)
*/
public int read(InputStream is) {
init();
if (is != null) {
in = is;
readHeader();
if (!err()) {
readContents();
if (frameCount < 0) {
status = STATUS_FORMAT_ERROR;
}
}
} else {
status = STATUS_OPEN_ERROR;
}
try {
is.close();
} catch (Exception e) {
}
return status;
}
/**
* Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
*/
protected void decodeBitmapData() {
int nullCode = -1;
int npix = iw * ih;
int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
if ((pixels == null) || (pixels.length < npix)) {
pixels = new byte[npix]; // allocate new pixel array
}
if (prefix == null) {
prefix = new short[MAX_STACK_SIZE];
}
if (suffix == null) {
suffix = new byte[MAX_STACK_SIZE];
}
if (pixelStack == null) {
pixelStack = new byte[MAX_STACK_SIZE + 1];
}
// Initialize GIF data stream decoder.
data_size = read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = nullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
suffix[code] = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix;) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = readBlock();
if (count <= 0) {
break;
}
bi = 0;
}
datum += (((int) block[bi]) & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information)) {
break;
}
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = nullCode;
continue;
}
if (old_code == nullCode) {
pixelStack[top++] = suffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
pixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
pixelStack[top++] = suffix[code];
code = prefix[code];
}
first = ((int) suffix[code]) & 0xff;
// Add a new string to the string table,
if (available >= MAX_STACK_SIZE) {
break;
}
pixelStack[top++] = (byte) first;
prefix[available] = (short) old_code;
suffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
pixels[pi++] = pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
pixels[i] = 0; // clear missing pixels
}
}
/**
* Returns true if an error was encountered during reading/decoding
*/
protected boolean err() {
return status != STATUS_OK;
}
/**
* Initializes or re-initializes reader
*/
protected void init() {
status = STATUS_OK;
frameCount = 0;
frames = new Vector<GifFrame>();
gct = null;
lct = null;
}
/**
* Reads a single byte from the input stream.
*/
protected int read() {
int curByte = 0;
try {
curByte = in.read();
} catch (Exception e) {
status = STATUS_FORMAT_ERROR;
}
return curByte;
}
/**
* Reads next variable length block from input.
*
* #return number of bytes stored in "buffer"
*/
protected int readBlock() {
blockSize = read();
int n = 0;
if (blockSize > 0) {
try {
int count = 0;
while (n < blockSize) {
count = in.read(block, n, blockSize - n);
if (count == -1) {
break;
}
n += count;
}
} catch (Exception e) {
e.printStackTrace();
}
if (n < blockSize) {
status = STATUS_FORMAT_ERROR;
}
}
return n;
}
/**
* Reads color table as 256 RGB integer values
*
* #param ncolors
* int number of colors to read
* #return int array containing 256 colors (packed ARGB with full alpha)
*/
protected int[] readColorTable(int ncolors) {
int nbytes = 3 * ncolors;
int[] tab = null;
byte[] c = new byte[nbytes];
int n = 0;
try {
n = in.read(c);
} catch (Exception e) {
e.printStackTrace();
}
if (n < nbytes) {
status = STATUS_FORMAT_ERROR;
} else {
tab = new int[256]; // max size to avoid bounds checks
int i = 0;
int j = 0;
while (i < ncolors) {
int r = ((int) c[j++]) & 0xff;
int g = ((int) c[j++]) & 0xff;
int b = ((int) c[j++]) & 0xff;
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
return tab;
}
/**
* Main file parser. Reads GIF content blocks.
*/
protected void readContents() {
// read GIF file content blocks
boolean done = false;
while (!(done || err())) {
int code = read();
switch (code) {
case 0x2C: // image separator
readBitmap();
break;
case 0x21: // extension
code = read();
switch (code) {
case 0xf9: // graphics control extension
readGraphicControlExt();
break;
case 0xff: // application extension
readBlock();
String app = "";
for (int i = 0; i < 11; i++) {
app += (char) block[i];
}
if (app.equals("NETSCAPE2.0")) {
readNetscapeExt();
} else {
skip(); // don't care
}
break;
case 0xfe:// comment extension
skip();
break;
case 0x01:// plain text extension
skip();
break;
default: // uninteresting extension
skip();
}
break;
case 0x3b: // terminator
done = true;
break;
case 0x00: // bad byte, but keep going and see what happens break;
default:
status = STATUS_FORMAT_ERROR;
}
}
}
/**
* Reads Graphics Control Extension values
*/
protected void readGraphicControlExt() {
read(); // block size
int packed = read(); // packed fields
dispose = (packed & 0x1c) >> 2; // disposal method
if (dispose == 0) {
dispose = 1; // elect to keep old image if discretionary
}
transparency = (packed & 1) != 0;
delay = readShort() * 10; // delay in milliseconds
transIndex = read(); // transparent color index
read(); // block terminator
}
/**
* Reads GIF file header information.
*/
protected void readHeader() {
String id = "";
for (int i = 0; i < 6; i++) {
id += (char) read();
}
if (!id.startsWith("GIF")) {
status = STATUS_FORMAT_ERROR;
return;
}
readLSD();
if (gctFlag && !err()) {
gct = readColorTable(gctSize);
bgColor = gct[bgIndex];
}
}
/**
* Reads next frame image
*/
protected void readBitmap() {
ix = readShort(); // (sub)image position & size
iy = readShort();
iw = readShort();
ih = readShort();
int packed = read();
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace
lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
// 3 - sort flag
// 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
// table size
interlace = (packed & 0x40) != 0;
if (lctFlag) {
lct = readColorTable(lctSize); // read table
act = lct; // make local table active
} else {
act = gct; // make global table active
if (bgIndex == transIndex) {
bgColor = 0;
}
}
int save = 0;
if (transparency) {
save = act[transIndex];
act[transIndex] = 0; // set transparent color if specified
}
if (act == null) {
status = STATUS_FORMAT_ERROR; // no color table defined
}
if (err()) {
return;
}
decodeBitmapData(); // decode pixel data
skip();
if (err()) {
return;
}
frameCount++;
// create new image to receive frame data
image = new DixieMap(width, height, Pixmap.Format.RGBA8888);
setPixels(); // transfer pixel data to image
frames.addElement(new GifFrame(image, delay)); // add image to frame
// list
if (transparency) {
act[transIndex] = save;
}
resetFrame();
}
/**
* Reads Logical Screen Descriptor
*/
protected void readLSD() {
// logical screen size
width = readShort();
height = readShort();
// packed fields
int packed = read();
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
gctSize = 2 << (packed & 7); // 6-8 : gct size
bgIndex = read(); // background color index
pixelAspect = read(); // pixel aspect ratio
}
/**
* Reads Netscape extenstion to obtain iteration count
*/
protected void readNetscapeExt() {
do {
readBlock();
if (block[0] == 1) {
// loop count sub-block
int b1 = ((int) block[1]) & 0xff;
int b2 = ((int) block[2]) & 0xff;
loopCount = (b2 << 8) | b1;
}
} while ((blockSize > 0) && !err());
}
/**
* Reads next 16-bit value, LSB first
*/
protected int readShort() {
// read 16-bit value, LSB first
return read() | (read() << 8);
}
/**
* Resets frame state for reading next image.
*/
protected void resetFrame() {
lastDispose = dispose;
lrx = ix;
lry = iy;
lrw = iw;
lrh = ih;
lastPixmap = image;
lastBgColor = bgColor;
dispose = 0;
transparency = false;
delay = 0;
lct = null;
}
/**
* Skips variable length blocks up to and including next zero length block.
*/
protected void skip() {
do {
readBlock();
} while ((blockSize > 0) && !err());
}
public Animation<TextureRegion> getAnimation(PlayMode playMode) {
int nrFrames = getFrameCount();
Pixmap frame = getFrame(0);
int width = frame.getWidth();
int height = frame.getHeight();
int vzones = (int)Math.sqrt((double)nrFrames);
int hzones = vzones;
while(vzones * hzones < nrFrames) vzones++;
int v, h;
Pixmap target = new Pixmap(width * hzones, height * vzones, Pixmap.Format.RGBA8888);
for(h = 0; h < hzones; h++) {
for(v = 0; v < vzones; v++) {
int frameID = v + h * vzones;
if(frameID < nrFrames) {
frame = getFrame(frameID);
target.drawPixmap(frame, h * width, v * height);
}
}
}
Texture texture = new Texture(target);
Array<TextureRegion> texReg = new Array<TextureRegion>();
for(h = 0; h < hzones; h++) {
for(v = 0; v < vzones; v++) {
int frameID = v + h * vzones;
if(frameID < nrFrames) {
TextureRegion tr = new TextureRegion(texture, h * width, v * height, width, height);
texReg.add(tr);
}
}
}
float frameDuration = (float)getDelay(0);
frameDuration /= 1000; // convert milliseconds into seconds
Animation<TextureRegion> result = new Animation<TextureRegion>(frameDuration, texReg, playMode);
return result;
}
public static Animation<TextureRegion> loadGIFAnimation(Animation.PlayMode playMode, InputStream is) {
GifDecoder gdec = new GifDecoder();
gdec.read(is);
return gdec.getAnimation(playMode);
}
}
please anyone help me
I could only make this work in the syncloader and not in the asyncloader is problably because it takes too much resources.
Too avoid the errors i made a GIF class to wrap the Animation this is also to dispose the texture when its no longer needed if this is not done it will create a memory leak.
public class GIF implements Disposable {
public Animation<TextureRegion> animation;
public Texture texture;
public GIF( Animation<TextureRegion> animation ) {
Object[] object = animation.getKeyFrames();
this.animation = animation;
this.texture = ((TextureRegion) object[ 0 ]).getTexture();
}
#Override
public void dispose() {
texture.dispose();
}
}
Here is the asset loader code:
public class Gifloader extends AsynchronousAssetLoader<GIF, Gifloader.GifloaderParameter> {
private Animation<TextureRegion> animation;
public Gifloader(FileHandleResolver resolver) {
super(resolver);
this.animation = null;
}
#Override
public void loadAsync(AssetManager manager, String fileName, FileHandle file, GifloaderParameter parameter) {
}
#Override
public GIF loadSync(AssetManager manager, String fileName, FileHandle file, GifloaderParameter parameter) {
PlayMode playMode = PlayMode.LOOP;
if ( parameter != null ){
playMode = parameter.playMode;
}
animation = (GifDecoder.loadGIFAnimation( playMode, file.read() ));
return new GIF( animation );
}
#SuppressWarnings( "rawtypes" )
#Override
public Array<AssetDescriptor> getDependencies(String fileName, FileHandle file, GifloaderParameter parameter) {
return null;
}
public static class GifloaderParameter extends AssetLoaderParameters<GIF> {
public PlayMode playMode = PlayMode.LOOP;
}
}
My goal is to make a .ico or a HICON out of canvas drawing. Below is a snippet for windows. What it does is lets you file pick. You pick an image and then it adds canvas to the document in current tab (at way bottom) and then draws the 32px logo on cavas and then on that it overlays the browsed image. Now im trying to us jsctypes of winapi CreateIconFromResourceEx with canvas.mozFetchAsStream but I can't figure it out.
I'm stuck on this line: alert(streamData).
Also for PBYTE in my CreateIconFromResourceEx is it correct to set it to ctypes.unsigned_char.ptr? (Because byte I know is just ctypes.unsigned_char right?)
Components.utils.import('resource://gre/modules/ctypes.jsm');
var user32 = ctypes.open('user32.dll');
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648061%28v=vs.85%29.aspx
* HICON WINAPI CreateIconFromResourceEx(
* __in_ PBYTE pbIconBits,
* __in_ DWORD cbIconBits,
* __in_ BOOL fIcon,
* __in_ DWORD dwVersion,
* __in_ int cxDesired,
* __in_ int cyDesired,
* __in_ UINT uFlags
* );
*/
var CreateIconFromResourceEx = user32.declare('CreateIconFromResourceEx', ctypes.winapi_abi, ctypes.voidptr_t,
ctypes.unsigned_char.ptr, /* PBYTE pbIconBits */
ctypes.unsigned_long, /* DWORD cbIconBits */
ctypes.bool, /* BOOL fIcon */
ctypes.unsigned_long, /* DWORD dwVersion */
ctypes.int, /* int cxDesired */
ctypes.int, /* int cyDesired */
ctypes.unsigned_int /* UINT uFlags */
);
/////////////// running stuff below. above was just defining stuff
var me = Services.wm.getMostRecentWindow(null);
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
var ctx = canvas.getContext('2d');
gBrowser.contentDocument.documentElement.appendChild(canvas);
var fp = Cc['#mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
fp.init(window, 'Select Badge Image', Ci.nsIFilePicker.modeOpen);
fp.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText);
var rv = fp.show();
if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) {
var file = fp.file;
// Get the path as string. Note that you usually won't
// need to work with the string paths.
var path = fp.file.path;
var oImage = new Image();
oImage.src = 'chrome://branding/content/icon32.png'; //Services.io.newFileURI(file).spec;
oImage.onload = function() {
alert('loaded')
canvas.width = this.width;
canvas.height = this.height;
ctx.clearRect(0, 0, this.width, this.height);
ctx.drawImage(this, 0, 0);
var oImage = new Image();
oImage.src = Services.io.newFileURI(file).spec;
oImage.onload = function() {
alert('loaded')
ctx.drawImage(this, canvas.width-this.width, canvas.height-this.width);
//mozFetchAsStream stuff: https://github.com/mozilla/build-partner-repacks/blob/885947b726c5d6e131af4e4aae621d51109bded4/partners/yandex-drp/distribution/extensions/vb%40yandex.ru/cbapp/parts/screenshotsGrabber.js#L295
var asyncStreamCallback = {
onInputStreamReady: function (streamData) {
alert(streamData)
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISupports,
Ci.nsIInputStreamCallback
])
};
canvas.mozFetchAsStream(asyncStreamCallback, 'image/vnd.microsoft.icon')
//now do canvas.mozGetAFile(blob) then reconstruct icon
}
}
}
////////////////
user32.close();
I don't know what's wrong with your code. But since your objective is to turn the canvas contents to an ICO, here is another way (somewaht more straightfoward I dare to say).
// assuming ctx holds your drawings
let imgdata = ctx.getImageData(0,0,32,32); // this is a 32x32 icon, right?
let icoencoder = Cc["#mozilla.org/image/encoder;2?type=image/vnd.microsoft.icon"].createInstance(Ci.imgIEncoder);
icoencoder.initFromData(imgdata.data, imgdata.data.length, 32, 32, 32*4, Ci.imgIEncoder.INPUT_FORMAT_RGBA, "");
icoencoder.QueryInterface(Ci.nsIInputStream);
var icofile = new FileUtils.File("/path/to/canvas.ico");
var output = FileUtils.openSafeFileOutputStream(icofile);
NetUtil.asyncCopy(icoencoder, output, youroptionalcallback);
The result is a proper canvas.ico file which you can pass to the windows api functions.