From 23d682f53d18a500a9a966add9c41aa75f34e131 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 10 Oct 2014 16:13:42 -0700 Subject: [PATCH 001/334] Makefile: compile with -Os as well Good to spot strange bugs that only appear with certain flags. --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1261d977c..5bdd2617b 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ RELEASE = build/release.sh VERSION = 1 TARPARAMS ?= -j +CFLAGS=-g -Wall -Werror -Os + croutoncursor_LIBS = -lX11 -lXfixes -lXrender croutonwmtools_LIBS = -lX11 croutonxi2event_LIBS = -lX11 -lXi @@ -50,8 +52,8 @@ $(TARGET): $(WRAPPER) $(SCRIPTS) $(GENVERSION) $(GITHEAD) Makefile $(EXTTARGET): $(EXTSOURCES) Makefile rm -f $(EXTTARGET) && zip -q --junk-paths $(EXTTARGET) $(EXTSOURCES) -$(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) Makefile - gcc -g -Wall -Werror $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ +$(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile + gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ extension: $(EXTTARGET) From 7d418b73cb7b701167213c35b30cc5b5b511b9e0 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 15:03:55 -0700 Subject: [PATCH 002/334] targets/common, compile: Insert include files as needed. --- targets/common | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/targets/common b/targets/common index 853f49733..26510acd7 100644 --- a/targets/common +++ b/targets/common @@ -76,7 +76,8 @@ elif [ ! "$TARGETS" = 'check' ]; then # Lines with "### append filename" will queue a file to be processed after # the current file is done. # Lines that start with "compile" will have their source code inserted as a - # HERE document. + # HERE document. In that case, we also look for local include file, and + # insert them as needed. t="$TARGET" if [ "${t#/}" = "$t" ]; then t="$TARGETSDIR/$t" @@ -97,7 +98,23 @@ elif [ ! "$TARGETS" = 'check' ]; then } src && $NF != substr("\\\\", 1, 1) { print $0 " < 0) { + if (line ~ /^#include \".*\.h\"$/) { + include = line + sub(/^#include \"/, "", include) + sub(/\"$/, "", include) + include = "'"${SRCDIR:-$TARGETSDIR/../src}"'/" include; + print "// BEGIN " include + while ((getline line < include) > 0) { + print line + } + close(include) + print "// END " include + } else { + print line + } + } + close(src) print "EOF" src = ""; next From 57381e2d91555a874944e16a83bb9aced084582a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 10 Jan 2014 16:39:26 -0800 Subject: [PATCH 003/334] Support croutoncycle commands via websocket --- src/websocket.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/websocket.c b/src/websocket.c index 0b7c2b7b8..a95324a40 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -135,7 +135,8 @@ static int block_write(int fd, char* buffer, size_t size) { /* Run external command, piping some data on its stdin, and reading back * the output. Returns the number of bytes read from the process (at most * outlen), or -1 on error. */ -static int popen2(char* cmd, char* input, int inlen, char* output, int outlen) { +static int popen2(char* cmd, char* param, + char* input, int inlen, char* output, int outlen) { pid_t pid = 0; int stdin_fd[2]; int stdout_fd[2]; @@ -158,7 +159,7 @@ static int popen2(char* cmd, char* input, int inlen, char* output, int outlen) { close(stdout_fd[0]); dup2(stdout_fd[1], STDOUT_FILENO); - execlp(cmd, cmd, NULL); + execlp(cmd, cmd, param, NULL); error("Error running '%s'.", cmd); exit(1); @@ -816,12 +817,33 @@ static void socket_client_read() { data = 1; } - /* In future versions, we can process such packets here. */ - - /* In the current version, this is actually never supposed to happen: - * close the connection */ - error("Received an unexpected packet from client."); - socket_client_close(0); + /* Process the client request. */ + buffer[length == BUFFERSIZE ? BUFFERSIZE-1 : length] = 0; + switch (buffer[0]) { + case 'C': /* Send a command to croutoncycle and get the response */ + log(2, "Received croutoncycle command (%s)", &buffer[1]); + length = popen2("croutoncycle", &buffer[1], + "", 0, &buffer[1], BUFFERSIZE-1); + if (length == -1) { + error("Call to croutoncycle failed."); + socket_client_close(0); + return; + } + length++; + buffer[length == BUFFERSIZE ? BUFFERSIZE-1 : length] = 0; + log(2, "Sending croutoncycle response (%s)", buffer); + if (socket_client_write_frame(buffer, length, + WS_OPCODE_TEXT, 1) < 0) { + error("Write error."); + socket_client_close(0); + return; + } + break; + default: + error("Received an unexpected packet from client."); + socket_client_close(0); + break; + } } /* Send a version packet to the extension, and read VOK reply. */ @@ -1116,7 +1138,7 @@ static void socket_server_accept() { memcpy(websocket_key+SECKEY_LEN, GUID, strlen(GUID)); /* SHA-1 is 20 bytes long (40 characters in hex form) */ - if (popen2("sha1sum", websocket_key, websocket_keylen, + if (popen2("sha1sum", NULL, websocket_key, websocket_keylen, buffer, BUFFERSIZE) < 2*SHA1_LEN) { error("sha1sum response too short."); exit(1); @@ -1135,7 +1157,7 @@ static void socket_server_accept() { /* base64 encoding of SHA1_LEN bytes must be SHA1_BASE64_LEN bytes long. * Either the output is exactly SHA1_BASE64_LEN long, or the last character * is a line feed (RFC 3548 forbids other characters in output) */ - int n = popen2("base64", sha1, SHA1_LEN, b64, b64_len); + int n = popen2("base64", NULL, sha1, SHA1_LEN, b64, b64_len); if (n < SHA1_BASE64_LEN || (n != SHA1_BASE64_LEN && b64[SHA1_BASE64_LEN] != '\r' && b64[SHA1_BASE64_LEN] != '\n')) { From 0e57eaae8ff2cb4ac42b63f1f7fee29b2746a396 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 10 Jan 2014 19:00:50 -0800 Subject: [PATCH 004/334] Add basic chroot listing and switching to the extension TODO: croutoncycle should check version and then write 'C' to the fifo. --- host-ext/crouton/background.js | 75 +++++++++++++++++++++++++++++----- host-ext/crouton/popup.html | 25 +++++++++++- src/websocket.c | 20 ++++++--- 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 38f9f7136..8638c2421 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -4,10 +4,11 @@ /* Constants */ var URL = "ws://localhost:30001/"; -var VERSION = 1; /* Note: the extension must always be backward compatible */ +var VERSION = 2; /* Note: the extension must always be backward compatible */ var MAXLOGGERLEN = 20; var RETRY_TIMEOUT = 5; var UPDATE_CHECK_INTERVAL = 15*60; /* Check for updates every 15' at most */ +var WINDOW_UPDATE_INTERVAL = 15*60; /* Update window list every 15' at most */ /* String to copy to the clipboard if it should be empty */ var DUMMY_EMPTYSTRING = "%"; @@ -31,9 +32,12 @@ var dummystr_ = false; /* true if the last string we copied was the dummy string var update_ = false; /* true if an update to the extension is available */ var lastupdatecheck_ = null; +var lastwindowlistupdate_ = null; var status_ = ""; +var sversion_ = 0; /* Version of the websocket server */ var logger_ = []; /* Array of status messages: [LogLevel, time, message] */ +var windows_ = []; /* Array of windows (xorg, xephyr, host-x11, etc) */ /* Set the current status string. * active is a boolean, true if the WebSocket connection is established. */ @@ -45,6 +49,10 @@ function setStatus(status, active) { if (update_ && !active_) chrome.runtime.reload(); + /* Force a window list update on active */ + if (active_) + updateWindowList(true); + refreshUI(); } @@ -78,8 +86,24 @@ function checkUpdate(force) { } } +function updateWindowList(force) { + if (!active_ || sversion_ < 2) + return; + + var currenttime = new Date().getTime(); + + if (force || lastwindowlistupdate_ == null || + (currenttime-lastwindowlistupdate_) > 1000*WINDOW_UPDATE_INTERVAL) { + printLog("Sending window list request", LogLevel.DEBUG); + websocket_.send("Cl"); + lastwindowlistupdate_ = currenttime; + } +} + /* Update the icon, and refresh the popup page */ function refreshUI() { + updateWindowList(false); + if (error_) icon = "error" else if (!enabled_) @@ -96,14 +120,15 @@ function refreshUI() { var views = chrome.extension.getViews({type: "popup"}); for (var i = 0; i < views.length; views++) { + var view = views[i]; /* Make sure page is ready */ if (document.readyState === "complete") { /* Update "help" link */ - helplink = views[i].document.getElementById("help"); + helplink = view.document.getElementById("help"); helplink.onclick = showHelp; /* Update enable/disable link. */ /* FIXME: Sometimes, there is a little box coming around the link */ - enablelink = views[i].document.getElementById("enable"); + enablelink = view.document.getElementById("enable"); if (enabled_) { enablelink.textContent = "Disable"; enablelink.onclick = function() { @@ -127,7 +152,7 @@ function refreshUI() { } /* Update debug mode according to checkbox state. */ - debugcheck = views[i].document.getElementById("debugcheck"); + debugcheck = view.document.getElementById("debugcheck"); debugcheck.onclick = function() { debug_ = debugcheck.checked; refreshUI(); @@ -135,17 +160,39 @@ function refreshUI() { debugcheck.checked = debug_; /* Update status box */ - views[i].document.getElementById("info").textContent = status_; + view.document.getElementById("info").textContent = status_; + + /* Update window table */ + windowlist = view.document.getElementById("windowlist"); + + while (windowlist.rows.length > 0) { + windowlist.deleteRow(0); + } + + for (var i = 0; i < windows_.length; i++) { + if (!windows_[i].length) + continue; + var row = windowlist.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(1); + cell1.className = "display"; + cell1.innerHTML = windows_[i][0]; + cell2.className = "name"; + cell2.innerHTML = windows_[i].substring(3) + cell2.onclick = (function(i) { return function() { + websocket_.send("C" + i); + } })(i); + } /* Update logger table */ - loggertable = views[i].document.getElementById("logger"); + loggertable = view.document.getElementById("logger"); /* FIXME: only update needed rows */ while (loggertable.rows.length > 0) { loggertable.deleteRow(0); } - for (i = 0; i < logger_.length; i++) { + for (var i = 0; i < logger_.length; i++) { value = logger_[i]; if (value[0] == LogLevel.DEBUG && !debug_) @@ -235,14 +282,15 @@ function websocketMessage(evt) { /* Only accept version packets until we have received one. */ if (!active_) { if (cmd == 'V') { /* Version */ - if (payload < 1 || payload > VERSION) { + sversion_ = payload; + if (sversion_ < 1 || sversion_ > VERSION) { websocket_.send("EInvalid version (> " + VERSION + ")"); - error("Invalid server version " + payload + " > " + VERSION, + error("Invalid server version " + sversion_ + " > " + VERSION, false); } + websocket_.send("VOK"); /* Set active_ to true */ setStatus("Connected", true); - websocket_.send("VOK"); return; } else { error("Received frame while waiting for version", false); @@ -295,6 +343,13 @@ function websocketMessage(evt) { websocket_.send("EError: URL must be absolute"); } + break; + case 'C': /* Returned data from a croutoncycle command */ + /* Non-zero length has a window list; otherwise it's a cycle signal */ + if (payload.length > 0) { + windows_ = payload.split('\n'); + } + refreshUI(); break; case 'P': /* Ping */ websocket_.send(received_msg); diff --git a/host-ext/crouton/popup.html b/host-ext/crouton/popup.html index bee2d4bf6..7ad59de64 100644 --- a/host-ext/crouton/popup.html +++ b/host-ext/crouton/popup.html @@ -18,6 +18,15 @@ outline: 0; } + #switcher { + width: 400px; + } + + #windowlist { + border: 0px; + border-spacing: 0; + } + #scrolllogger { width: 400px; height: 200px; @@ -33,7 +42,17 @@ font-family: "monospace"; font-size: 12px; vertical-align: top; - padding: 2px 15px; + } + + td.display { + padding: 2px 5px; + color:#666666; + } + + td.name { + padding: 2px 5px; + color:#000080; + cursor:pointer; } td.time { @@ -46,6 +65,9 @@ color: #000000; } + td.value { + padding: 2px 15px; + } td.value.debug { color: #808080; } td.value.info { color: #008000; } td.value.error { color: #800000; } @@ -57,6 +79,7 @@

crouton integration


Loading...
+
diff --git a/src/websocket.c b/src/websocket.c index a95324a40..38d571ba0 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -31,7 +31,7 @@ const int BUFFERSIZE = 4096; /* WebSocket constants */ -#define VERSION "1" +#define VERSION "2" const int PORT = 30001; const int FRAMEMAXHEADERSIZE = 2+8; const int MAXFRAMESIZE = 16*1048576; // 16MiB @@ -55,6 +55,7 @@ const int WS_OPCODE_PONG = 0xA; const char* PIPE_DIR = "/tmp/crouton-ext"; const char* PIPEIN_FILENAME = "/tmp/crouton-ext/in"; const char* PIPEOUT_FILENAME = "/tmp/crouton-ext/out"; +const char* PIPE_VERSION_FILE = "/tmp/crouton-ext/version"; const int PIPEOUT_WRITE_TIMEOUT = 3000; /* 0 - Quiet @@ -530,6 +531,15 @@ void pipe_init() { exit(1); } + /* Create a file with the version number of the protocol */ + FILE *vers = fopen(PIPE_VERSION_FILE, "w"); + if (!vers + || fputs(VERSION "\n", vers) == EOF + || fclose(vers) == EOF) { + error("Unable to write to %s.", PIPE_VERSION_FILE); + exit(1); + } + pipein_reopen(); } @@ -822,16 +832,16 @@ static void socket_client_read() { switch (buffer[0]) { case 'C': /* Send a command to croutoncycle and get the response */ log(2, "Received croutoncycle command (%s)", &buffer[1]); - length = popen2("croutoncycle", &buffer[1], - "", 0, &buffer[1], BUFFERSIZE-1); + length = popen2("croutoncycle", &buffer[1], "", 0, + &buffer[FRAMEMAXHEADERSIZE+1], + BUFFERSIZE-FRAMEMAXHEADERSIZE-1); if (length == -1) { error("Call to croutoncycle failed."); socket_client_close(0); return; } + buffer[FRAMEMAXHEADERSIZE] = 'C'; length++; - buffer[length == BUFFERSIZE ? BUFFERSIZE-1 : length] = 0; - log(2, "Sending croutoncycle response (%s)", buffer); if (socket_client_write_frame(buffer, length, WS_OPCODE_TEXT, 1) < 0) { error("Write error."); From 92237bccc2c1fea696a65414686f5037505c3f2b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 13 Jan 2014 14:49:05 -0800 Subject: [PATCH 005/334] Only show one of the windowlist or the "connected" status --- host-ext/crouton/background.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 8638c2421..19871a7c1 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -49,9 +49,8 @@ function setStatus(status, active) { if (update_ && !active_) chrome.runtime.reload(); - /* Force a window list update on active */ - if (active_) - updateWindowList(true); + /* Force a window list update */ + updateWindowList(true); refreshUI(); } @@ -87,8 +86,10 @@ function checkUpdate(force) { } function updateWindowList(force) { - if (!active_ || sversion_ < 2) + if (!active_ || sversion_ < 2) { + windows_ = []; return; + } var currenttime = new Date().getTime(); @@ -290,7 +291,7 @@ function websocketMessage(evt) { } websocket_.send("VOK"); /* Set active_ to true */ - setStatus("Connected", true); + setStatus("", true); return; } else { error("Received frame while waiting for version", false); From cac305aa0ad8bad730fce04f303dd761f448d241 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 13 Jan 2014 15:47:29 -0800 Subject: [PATCH 006/334] Hide log behind checkbox, and move disable button. --- host-ext/crouton/background.js | 35 ++++++++++++++++++++-------------- host-ext/crouton/popup.html | 24 +++++++++-------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 19871a7c1..077a2c240 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -193,20 +193,27 @@ function refreshUI() { loggertable.deleteRow(0); } - for (var i = 0; i < logger_.length; i++) { - value = logger_[i]; - - if (value[0] == LogLevel.DEBUG && !debug_) - continue; - - var row = loggertable.insertRow(-1); - var cell1 = row.insertCell(0); - var cell2 = row.insertCell(1); - var levelclass = value[0]; - cell1.className = "time " + levelclass; - cell2.className = "value " + levelclass; - cell1.innerHTML = value[1]; - cell2.innerHTML = value[2]; + /* Only update if "show log" is enabled */ + logcheck = view.document.getElementById("logcheck"); + logcheck.onclick = function() { + refreshUI(); + } + if (logcheck.checked) { + for (var i = 0; i < logger_.length; i++) { + value = logger_[i]; + + if (value[0] == LogLevel.DEBUG && !debug_) + continue; + + var row = loggertable.insertRow(-1); + var cell1 = row.insertCell(0); + var cell2 = row.insertCell(1); + var levelclass = value[0]; + cell1.className = "time " + levelclass; + cell2.className = "value " + levelclass; + cell1.innerHTML = value[1]; + cell2.innerHTML = value[2]; + } } } } diff --git a/host-ext/crouton/popup.html b/host-ext/crouton/popup.html index 7ad59de64..6d544960d 100644 --- a/host-ext/crouton/popup.html +++ b/host-ext/crouton/popup.html @@ -18,21 +18,11 @@ outline: 0; } - #switcher { - width: 400px; - } - #windowlist { border: 0px; border-spacing: 0; } - #scrolllogger { - width: 400px; - height: 200px; - overflow-y: auto; - } - #logger { border: 0px; border-spacing: 0; @@ -51,6 +41,7 @@ td.name { padding: 2px 5px; + font-size: 14px; color:#000080; cursor:pointer; } @@ -75,15 +66,18 @@ - +

crouton integration


Loading...
- -
-
-
+
+ + + + +
From 43634d72ea6302dd24082462805abbbed0961079 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 13 Jan 2014 19:53:42 -0800 Subject: [PATCH 007/334] Extension should still display "connected" with old chroots --- host-ext/crouton/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 077a2c240..c60e95e3a 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -298,7 +298,7 @@ function websocketMessage(evt) { } websocket_.send("VOK"); /* Set active_ to true */ - setStatus("", true); + setStatus(sversion_ >= 2 ? "" : "Connected", true); return; } else { error("Received frame while waiting for version", false); From cd219861868281c708c32566bde07d1d6dc4ca98 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:07:40 -0700 Subject: [PATCH 008/334] Bigger font size. But, seriously, we need some UI redesign. --- host-ext/crouton/popup.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/host-ext/crouton/popup.html b/host-ext/crouton/popup.html index 6d544960d..c7c97c9f9 100644 --- a/host-ext/crouton/popup.html +++ b/host-ext/crouton/popup.html @@ -36,12 +36,13 @@ td.display { padding: 2px 5px; + font-size: 20px; color:#666666; } td.name { padding: 2px 5px; - font-size: 14px; + font-size: 20px; color:#000080; cursor:pointer; } From 9b63954ef7503a653ecc76ee9e1213c68f369efa Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:20:45 -0700 Subject: [PATCH 009/334] websocket: Split file in 2 parts. websocket.h with generic functions, websocket.c with specific parts. --- Makefile | 2 + src/websocket.c | 952 +++--------------------------------------------- src/websocket.h | 894 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 937 insertions(+), 911 deletions(-) create mode 100644 src/websocket.h diff --git a/Makefile b/Makefile index 5bdd2617b..ea0bdc0b7 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ croutoncursor_LIBS = -lX11 -lXfixes -lXrender croutonwmtools_LIBS = -lX11 croutonxi2event_LIBS = -lX11 -lXi +croutonwebsocket_DEPS = src/websocket.h + ifeq ($(wildcard .git/HEAD),) GITHEAD := else diff --git a/src/websocket.c b/src/websocket.c index 38d571ba0..214cadbf5 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -3,53 +3,19 @@ * found in the LICENSE file. * * WebSocket server that provides an interface to an extension running in - * Chromium OS. + * Chromium OS, used for clipboard synchronization and URL handling. * - * Mostly compliant with RFC 6455 - The WebSocket Protocol. - * - * Things that are supported, but not tested: - * - Fragmented packets from client - * - Ping packets */ #define _GNU_SOURCE /* for ppoll */ -#include -#include -#include -#include -#include +#include "websocket.h" #include -#include -#include #include -#include -#include -#include -#include -#include - -const int BUFFERSIZE = 4096; +#include /* WebSocket constants */ -#define VERSION "2" -const int PORT = 30001; -const int FRAMEMAXHEADERSIZE = 2+8; -const int MAXFRAMESIZE = 16*1048576; // 16MiB -const char* GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; -/* Key from client must be 24 bytes long (16 bytes, base64 encoded) */ -const int SECKEY_LEN = 24; -/* SHA-1 is 20 bytes long */ -const int SHA1_LEN = 20; -/* base64-encoded SHA-1 must be 28 bytes long (ceil(20/3*4)+1). */ -const int SHA1_BASE64_LEN = 28; - -/* WebSocket opcodes */ -const int WS_OPCODE_CONT = 0x0; -const int WS_OPCODE_TEXT = 0x1; -const int WS_OPCODE_BINARY = 0x2; -const int WS_OPCODE_CLOSE = 0x8; -const int WS_OPCODE_PING = 0x9; -const int WS_OPCODE_PONG = 0xA; +#define VERSION "V2" +#define PORT 30001 /* Pipe constants */ const char* PIPE_DIR = "/tmp/crouton-ext"; @@ -58,208 +24,12 @@ const char* PIPEOUT_FILENAME = "/tmp/crouton-ext/out"; const char* PIPE_VERSION_FILE = "/tmp/crouton-ext/version"; const int PIPEOUT_WRITE_TIMEOUT = 3000; -/* 0 - Quiet - * 1 - General messages (init, new connections) - * 2 - 1 + Information on each transfer - * 3 - 2 + Extra information */ -static int verbose = 0; - -#define log(level, str, ...) do { \ - if (verbose >= (level)) printf("%s: " str "\n", __func__, ##__VA_ARGS__); \ -} while (0) - -#define error(str, ...) printf("%s: " str "\n", __func__, ##__VA_ARGS__) - -/* Similar to perror, but prints function name as well */ -#define syserror(str, ...) printf("%s: " str " (%s)\n", \ - __func__, ##__VA_ARGS__, strerror(errno)) - /* File descriptors */ -static int server_fd = -1; static int pipein_fd = -1; -static int client_fd = -1; static int pipeout_fd = -1; -/* Prototypes */ -static int socket_client_write_frame(char* buffer, unsigned int size, - unsigned int opcode, int fin); -static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, - int* length); -static int socket_client_read_frame_data(char* buffer, unsigned int size, - uint32_t maskkey); -static void socket_client_close(int close_reason); - static void pipeout_close(); -/**/ -/* Helper functions */ -/**/ - -/* Read exactly size bytes from fd, no matter how many reads it takes. - * Returns size if successful, < 0 in case of error. */ -static int block_read(int fd, char* buffer, size_t size) { - int n; - int tot = 0; - - while (tot < size) { - n = read(fd, buffer+tot, size-tot); - log(3, "n=%d+%d/%zd", n, tot, size); - if (n < 0) - return n; - if (n == 0) - return -1; /* EOF */ - tot += n; - } - - return tot; -} - -/* Write exactly size bytes from fd, no matter how many writes it takes. - * Returns size if successful, < 0 in case of error. */ -static int block_write(int fd, char* buffer, size_t size) { - int n; - int tot = 0; - - while (tot < size) { - n = write(fd, buffer+tot, size-tot); - log(3, "n=%d+%d/%zd", n, tot, size); - if (n < 0) - return n; - if (n == 0) - return -1; - tot += n; - } - - return tot; -} - -/* Run external command, piping some data on its stdin, and reading back - * the output. Returns the number of bytes read from the process (at most - * outlen), or -1 on error. */ -static int popen2(char* cmd, char* param, - char* input, int inlen, char* output, int outlen) { - pid_t pid = 0; - int stdin_fd[2]; - int stdout_fd[2]; - int ret = -1; - - if (pipe(stdin_fd) < 0 || pipe(stdout_fd) < 0) { - syserror("Failed to create pipe."); - return -1; - } - - pid = fork(); - - if (pid < 0) { - syserror("Fork error."); - return -1; - } else if (pid == 0) { - /* Child: connect stdin/out to the pipes, close the unneeded halves */ - close(stdin_fd[1]); - dup2(stdin_fd[0], STDIN_FILENO); - close(stdout_fd[0]); - dup2(stdout_fd[1], STDOUT_FILENO); - - execlp(cmd, cmd, param, NULL); - - error("Error running '%s'.", cmd); - exit(1); - } - - /* Parent */ - - /* Write input, and read output, while waiting for process termination. - * This could be done without polling, by reacting on SIGCHLD, but this is - * good enough for our purpose, and slightly simpler. */ - struct pollfd fds[2]; - fds[0].events = POLLIN; - fds[0].fd = stdout_fd[0]; - fds[1].events = POLLOUT; - fds[1].fd = stdin_fd[1]; - - pid_t wait_pid; - int readlen = 0; - int writelen = 0; - while (1) { - /* Get child status */ - wait_pid = waitpid(pid, NULL, WNOHANG); - /* Check if there is data to read, no matter the process status. */ - /* Timeout after 10ms, or immediately if the process exited already */ - int polln = poll(fds, 2, (wait_pid == pid) ? 0 : 10); - - if (polln < 0) { - syserror("poll error."); - goto error; - } - - log(3, "poll=%d (%d)", polln, (wait_pid == pid)); - - /* We can write something to stdin */ - if (fds[1].revents & POLLOUT) { - int n = write(stdin_fd[1], input+writelen, inlen-writelen); - if (n < 0) { - error("write error."); - goto error; - } - log(3, "write n=%d/%d", n, inlen); - - writelen += n; - if (writelen == inlen) { - /* Done writing: Only poll stdout from now on. */ - close(stdin_fd[1]); - stdin_fd[1] = -1; - fds[1].fd = -1; - } - polln--; - } - - /* We can read something from stdout */ - if (fds[0].revents & POLLIN) { - int n = read(stdout_fd[0], output+readlen, outlen-readlen); - if (n < 0) { - error("read error."); - goto error; - } - log(3, "read n=%d", n); - - readlen += n; - if (readlen >= outlen) { - error("Output too long."); - ret = readlen; - goto error; - } - polln--; - } - - if (polln != 0) { - error("Unknown poll event (%d).", fds[0].revents); - goto error; - } - - if (wait_pid == -1) { - error("waitpid error."); - goto error; - } else if (wait_pid == pid) { - log(3, "child exited!"); - break; - } - } - - if (stdin_fd[1] >= 0) - close(stdin_fd[1]); - close(stdout_fd[0]); - return readlen; - -error: - if (stdin_fd[1] >= 0) - close(stdin_fd[1]); - /* Closing the stdout pipe forces the child process to exit */ - close(stdout_fd[0]); - /* Try to wait 10ms for the process to exit, then bail out. */ - waitpid(pid, NULL, 10); - return ret; -} - /* Open a pipe in non-blocking mode, then set it back to blocking mode. */ /* Returns fd on success, -1 if the pipe cannot be open, -2 if the O_NONBLOCK * flag cannot be cleared. */ @@ -543,305 +313,51 @@ void pipe_init() { pipein_reopen(); } -/**/ -/* Websocket functions. */ -/**/ - -/* Close the client socket, sending a close packet if sendclose is true. */ -static void socket_client_close(int sendclose) { - if (client_fd < 0) - return; - - if (sendclose) { - char buffer[FRAMEMAXHEADERSIZE]; - socket_client_write_frame(buffer, 0, WS_OPCODE_CLOSE, 1); - /* FIXME: We are supposed to read back the answer (if we are not - * replying to a close frame sent by the client), but we probably do not - * want to block, waiting for the answer, so we just close the socket. - */ - } - - close(client_fd); - client_fd = -1; -} - -/* Send a frame to the WebSocket client. - * - buffer needs to be FRAMEMAXHEADERSIZE+size long, and data must start at - * buffer[FRAMEMAXHEADERSIZE] only. - * - opcode should generally be WS_OPCODE_TEXT or WS_OPCODE_CONT (continuation) - * - fin indicates if the this is the last frame in the message - * Returns size on success. On error, closes the socket, and returns -1. - */ -static int socket_client_write_frame(char* buffer, unsigned int size, - unsigned int opcode, int fin) { - /* Start of frame, with header: at least 2 bytes before the actual data */ - char* pbuffer = buffer+FRAMEMAXHEADERSIZE-2; - int payloadlen = size; - int extlensize = 0; - - /* Test if we need an extended length field. */ - if (payloadlen > 125) { - if (payloadlen < 65536) { - payloadlen = 126; - extlensize = 2; - } else { - payloadlen = 127; - extlensize = 8; - } - pbuffer -= extlensize; - - /* Network-order (big-endian) */ - unsigned int tmpsize = size; - int i; - for (i = extlensize-1; i >= 0; i--) { - pbuffer[2+i] = tmpsize & 0xff; - tmpsize >>= 8; - } - } - - pbuffer[0] = opcode & 0x0f; - if (fin) pbuffer[0] |= 0x80; - pbuffer[1] = payloadlen; /* No mask (0x80) in server->client direction */ - - int wlen = 2+extlensize+size; - if (block_write(client_fd, pbuffer, wlen) != wlen) { - syserror("Write error."); - socket_client_close(0); - return -1; - } - - return size; -} - -/* Read a WebSocket frame header: - * - fin indicates in this is the final frame in a fragmented message - * - maskkey is the XOR key used for the message - * - retry is set to 1 if we receive a control packet: the caller must call - * again if it expects more data. - * - * Returns the frame length on success. On error, closes the socket, - * and returns -1. - * - * Data is then read with socket_client_read_frame_data() - */ -static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, - int* retry) { - char header[2]; /* Minimum header length */ - char extlen[8]; /* Extended length */ - int n; - - *retry = 0; - - n = block_read(client_fd, header, 2); - if (n != 2) { - error("Read error."); - socket_client_close(0); - return -1; - } - - int opcode, mask; - uint64_t length; - *fin = (header[0] & 0x80) != 0; - if (header[0] & 0x70) { /* Reserved bits are on */ - error("Reserved bits are on."); - socket_client_close(1); - return -1; - } - opcode = header[0] & 0x0F; - mask = (header[1] & 0x80) != 0; - length = header[1] & 0x7F; - - log(2, "fin=%d; opcode=%d; mask=%d; length=%llu", - *fin, opcode, mask, (long long unsigned int)length); - - /* Read extended length if necessary */ - int extlensize = 0; - if (length == 126) - extlensize = 2; - else if (length == 127) - extlensize = 8; - - if (extlensize > 0) { - n = block_read(client_fd, extlen, extlensize); - if (n != extlensize) { - error("Read error."); - socket_client_close(0); - return -1; - } - - /* Network-order (big-endian) */ - int i; - length = 0; - for (i = 0; i < extlensize; i++) { - length = length << 8 | (uint8_t)extlen[i]; - } - - log(3, "extended length=%llu", (long long unsigned int)length); - } - - /* Read masking key if necessary */ - if (mask) { - n = block_read(client_fd, (char*)maskkey, 4); - if (n != 4) { - error("Read error."); - socket_client_close(0); - return -1; - } - } else { - /* RFC section 5.1 says we must close the connection if we receive a - * frame that is not masked. */ - error("No mask set."); - socket_client_close(1); - return -1; - } - - log(3, "maskkey=%04x", *maskkey); - - if (length > MAXFRAMESIZE) { - error("Frame too big! (%llu>%d)\n", - (long long unsigned int)length, MAXFRAMESIZE); - socket_client_close(1); - return -1; - } - - /* is opcode continuation, text, or binary? */ - /* FIXME: We should check that only the first packet is text or binary, and - * that the following are continuation ones. */ - if (opcode != WS_OPCODE_CONT && - opcode != WS_OPCODE_TEXT && opcode != WS_OPCODE_BINARY) { - log(2, "Got a control packet (opcode=%d).", opcode); - - /* Control packets cannot be fragmented. - * Unknown data (opcodes 3-7) will result in error anyway. */ - if (*fin == 0) { - error("Fragmented unknown packet (%x).", opcode); - socket_client_close(1); - return -1; - } - - /* Read the rest of the packet */ - char* buffer = malloc(length+3); /* +3 for unmasking safety */ - if (socket_client_read_frame_data(buffer, length, *maskkey) < 0) { - socket_client_close(0); - free(buffer); - return -1; - } - - if (opcode == WS_OPCODE_CLOSE) { /* Connection close. */ - error("Connection close from WebSocket client."); - socket_client_close(1); - free(buffer); - return -1; - } else if (opcode == WS_OPCODE_PING) { /* Ping */ - socket_client_write_frame(buffer, length, WS_OPCODE_PONG, 1); - } else if (opcode == WS_OPCODE_PONG) { /* Pong */ - /* Do nothing */ - } else { /* Unknown opcode */ - error("Unknown packet (%x).", opcode); - socket_client_close(1); - free(buffer); - return -1; - } - - free(buffer); - - /* Tell the caller to wait for the next packet */ - *retry = 1; - *fin = 0; - return 0; - } - - return length; -} - -/* Read frame data from the WebSocket client: - * - Make sure that buffer is at least 4*ceil(size/4) long, as unmasking works - * on blocks of 4 bytes. - * Returns size on success (the buffer has been completely filled). - * On error, closes the socket, and returns -1. - */ -static int socket_client_read_frame_data(char* buffer, unsigned int size, - uint32_t maskkey) { - int n = block_read(client_fd, buffer, size); - if (n != size) { - error("Read error."); - socket_client_close(0); - return -1; - } - - if (maskkey != 0) { - int i; - int len32 = (size+3)/4; - uint32_t* buffer32 = (uint32_t*)buffer; - for (i = 0; i < len32; i++) { - buffer32[i] ^= maskkey; - } - } - - return n; -} - /* Unrequested data came in from WebSocket client. */ static void socket_client_read() { char buffer[BUFFERSIZE]; - int length = 0; - int fin = 0; - uint32_t maskkey; - int retry = 0; - int data = 0; /* 1 if we received some valid data */ - - /* Read possible fragmented message into buffer */ - while (fin != 1) { - int curlen = socket_client_read_frame_header(&fin, &maskkey, &retry); - - if (retry) { - if (!data) { - /* We only got a control frame, go back to main loop. We will - * get called again if there is more data waiting. */ - return; - } else { - /* We already read some frames of a fragmented message: wait - * for the rest. */ - continue; - } - } - - if (curlen < 0) { - socket_client_close(0); - return; - } - - if (length+curlen > BUFFERSIZE) { - error("Message too big (%d>%d).", length+curlen, BUFFERSIZE); - socket_client_close(1); - return; - } - - if (socket_client_read_frame_data(buffer+length, curlen, maskkey) < 0) { - error("Read error."); - socket_client_close(0); - return; - } + int length; - length += curlen; - data = 1; + length = socket_client_read_frame(buffer, sizeof(buffer)); + if (length < 0) { + socket_client_close(1); + return; } /* Process the client request. */ buffer[length == BUFFERSIZE ? BUFFERSIZE-1 : length] = 0; switch (buffer[0]) { - case 'C': /* Send a command to croutoncycle and get the response */ + case 'C': /* Send a command to croutoncycle */ log(2, "Received croutoncycle command (%s)", &buffer[1]); - length = popen2("croutoncycle", &buffer[1], "", 0, - &buffer[FRAMEMAXHEADERSIZE+1], - BUFFERSIZE-FRAMEMAXHEADERSIZE-1); - if (length == -1) { - error("Call to croutoncycle failed."); - socket_client_close(0); - return; - } + char* cmd = "croutoncycle"; + char* args[] = { cmd, &buffer[1], NULL }; + buffer[FRAMEMAXHEADERSIZE] = 'C'; - length++; + /* We are only interested in the output for list commands */ + if (buffer[1] == 'l') { + length = popen2(cmd, args, NULL, 0, + &buffer[FRAMEMAXHEADERSIZE+1], + BUFFERSIZE-FRAMEMAXHEADERSIZE-1); + if (length == -1) { + error("Call to croutoncycle failed."); + socket_client_close(0); + return; + } + length++; + } else { + /* Launch command in background (this is necessary as + croutoncycle may send a websocket command) */ + pid_t pid = fork(); + if (pid < 0) { + syserror("Fork error."); + exit(1); + } else if (pid == 0) { + execvp(cmd, args); + error("Error running '%s'.", cmd); + exit(127); + } + length = 1; + } if (socket_client_write_frame(buffer, length, WS_OPCODE_TEXT, 1) < 0) { error("Write error."); @@ -856,392 +372,6 @@ static void socket_client_read() { } } -/* Send a version packet to the extension, and read VOK reply. */ -static void socket_client_sendversion() { - char* version = "V"VERSION; - int versionlen = strlen(version); - char* outbuf = malloc(FRAMEMAXHEADERSIZE+versionlen); - memcpy(outbuf+FRAMEMAXHEADERSIZE, version, versionlen); - - log(2, "Sending version packet (%s).", version); - - if (socket_client_write_frame(outbuf, versionlen, WS_OPCODE_TEXT, 1) < 0) { - error("Write error."); - socket_client_close(0); - free(outbuf); - return; - } - free(outbuf); - - /* Read response back */ - char buffer[256]; - int buflen = 0; - int fin = 0; - uint32_t maskkey; - int retry = 0; - - /* Read possibly fragmented message from WebSocket. */ - while (fin != 1) { - int len = socket_client_read_frame_header(&fin, &maskkey, &retry); - - if (retry) - continue; - - if (len < 0) - break; - - if (len+buflen > 256) { - error("Response too long: (>%d bytes).", 256); - socket_client_close(1); - return; - } - - if (socket_client_read_frame_data(buffer+buflen, len, maskkey) < 0) { - socket_client_close(0); - return; - } - buflen += len; - } - - buffer[buflen == 256 ? 255 : buflen] = 0; - if (buflen != 3 || strcmp(buffer, "VOK")) { - int i; - for (i = 0; i < buflen; i++) { - if (!isprint(buffer[i])) - buffer[i] = '?'; - } - error("Invalid response: %s.", buffer); - socket_client_close(1); - return; - } - - log(2, "Received VOK."); -} - -/* Bitmask indicating if we received everything we need in the header */ -const int OK_GET = 0x01; /* GET {PATH} HTTP/1.1 */ -const int OK_GET_PATH = 0x02; /* {PATH} == / in GET request */ -const int OK_UPGRADE = 0x04; /* Upgrade: websocket */ -const int OK_CONNECTION = 0x08; /* Connection: Upgrade */ -const int OK_SEC_VERSION = 0x10; /* Sec-WebSocket-Version: {VERSION} */ -const int OK_VERSION = 0x20; /* {VERSION} == 13 */ -const int OK_SEC_KEY = 0x40; /* Sec-WebSocket-Key: 24 bytes */ -const int OK_HOST = 0x80; /* Host: localhost:PORT */ -const int OK_ALL = 0xFF; /* Final correct value is 0xFF */ - -/* Send an error on a new client socket, then close the socket. */ -static void socket_server_error(int newclient_fd, int ok) { - /* Values found only in WebSocket header */ - const int OK_WEBSOCKET = OK_UPGRADE|OK_CONNECTION|OK_SEC_VERSION| - OK_VERSION|OK_SEC_KEY; - /* Values found in WebSocket header of a possibly wrong version */ - const int OK_OTHER_VERSION = OK_GET|OK_UPGRADE|OK_CONNECTION|OK_SEC_VERSION; - - char buffer[BUFFERSIZE]; - - if ((ok & OK_GET) && - (!(ok & OK_GET_PATH) || !(ok & OK_WEBSOCKET))) { - /* Path is not /, or / but clearly not a WebSocket handshake: 404 */ - strncpy(buffer, - "HTTP/1.1 404 Not Found\r\n" - "\r\n" - "

404 Not Found

", BUFFERSIZE); - } else if ((ok & OK_OTHER_VERSION) == OK_OTHER_VERSION && - !(ok & OK_VERSION)) { - /* We received something that looks like a WebSocket handshake, - * but wrong version */ - strncpy(buffer, - "HTTP/1.1 400 Bad Request\r\n" - "Sec-WebSocket-Version: 13\r\n" - "\r\n", BUFFERSIZE); - } else { - /* Generic answer */ - strncpy(buffer, - "HTTP/1.1 400 Bad Request\r\n" - "\r\n" - "

400 Bad Request

", BUFFERSIZE); - } - - log(3, "answer:\n%s===", buffer); - - /* Ignore errors */ - block_write(newclient_fd, buffer, strlen(buffer)); - - close(newclient_fd); -} - -/* Read and parse HTTP header. - * Returns 0 if the header is valid. websocket_key must be at least SECKEY_LEN - * bytes long, and contains the value of Sec-WebSocket-Key on success. - * Returns < 0 in case of error: in that case newclient_fd is closed. - */ -static int socket_server_read_header(int newclient_fd, char* websocket_key) { - int first = 1; - char buffer[BUFFERSIZE]; - int ok = 0x00; - - char* pbuffer = buffer; - int n = read(newclient_fd, buffer, BUFFERSIZE); - if (n <= 0) { - syserror("Cannot read from client."); - close(newclient_fd); - return -1; - } - - while (1) { - /* Start of current line (until ':' for key-value pairs) */ - char* key = pbuffer; - /* Start of value in current line (part after ': '). */ - char* value = NULL; - - /* Read a line of header, splitting key-value pairs if possible. */ - while (1) { - if (n == 0) { - /* No more data in buffer: shift data so that key == buffer, - * and try reading again. */ - memmove(buffer, key, pbuffer-key); - if (value) - value -= (key-buffer); - pbuffer -= (key-buffer); - key = buffer; - - n = read(newclient_fd, pbuffer, BUFFERSIZE-(pbuffer-buffer)); - if (n <= 0) { - syserror("Cannot read from client."); - close(newclient_fd); - return -1; - } - } - - /* Detect new line: - * HTTP RFC says it must be CRLF, but we accept LF. */ - if (*pbuffer == '\n') { - if (*(pbuffer-1) == '\r') - *(pbuffer-1) = '\0'; - else - *pbuffer = '\0'; - n--; pbuffer++; - break; - } - - /* Detect "Key: Value" pairs, on all lines but the first one. */ - if (!first && !value && *pbuffer == ':') { - value = pbuffer+2; - *pbuffer = '\0'; - } - - n--; pbuffer++; - } - - log(3, "HTTP header: key=%s; value=%s.", key, value); - - /* Empty line indicates end of header. */ - if (strlen(key) == 0 && !value) - break; - - if (first) { /* Normally GET / HTTP/1.1 */ - first = 0; - - char* tok = strtok(key, " "); - if (!tok || strcmp(tok, "GET")) { - error("Invalid HTTP method (%s).", tok); - continue; - } - - tok = strtok(NULL, " "); - if (!tok || strcmp(tok, "/")) { - error("Invalid path (%s).", tok); - } else { - ok |= OK_GET_PATH; - } - - tok = strtok(NULL, " "); - if (!tok || strcmp(tok, "HTTP/1.1")) { - error("Invalid HTTP version (%s).", tok); - continue; - } - - ok |= OK_GET; - } else { - if (!value) { - error("Invalid HTTP header (%s).", key); - socket_server_error(newclient_fd, 0x00); - return -1; - } - - if (!strcmp(key, "Upgrade") && !strcmp(value, "websocket")) { - ok |= OK_UPGRADE; - } else if (!strcmp(key, "Connection") && - !strcmp(value, "Upgrade")) { - ok |= OK_CONNECTION; - } else if (!strcmp(key, "Sec-WebSocket-Version")) { - ok |= OK_SEC_VERSION; - if (strcmp(value, "13")) { - error("Invalid Sec-WebSocket-Version: '%s'.", value); - continue; - } - ok |= OK_VERSION; - } else if (!strcmp(key, "Sec-WebSocket-Key")) { - if (strlen(value) != SECKEY_LEN) { - error("Invalid Sec-WebSocket-Key: '%s'.", value); - continue; - } - memcpy(websocket_key, value, SECKEY_LEN); - ok |= OK_SEC_KEY; - } else if (!strcmp(key, "Host")) { - char strbuf[32]; - snprintf(strbuf, 32, "localhost:%d", PORT); - - if (strcmp(value, strbuf)) { - error("Invalid Host field: '%s'.", value); - continue; - } - ok |= OK_HOST; - } - } - } - - if (ok != OK_ALL) { - error("Some WebSocket headers missing (%x).", ~ok & OK_ALL); - socket_server_error(newclient_fd, ok); - return -1; - } - - return 0; -} - -/* Accept a new client connection on the server socket. */ -static void socket_server_accept() { - int newclient_fd; - struct sockaddr_in client_addr; - unsigned int client_addr_len = sizeof(client_addr); - char buffer[BUFFERSIZE]; - - newclient_fd = accept(server_fd, - (struct sockaddr*)&client_addr, &client_addr_len); - - if (newclient_fd < 0) { - syserror("Error accepting new connection."); - return; - } - - /* key from client + GUID */ - int websocket_keylen = SECKEY_LEN+strlen(GUID); - char websocket_key[websocket_keylen]; - - /* Read and parse HTTP header */ - if (socket_server_read_header(newclient_fd, websocket_key) < 0) { - return; - } - - log(1, "Header read successfully."); - - /* Compute sha1+base64 response (RFC section 4.2.2, paragraph 5.4) */ - - char sha1[SHA1_LEN]; - - /* Some margin so we can read the full output of base64 */ - int b64_len = SHA1_BASE64_LEN+4; - char b64[b64_len]; - int i; - - memcpy(websocket_key+SECKEY_LEN, GUID, strlen(GUID)); - - /* SHA-1 is 20 bytes long (40 characters in hex form) */ - if (popen2("sha1sum", NULL, websocket_key, websocket_keylen, - buffer, BUFFERSIZE) < 2*SHA1_LEN) { - error("sha1sum response too short."); - exit(1); - } - - for (i = 0; i < SHA1_LEN; i++) { - unsigned int value; - if (sscanf(&buffer[i*2], "%02x", &value) != 1) { - buffer[2*SHA1_LEN] = 0; - error("Cannot read SHA-1 sum (%s).", buffer); - exit(1); - } - sha1[i] = (char)value; - } - - /* base64 encoding of SHA1_LEN bytes must be SHA1_BASE64_LEN bytes long. - * Either the output is exactly SHA1_BASE64_LEN long, or the last character - * is a line feed (RFC 3548 forbids other characters in output) */ - int n = popen2("base64", NULL, sha1, SHA1_LEN, b64, b64_len); - if (n < SHA1_BASE64_LEN || - (n != SHA1_BASE64_LEN && b64[SHA1_BASE64_LEN] != '\r' && - b64[SHA1_BASE64_LEN] != '\n')) { - error("Invalid base64 response."); - exit(1); - } - b64[SHA1_BASE64_LEN] = '\0'; - - int len = snprintf(buffer, BUFFERSIZE, - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n" - "\r\n", b64); - - if (len == BUFFERSIZE) { - error("Response length > %d.", BUFFERSIZE); - exit(1); - } - - log(3, "HTTP response:\n%s===", buffer); - - if (block_write(newclient_fd, buffer, len) != len) { - syserror("Cannot write response."); - close(newclient_fd); - return; - } - - log(2, "Response sent."); - - /* Close existing connection, if any. */ - if (client_fd >= 0) - socket_client_close(1); - - client_fd = newclient_fd; - - socket_client_sendversion(); - - return; -} - -/* Initialise WebSocket server */ -static void socket_server_init() { - struct sockaddr_in server_addr; - int optval; - - server_fd = socket(AF_INET, SOCK_STREAM, 0); - if (server_fd < 0) { - syserror("Cannot create server socket."); - exit(1); - } - - /* SO_REUSEADDR to make sure the server can restart after a crash. */ - optval = 1; - setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); - - /* Listen on loopback interface, port PORT. */ - memset(&server_addr, 0, sizeof(server_addr)); - server_addr.sin_family = AF_INET; - server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - server_addr.sin_port = htons(PORT); - - if (bind(server_fd, - (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { - syserror("Cannot bind server socket."); - exit(1); - } - - if (listen(server_fd, 5) < 0) { - syserror("Cannot listen on server socket."); - exit(1); - } -} - static int terminate = 0; static void signal_handler(int sig) { @@ -1313,7 +443,7 @@ int main(int argc, char **argv) { fds[2].events = POLLIN; /* Initialise pipe and WebSocket server */ - socket_server_init(); + socket_server_init(PORT); pipe_init(); while (!terminate) { @@ -1338,7 +468,7 @@ int main(int argc, char **argv) { if (fds[0].revents & POLLIN) { log(1, "WebSocket accept."); - socket_server_accept(); + socket_server_accept(VERSION); n--; } if (fds[1].revents & POLLIN) { diff --git a/src/websocket.h b/src/websocket.h new file mode 100644 index 000000000..0ed24a438 --- /dev/null +++ b/src/websocket.h @@ -0,0 +1,894 @@ +/* Copyright (c) 2014 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Provides common WebSocket functions that can be used by both websocket.c + * and fbserver.c. + * + * Mostly compliant with RFC 6455 - The WebSocket Protocol. + * + * Things that are supported, but not tested: + * - Fragmented packets from client + * - Ping packets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int BUFFERSIZE = 4096; + +/* WebSocket constants */ +const int FRAMEMAXHEADERSIZE = 16; // Actually 2+8, but align on 8-byte boundary +const int MAXFRAMESIZE = 16*1048576; // 16MiB +const char* GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +/* Key from client must be 24 bytes long (16 bytes, base64 encoded) */ +const int SECKEY_LEN = 24; +/* SHA-1 is 20 bytes long */ +const int SHA1_LEN = 20; +/* base64-encoded SHA-1 must be 28 bytes long (ceil(20/3*4)+1). */ +const int SHA1_BASE64_LEN = 28; + +/* WebSocket opcodes */ +const int WS_OPCODE_CONT = 0x0; +const int WS_OPCODE_TEXT = 0x1; +const int WS_OPCODE_BINARY = 0x2; +const int WS_OPCODE_CLOSE = 0x8; +const int WS_OPCODE_PING = 0x9; +const int WS_OPCODE_PONG = 0xA; + +/* 0 - Quiet + * 1 - General messages (init, new connections) + * 2 - 1 + Information on each transfer + * 3 - 2 + Extra information */ +static int verbose = 0; + +#define log(level, str, ...) do { \ + if (verbose >= (level)) printf("%s: " str "\n", __func__, ##__VA_ARGS__); \ +} while (0) + +#define error(str, ...) printf("%s: " str "\n", __func__, ##__VA_ARGS__) + +#define trueorabort(expr, str, ...) do { \ + if (!(expr)) { \ + printf("%s: " str "\n", __func__, ##__VA_ARGS__); \ + abort(); \ + } \ +} while (0) + +/* Similar to perror, but prints function name as well */ +#define syserror(str, ...) printf("%s: " str " (%s)\n", \ + __func__, ##__VA_ARGS__, strerror(errno)) + +/* Port number, assigned in socket_server_init() */ +static int port = -1; + +/* File descriptors */ +static int server_fd = -1; +static int client_fd = -1; + +/* Prototypes */ +static int socket_client_write_frame(char* buffer, unsigned int size, + unsigned int opcode, int fin); +static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, + int* length); +static int socket_client_read_frame_data(char* buffer, unsigned int size, + uint32_t maskkey); +static void socket_client_close(int close_reason); + +/**/ +/* Helper functions */ +/**/ + +/* Read exactly size bytes from fd, no matter how many reads it takes. + * Returns size if successful, < 0 in case of error. */ +static int block_read(int fd, char* buffer, size_t size) { + int n; + int tot = 0; + + while (tot < size) { + n = read(fd, buffer+tot, size-tot); + log(3, "n=%d+%d/%zd", n, tot, size); + if (n < 0) + return n; + if (n == 0) + return -1; /* EOF */ + tot += n; + } + + return tot; +} + +/* Write exactly size bytes from fd, no matter how many writes it takes. + * Returns size if successful, < 0 in case of error. */ +static int block_write(int fd, char* buffer, size_t size) { + int n; + int tot = 0; + + while (tot < size) { + n = write(fd, buffer+tot, size-tot); + log(3, "n=%d+%d/%zd", n, tot, size); + if (n < 0) + return n; + if (n == 0) + return -1; + tot += n; + } + + return tot; +} + +/* Run external command, piping some data on its stdin, and reading back + * the output. Returns the number of bytes read from the process (at most + * outlen), or -1 on error. */ +static int popen2(char* cmd, char *const argv[], + char* input, int inlen, char* output, int outlen) { + pid_t pid = 0; + int stdin_fd[2]; + int stdout_fd[2]; + int ret = -1; + + if (pipe(stdin_fd) < 0 || pipe(stdout_fd) < 0) { + syserror("Failed to create pipe."); + return -1; + } + + pid = fork(); + + if (pid < 0) { + syserror("Fork error."); + return -1; + } else if (pid == 0) { + /* Child: connect stdin/out to the pipes, close the unneeded halves */ + close(stdin_fd[1]); + dup2(stdin_fd[0], STDIN_FILENO); + close(stdout_fd[0]); + dup2(stdout_fd[1], STDOUT_FILENO); + + if (argv) { + execvp(cmd, argv); + } else { + execlp(cmd, cmd, NULL); + } + + error("Error running '%s'.", cmd); + exit(1); + } + + /* Parent */ + + /* Write input, and read output, while waiting for process termination. + * This could be done without polling, by reacting on SIGCHLD, but this is + * good enough for our purpose, and slightly simpler. */ + struct pollfd fds[2]; + fds[0].events = POLLIN; + fds[0].fd = stdout_fd[0]; + fds[1].events = POLLOUT; + fds[1].fd = stdin_fd[1]; + + pid_t wait_pid; + int readlen = 0; + int writelen = 0; + while (1) { + /* Get child status */ + wait_pid = waitpid(pid, NULL, WNOHANG); + /* Check if there is data to read, no matter the process status. */ + /* Timeout after 10ms, or immediately if the process exited already */ + int polln = poll(fds, 2, (wait_pid == pid) ? 0 : 10); + + if (polln < 0) { + syserror("poll error."); + goto error; + } + + log(3, "poll=%d (%d)", polln, (wait_pid == pid)); + + /* We can write something to stdin */ + if (fds[1].revents & POLLOUT) { + if (inlen > writelen) { + int n = write(stdin_fd[1], input+writelen, inlen-writelen); + if (n < 0) { + error("write error."); + goto error; + } + log(3, "write n=%d/%d", n, inlen); + writelen += n; + } + + if (writelen == inlen) { + /* Done writing: Only poll stdout from now on. */ + close(stdin_fd[1]); + stdin_fd[1] = -1; + fds[1].fd = -1; + } + polln--; + } + + /* We can read something from stdout */ + if (fds[0].revents & POLLIN) { + int n = read(stdout_fd[0], output+readlen, outlen-readlen); + if (n < 0) { + error("read error."); + goto error; + } + log(3, "read n=%d", n); + + readlen += n; + if (readlen >= outlen) { + error("Output too long."); + ret = readlen; + goto error; + } + polln--; + } + + if (polln != 0) { + error("Unknown poll event (%d).", fds[0].revents); + goto error; + } + + if (wait_pid == -1) { + error("waitpid error."); + goto error; + } else if (wait_pid == pid) { + log(3, "child exited!"); + break; + } + } + + if (stdin_fd[1] >= 0) + close(stdin_fd[1]); + close(stdout_fd[0]); + return readlen; + +error: + if (stdin_fd[1] >= 0) + close(stdin_fd[1]); + /* Closing the stdout pipe forces the child process to exit */ + close(stdout_fd[0]); + /* Try to wait 10ms for the process to exit, then bail out. */ + waitpid(pid, NULL, 10); + return ret; +} + +/**/ +/* Websocket functions. */ +/**/ + +/* Close the client socket, sending a close packet if sendclose is true. */ +static void socket_client_close(int sendclose) { + if (client_fd < 0) + return; + + if (sendclose) { + char buffer[FRAMEMAXHEADERSIZE]; + socket_client_write_frame(buffer, 0, WS_OPCODE_CLOSE, 1); + /* FIXME: We are supposed to read back the answer (if we are not + * replying to a close frame sent by the client), but we probably do not + * want to block, waiting for the answer, so we just close the socket. + */ + } + + close(client_fd); + client_fd = -1; +} + +/* Send a frame to the WebSocket client. + * - buffer needs to be FRAMEMAXHEADERSIZE+size long, and data must start at + * buffer[FRAMEMAXHEADERSIZE] only. + * - opcode should generally be WS_OPCODE_TEXT or WS_OPCODE_CONT (continuation) + * - fin indicates if the this is the last frame in the message + * Returns size on success. On error, closes the socket, and returns -1. + */ +static int socket_client_write_frame(char* buffer, unsigned int size, + unsigned int opcode, int fin) { + /* Start of frame, with header: at least 2 bytes before the actual data */ + char* pbuffer = buffer+FRAMEMAXHEADERSIZE-2; + int payloadlen = size; + int extlensize = 0; + + /* Test if we need an extended length field. */ + if (payloadlen > 125) { + if (payloadlen < 65536) { + payloadlen = 126; + extlensize = 2; + } else { + payloadlen = 127; + extlensize = 8; + } + pbuffer -= extlensize; + + /* Network-order (big-endian) */ + unsigned int tmpsize = size; + int i; + for (i = extlensize-1; i >= 0; i--) { + pbuffer[2+i] = tmpsize & 0xff; + tmpsize >>= 8; + } + } + + pbuffer[0] = opcode & 0x0f; + if (fin) pbuffer[0] |= 0x80; + pbuffer[1] = payloadlen; /* No mask (0x80) in server->client direction */ + + int wlen = 2+extlensize+size; + if (block_write(client_fd, pbuffer, wlen) != wlen) { + syserror("Write error."); + socket_client_close(0); + return -1; + } + + return size; +} + +/* Read a WebSocket frame header: + * - fin indicates in this is the final frame in a fragmented message + * - maskkey is the XOR key used for the message + * - retry is set to 1 if we receive a control packet: the caller must call + * again if it expects more data. + * + * Returns the frame length on success. On error, closes the socket, + * and returns -1. + * + * Data is then read with socket_client_read_frame_data() + */ +static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, + int* retry) { + char header[2]; /* Minimum header length */ + char extlen[8]; /* Extended length */ + int n; + + *retry = 0; + + n = block_read(client_fd, header, 2); + if (n != 2) { + error("Read error."); + socket_client_close(0); + return -1; + } + + int opcode, mask; + uint64_t length; + *fin = (header[0] & 0x80) != 0; + if (header[0] & 0x70) { /* Reserved bits are on */ + error("Reserved bits are on."); + socket_client_close(1); + return -1; + } + opcode = header[0] & 0x0F; + mask = (header[1] & 0x80) != 0; + length = header[1] & 0x7F; + + log(2, "fin=%d; opcode=%d; mask=%d; length=%llu", + *fin, opcode, mask, (long long unsigned int)length); + + /* Read extended length if necessary */ + int extlensize = 0; + if (length == 126) + extlensize = 2; + else if (length == 127) + extlensize = 8; + + if (extlensize > 0) { + n = block_read(client_fd, extlen, extlensize); + if (n != extlensize) { + error("Read error."); + socket_client_close(0); + return -1; + } + + /* Network-order (big-endian) */ + int i; + length = 0; + for (i = 0; i < extlensize; i++) { + length = length << 8 | (uint8_t)extlen[i]; + } + + log(3, "extended length=%llu", (long long unsigned int)length); + } + + /* Read masking key if necessary */ + if (mask) { + n = block_read(client_fd, (char*)maskkey, 4); + if (n != 4) { + error("Read error."); + socket_client_close(0); + return -1; + } + } else { + /* RFC section 5.1 says we must close the connection if we receive a + * frame that is not masked. */ + error("No mask set."); + socket_client_close(1); + return -1; + } + + log(3, "maskkey=%04x", *maskkey); + + if (length > MAXFRAMESIZE) { + error("Frame too big! (%llu>%d)\n", + (long long unsigned int)length, MAXFRAMESIZE); + socket_client_close(1); + return -1; + } + + /* is opcode continuation, text, or binary? */ + /* FIXME: We should check that only the first packet is text or binary, and + * that the following are continuation ones. */ + if (opcode != WS_OPCODE_CONT && + opcode != WS_OPCODE_TEXT && opcode != WS_OPCODE_BINARY) { + log(2, "Got a control packet (opcode=%d).", opcode); + + /* Control packets cannot be fragmented. + * Unknown data (opcodes 3-7) will result in error anyway. */ + if (*fin == 0) { + error("Fragmented unknown packet (%x).", opcode); + socket_client_close(1); + return -1; + } + + /* Read the rest of the packet */ + char* buffer = malloc(length+3); /* +3 for unmasking safety */ + if (socket_client_read_frame_data(buffer, length, *maskkey) < 0) { + socket_client_close(0); + free(buffer); + return -1; + } + + if (opcode == WS_OPCODE_CLOSE) { /* Connection close. */ + error("Connection close from WebSocket client."); + socket_client_close(1); + free(buffer); + return -1; + } else if (opcode == WS_OPCODE_PING) { /* Ping */ + socket_client_write_frame(buffer, length, WS_OPCODE_PONG, 1); + } else if (opcode == WS_OPCODE_PONG) { /* Pong */ + /* Do nothing */ + } else { /* Unknown opcode */ + error("Unknown packet (%x).", opcode); + socket_client_close(1); + free(buffer); + return -1; + } + + free(buffer); + + /* Tell the caller to wait for the next packet */ + *retry = 1; + *fin = 0; + return 0; + } + + return length; +} + +/* Read frame data from the WebSocket client: + * - Make sure that buffer is at least 4*ceil(size/4) long, as unmasking works + * on blocks of 4 bytes. + * Returns size on success (the buffer has been completely filled). + * On error, closes the socket, and returns -1. + */ +static int socket_client_read_frame_data(char* buffer, unsigned int size, + uint32_t maskkey) { + int n = block_read(client_fd, buffer, size); + if (n != size) { + error("Read error."); + socket_client_close(0); + return -1; + } + + if (maskkey != 0) { + int i; + int len32 = (size+3)/4; + uint32_t* buffer32 = (uint32_t*)buffer; + for (i = 0; i < len32; i++) { + buffer32[i] ^= maskkey; + } + } + + return n; +} + +/* Read a complete frame from the WebSocket client: + * - Make sure that buffer size is a multiple of 4 (for unmasking). + * Returns packet size on success. + * On error (e.g. packet too large for buffer), closes the socket, and + * returns -1. + */ +static int socket_client_read_frame(char* buffer, int size) { + int buflen = 0; + int fin = 0; + uint32_t maskkey; + int retry = 0; + + /* Read possibly fragmented message from WebSocket. */ + while (fin != 1) { + int len = socket_client_read_frame_header(&fin, &maskkey, &retry); + + if (retry) + continue; + + if (len < 0) + return -1; + + if (len+buflen > size) { + error("Response too long: (>%d bytes).", size); + socket_client_close(1); + return -1; + } + + if (socket_client_read_frame_data(buffer+buflen, len, maskkey) < 0) { + socket_client_close(0); + return -1; + } + buflen += len; + } + + return buflen; +} + +/* Send a version packet to the extension, and read VOK reply. */ +static int socket_client_sendversion(char* version) { + int versionlen = strlen(version); + char* outbuf = malloc(FRAMEMAXHEADERSIZE+versionlen); + memcpy(outbuf+FRAMEMAXHEADERSIZE, version, versionlen); + + log(2, "Sending version packet (%s).", version); + + if (socket_client_write_frame(outbuf, versionlen, WS_OPCODE_TEXT, 1) < 0) { + error("Write error."); + socket_client_close(0); + free(outbuf); + return -1; + } + free(outbuf); + + /* Read response back */ + char buffer[256]; + int buflen = socket_client_read_frame(buffer, sizeof(buffer)); + + buffer[buflen == 256 ? 255 : buflen] = 0; + if (buflen != 3 || strcmp(buffer, "VOK")) { + int i; + for (i = 0; i < buflen; i++) { + if (!isprint(buffer[i])) + buffer[i] = '?'; + } + error("Invalid response: %s.", buffer); + socket_client_close(1); + return -1; + } + + log(2, "Received VOK."); + return 0; +} + +/* Bitmask indicating if we received everything we need in the header */ +const int OK_GET = 0x01; /* GET {PATH} HTTP/1.1 */ +const int OK_GET_PATH = 0x02; /* {PATH} == / in GET request */ +const int OK_UPGRADE = 0x04; /* Upgrade: websocket */ +const int OK_CONNECTION = 0x08; /* Connection: Upgrade */ +const int OK_SEC_VERSION = 0x10; /* Sec-WebSocket-Version: {VERSION} */ +const int OK_VERSION = 0x20; /* {VERSION} == 13 */ +const int OK_SEC_KEY = 0x40; /* Sec-WebSocket-Key: 24 bytes */ +const int OK_HOST = 0x80; /* Host: localhost:PORT */ +const int OK_ALL = 0xFF; /* Final correct value is 0xFF */ + +/* Send an error on a new client socket, then close the socket. */ +static void socket_server_error(int newclient_fd, int ok) { + /* Values found only in WebSocket header */ + const int OK_WEBSOCKET = OK_UPGRADE|OK_CONNECTION|OK_SEC_VERSION| + OK_VERSION|OK_SEC_KEY; + /* Values found in WebSocket header of a possibly wrong version */ + const int OK_OTHER_VERSION = OK_GET|OK_UPGRADE|OK_CONNECTION|OK_SEC_VERSION; + + char buffer[BUFFERSIZE]; + + if ((ok & OK_GET) && + (!(ok & OK_GET_PATH) || !(ok & OK_WEBSOCKET))) { + /* Path is not /, or / but clearly not a WebSocket handshake: 404 */ + strncpy(buffer, + "HTTP/1.1 404 Not Found\r\n" + "\r\n" + "

404 Not Found

", BUFFERSIZE); + } else if ((ok & OK_OTHER_VERSION) == OK_OTHER_VERSION && + !(ok & OK_VERSION)) { + /* We received something that looks like a WebSocket handshake, + * but wrong version */ + strncpy(buffer, + "HTTP/1.1 400 Bad Request\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n", BUFFERSIZE); + } else { + /* Generic answer */ + strncpy(buffer, + "HTTP/1.1 400 Bad Request\r\n" + "\r\n" + "

400 Bad Request

", BUFFERSIZE); + } + + log(3, "answer:\n%s===", buffer); + + /* Ignore errors */ + block_write(newclient_fd, buffer, strlen(buffer)); + + close(newclient_fd); +} + +/* Read and parse HTTP header. + * Returns 0 if the header is valid. websocket_key must be at least SECKEY_LEN + * bytes long, and contains the value of Sec-WebSocket-Key on success. + * Returns < 0 in case of error: in that case newclient_fd is closed. + */ +static int socket_server_read_header(int newclient_fd, char* websocket_key) { + int first = 1; + char buffer[BUFFERSIZE]; + int ok = 0x00; + + char* pbuffer = buffer; + int n = read(newclient_fd, buffer, BUFFERSIZE); + if (n <= 0) { + syserror("Cannot read from client."); + close(newclient_fd); + return -1; + } + + while (1) { + /* Start of current line (until ':' for key-value pairs) */ + char* key = pbuffer; + /* Start of value in current line (part after ': '). */ + char* value = NULL; + + /* Read a line of header, splitting key-value pairs if possible. */ + while (1) { + if (n == 0) { + /* No more data in buffer: shift data so that key == buffer, + * and try reading again. */ + memmove(buffer, key, pbuffer-key); + if (value) + value -= (key-buffer); + pbuffer -= (key-buffer); + key = buffer; + + n = read(newclient_fd, pbuffer, BUFFERSIZE-(pbuffer-buffer)); + if (n <= 0) { + syserror("Cannot read from client."); + close(newclient_fd); + return -1; + } + } + + /* Detect new line: + * HTTP RFC says it must be CRLF, but we accept LF. */ + if (*pbuffer == '\n') { + if (*(pbuffer-1) == '\r') + *(pbuffer-1) = '\0'; + else + *pbuffer = '\0'; + n--; pbuffer++; + break; + } + + /* Detect "Key: Value" pairs, on all lines but the first one. */ + if (!first && !value && *pbuffer == ':') { + value = pbuffer+2; + *pbuffer = '\0'; + } + + n--; pbuffer++; + } + + log(3, "HTTP header: key=%s; value=%s.", key, value); + + /* Empty line indicates end of header. */ + if (strlen(key) == 0 && !value) + break; + + if (first) { /* Normally GET / HTTP/1.1 */ + first = 0; + + char* tok = strtok(key, " "); + if (!tok || strcmp(tok, "GET")) { + error("Invalid HTTP method (%s).", tok); + continue; + } + + tok = strtok(NULL, " "); + if (!tok || strcmp(tok, "/")) { + error("Invalid path (%s).", tok); + } else { + ok |= OK_GET_PATH; + } + + tok = strtok(NULL, " "); + if (!tok || strcmp(tok, "HTTP/1.1")) { + error("Invalid HTTP version (%s).", tok); + continue; + } + + ok |= OK_GET; + } else { + if (!value) { + error("Invalid HTTP header (%s).", key); + socket_server_error(newclient_fd, 0x00); + return -1; + } + + if (!strcmp(key, "Upgrade") && !strcmp(value, "websocket")) { + ok |= OK_UPGRADE; + } else if (!strcmp(key, "Connection") && + !strcmp(value, "Upgrade")) { + ok |= OK_CONNECTION; + } else if (!strcmp(key, "Sec-WebSocket-Version")) { + ok |= OK_SEC_VERSION; + if (strcmp(value, "13")) { + error("Invalid Sec-WebSocket-Version: '%s'.", value); + continue; + } + ok |= OK_VERSION; + } else if (!strcmp(key, "Sec-WebSocket-Key")) { + if (strlen(value) != SECKEY_LEN) { + error("Invalid Sec-WebSocket-Key: '%s'.", value); + continue; + } + memcpy(websocket_key, value, SECKEY_LEN); + ok |= OK_SEC_KEY; + } else if (!strcmp(key, "Host")) { + char strbuf[32]; + snprintf(strbuf, 32, "localhost:%d", port); + + if (strcmp(value, strbuf)) { + error("Invalid Host field: '%s'.", value); + continue; + } + ok |= OK_HOST; + } + } + } + + if (ok != OK_ALL) { + error("Some WebSocket headers missing (%x).", ~ok & OK_ALL); + socket_server_error(newclient_fd, ok); + return -1; + } + + return 0; +} + +/* Accept a new client connection on the server socket. */ +static int socket_server_accept(char* version) { + int newclient_fd; + struct sockaddr_in client_addr; + unsigned int client_addr_len = sizeof(client_addr); + char buffer[BUFFERSIZE]; + + newclient_fd = accept(server_fd, + (struct sockaddr*)&client_addr, &client_addr_len); + + if (newclient_fd < 0) { + syserror("Error accepting new connection."); + return -1; + } + + /* key from client + GUID */ + int websocket_keylen = SECKEY_LEN+strlen(GUID); + char websocket_key[websocket_keylen]; + + /* Read and parse HTTP header */ + if (socket_server_read_header(newclient_fd, websocket_key) < 0) { + return -1; + } + + log(1, "Header read successfully."); + + /* Compute sha1+base64 response (RFC section 4.2.2, paragraph 5.4) */ + + char sha1[SHA1_LEN]; + + /* Some margin so we can read the full output of base64 */ + int b64_len = SHA1_BASE64_LEN+4; + char b64[b64_len]; + int i; + + memcpy(websocket_key+SECKEY_LEN, GUID, strlen(GUID)); + + /* SHA-1 is 20 bytes long (40 characters in hex form) */ + if (popen2("sha1sum", NULL, websocket_key, websocket_keylen, + buffer, BUFFERSIZE) < 2*SHA1_LEN) { + error("sha1sum response too short."); + exit(1); + } + + for (i = 0; i < SHA1_LEN; i++) { + unsigned int value; + if (sscanf(&buffer[i*2], "%02x", &value) != 1) { + buffer[2*SHA1_LEN] = 0; + error("Cannot read SHA-1 sum (%s).", buffer); + exit(1); + } + sha1[i] = (char)value; + } + + /* base64 encoding of SHA1_LEN bytes must be SHA1_BASE64_LEN bytes long. + * Either the output is exactly SHA1_BASE64_LEN long, or the last character + * is a line feed (RFC 3548 forbids other characters in output) */ + int n = popen2("base64", NULL, sha1, SHA1_LEN, b64, b64_len); + if (n < SHA1_BASE64_LEN || + (n != SHA1_BASE64_LEN && b64[SHA1_BASE64_LEN] != '\r' && + b64[SHA1_BASE64_LEN] != '\n')) { + error("Invalid base64 response."); + exit(1); + } + b64[SHA1_BASE64_LEN] = '\0'; + + int len = snprintf(buffer, BUFFERSIZE, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "\r\n", b64); + + if (len == BUFFERSIZE) { + error("Response length > %d.", BUFFERSIZE); + exit(1); + } + + log(3, "HTTP response:\n%s===", buffer); + + if (block_write(newclient_fd, buffer, len) != len) { + syserror("Cannot write response."); + close(newclient_fd); + return -1; + } + + log(2, "Response sent."); + + /* Close existing connection, if any. */ + if (client_fd >= 0) + socket_client_close(1); + + client_fd = newclient_fd; + + return socket_client_sendversion(version); +} + +/* Initialise WebSocket server */ +static void socket_server_init(int port_) { + struct sockaddr_in server_addr; + int optval; + + port = port_; + + server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + syserror("Cannot create server socket."); + exit(1); + } + + /* SO_REUSEADDR to make sure the server can restart after a crash. */ + optval = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + /* Listen on loopback interface, port PORT. */ + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server_addr.sin_port = htons(port); + + if (bind(server_fd, + (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + syserror("Cannot bind server socket."); + exit(1); + } + + if (listen(server_fd, 5) < 0) { + syserror("Cannot listen on server socket."); + exit(1); + } +} From 1aa5efb0328781fd0638a95c6cafe6ba333a3b97 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 20 Oct 2014 11:56:54 -0700 Subject: [PATCH 010/334] websocket.h/popen2: Check command exit status. --- src/websocket.c | 5 ++++- src/websocket.h | 26 +++++++++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/websocket.c b/src/websocket.c index 214cadbf5..1c3f04c51 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -346,7 +346,10 @@ static void socket_client_read() { length++; } else { /* Launch command in background (this is necessary as - croutoncycle may send a websocket command) */ + croutoncycle may send a websocket command, leaving us + deadlocked...) */ + /* FIXME: These processes are not reaped and leave lots of + * zombies. Scary. */ pid_t pid = fork(); if (pid < 0) { syserror("Fork error."); diff --git a/src/websocket.h b/src/websocket.h index 0ed24a438..ffd629d30 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -126,7 +126,7 @@ static int block_write(int fd, char* buffer, size_t size) { /* Run external command, piping some data on its stdin, and reading back * the output. Returns the number of bytes read from the process (at most - * outlen), or -1 on error. */ + * outlen), or a negative number on error (-exit status). */ static int popen2(char* cmd, char *const argv[], char* input, int inlen, char* output, int outlen) { pid_t pid = 0; @@ -157,8 +157,8 @@ static int popen2(char* cmd, char *const argv[], execlp(cmd, cmd, NULL); } - error("Error running '%s'.", cmd); - exit(1); + syserror("Error running '%s'.", cmd); + exit(127); } /* Parent */ @@ -173,11 +173,12 @@ static int popen2(char* cmd, char *const argv[], fds[1].fd = stdin_fd[1]; pid_t wait_pid; + int status = 0; int readlen = 0; int writelen = 0; while (1) { /* Get child status */ - wait_pid = waitpid(pid, NULL, WNOHANG); + wait_pid = waitpid(pid, &status, WNOHANG); /* Check if there is data to read, no matter the process status. */ /* Timeout after 10ms, or immediately if the process exited already */ int polln = poll(fds, 2, (wait_pid == pid) ? 0 : 10); @@ -218,8 +219,12 @@ static int popen2(char* cmd, char *const argv[], goto error; } log(3, "read n=%d", n); - readlen += n; + + if (verbose >= 3) { + fwrite(output, 1, readlen, stdout); + } + if (readlen >= outlen) { error("Output too long."); ret = readlen; @@ -238,6 +243,17 @@ static int popen2(char* cmd, char *const argv[], goto error; } else if (wait_pid == pid) { log(3, "child exited!"); + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + error("child exited with status %d", WEXITSTATUS(status)); + ret = -WEXITSTATUS(status); + goto error; + } + } else { + error("child process did not exit: %d", status); + ret = -1; + goto error; + } break; } } From 1d38c2e2422f97b6bf55580c9efc027ecf9f2721 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:25:09 -0700 Subject: [PATCH 011/334] fbserver: WebSocket server to serve X11 framebuffer content --- Makefile | 2 + src/fbserver-proto.h | 72 +++++ src/fbserver.c | 614 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 688 insertions(+) create mode 100644 src/fbserver-proto.h create mode 100644 src/fbserver.c diff --git a/Makefile b/Makefile index ea0bdc0b7..9a3eb63a1 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,12 @@ TARPARAMS ?= -j CFLAGS=-g -Wall -Werror -Os croutoncursor_LIBS = -lX11 -lXfixes -lXrender +croutonfbserver_LIBS = -lX11 -lXdamage -lXext -lXfixes -lXtst croutonwmtools_LIBS = -lX11 croutonxi2event_LIBS = -lX11 -lXi croutonwebsocket_DEPS = src/websocket.h +croutonfbserver_DEPS = src/websocket.h ifeq ($(wildcard .git/HEAD),) GITHEAD := diff --git a/src/fbserver-proto.h b/src/fbserver-proto.h new file mode 100644 index 000000000..982783b51 --- /dev/null +++ b/src/fbserver-proto.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2014 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * WebSocket fbserver shared structures. + * + */ + +#include + +/* WebSocket constants */ +#define VERSION "VF1" +#define PORT_BASE 30010 + +struct __attribute__((__packed__)) screen { + char type; /* 'S' */ + uint8_t shm:1; /* Transfer data through shm */ + uint8_t refresh:1; /* Force a refresh, even if no damage is observed */ + uint16_t width; + uint16_t height; + uint64_t paddr; /* shm: client buffer address */ + uint64_t sig; /* shm: signature at the beginning of buffer */ +}; + +struct __attribute__((__packed__)) screen_reply { + char type; /* 'S' */ + uint8_t shm:1; /* Data was transfered through shm */ + uint8_t shmfailed:1; /* Set to 1 if shm trick has failed */ + uint8_t updated:1; /* Set to 1 if data has been updated */ + uint8_t cursor_updated:1; /* Set to 1 if cursor has been updated */ + uint16_t width; + uint16_t height; + uint32_t cursor_serial; /* Unique cursor serial number */ +}; + +/* Ask for cursor image (if serial is unknown) */ +struct __attribute__((__packed__)) cursor { + char type; /* 'P' */ +}; + +struct __attribute__((__packed__)) cursor_reply { + char type; /* 'P' */ + uint16_t width, height; + uint16_t xhot, yhot; + uint32_t cursor_serial; + uint32_t pixels[0]; +}; + +/* Change resolution (query + reply) */ +struct __attribute__((__packed__)) resolution { + char type; /* 'R' */ + uint16_t width; + uint16_t height; +}; + +struct __attribute__((__packed__)) key { + char type; /* 'K' */ + uint8_t down:1; /* 1: down, 0: up */ + uint32_t keysym; +}; + +struct __attribute__((__packed__)) mousemove { + char type; /* 'M' */ + uint16_t x; + uint16_t y; +}; + +struct __attribute__((__packed__)) mouseclick { + char type; /* 'C' */ + uint8_t down:1; + uint8_t button; +}; diff --git a/src/fbserver.c b/src/fbserver.c new file mode 100644 index 000000000..dcd2b4751 --- /dev/null +++ b/src/fbserver.c @@ -0,0 +1,614 @@ +/* Copyright (c) 2014 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * WebSocket server that acts as a X11 framebuffer server. It communicates + * with the extension in Chromium OS. It sends framebuffer and cursor data, + * and receives keyboard/mouse events. + * + */ + +#define _GNU_SOURCE /* for ppoll */ +#include "websocket.h" +#include "fbserver-proto.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static Display *dpy; + +int damageEvent; +int fixesEvent; + +/* shm entry cache */ +struct cache_entry { + uint64_t paddr; /* Address from PNaCl side */ + int fd; + void *map; /* mmap-ed memory */ + size_t length; /* mmap length */ +}; + +struct cache_entry cache[2]; +int next_entry; + +/* Remember which keys/buttons are currently pressed */ +typedef enum { INVALID=0, MOUSE=1, KEYBOARD=2 } keybuttontype; +struct keybutton { + keybuttontype type; + uint32_t code; /* KeyCode or mouse button */ +}; + +/* Store currently pressed keys/buttons in an array. + * No valid entry on or after curmax. */ +struct keybutton pressed[256]; +int curmax = 0; + +void kb_add(keybuttontype type, uint32_t code) { + trueorabort(curmax < sizeof(pressed)/sizeof(struct keybutton), + "Too many keys pressed"); + int firstfree = curmax; + int i; + for (i = 0; i < curmax; i++) { + if (pressed[i].type == type && pressed[i].code == code) { + /* Key already recorded */ + return; + } else if (pressed[i].type == INVALID && firstfree == curmax) { + firstfree = i; + } + } + pressed[firstfree].type = type; + pressed[firstfree].code = code; + if (firstfree == curmax) + curmax++; +} + +void kb_remove(keybuttontype type, uint32_t code) { + int lastvalid = -1; + int i; + for (i = 0; i < curmax; i++) { + if (pressed[i].type == type && pressed[i].code == code) { + pressed[i].type = INVALID; + } else if (pressed[i].type != INVALID) { + lastvalid = i; + } + } + curmax = lastvalid+1; +} + +void kb_release_all() { + int i; + log(2, "Releasing all keys..."); + for (i = 0; i < curmax; i++) { + if (pressed[i].type == MOUSE) { + log(2, "Mouse %d", pressed[i].code); + XTestFakeButtonEvent(dpy, pressed[i].code, 0, CurrentTime); + } else if (pressed[i].type == KEYBOARD) { + log(2, "Keyboard %d", pressed[i].code); + XTestFakeKeyEvent(dpy, pressed[i].code, 0, CurrentTime); + } + } + curmax = 0; +} + +/* X11-related functions */ + +static int xerror_handler(Display *dpy, XErrorEvent *e) { + return 0; +} + +/* Register XDamage events for a given Window. */ +static void register_damage(Display *dpy, Window win) { + XWindowAttributes attrib; + if (XGetWindowAttributes(dpy, win, &attrib) && + !attrib.override_redirect) { + XDamageCreate(dpy, win, XDamageReportRawRectangles); + } +} + +static int init_display(char* name) { + int event, error, major, minor; + + dpy = XOpenDisplay(name); + + if (!dpy) { + error("Cannot open display."); + return -1; + } + + /* We need XTest, XDamage and XFixes */ + if (!XTestQueryExtension(dpy, &event, &error, &major, &minor)) { + error("XTest not available!"); + return -1; + } + + if (!XDamageQueryExtension(dpy, &damageEvent, &error)) { + error("XDamage not available!"); + return -1; + } + + if (!XFixesQueryExtension(dpy, &fixesEvent, &error)) { + error("XFixes not available!"); + return -1; + } + + Window root = DefaultRootWindow(dpy); + Window rootp, parent; + Window *children; + unsigned int nchildren, i; + + /* Get notified when new windows are created. */ + XSelectInput(dpy, root, SubstructureNotifyMask); + + /* Register damage events for existing windows */ + XQueryTree(dpy, root, &rootp, &parent, &children, &nchildren); + + /* FIXME: We never reset the handler, is that a good thing? */ + XSetErrorHandler(xerror_handler); + + register_damage(dpy, root); + for (i = 0; i < nchildren; i++) { + register_damage(dpy, children[i]); + } + + /* Register for cursor events */ + XFixesSelectCursorInput(dpy, root, XFixesDisplayCursorNotifyMask); + + return 0; +} + +/* Change resolution using external handler. + * Reply must be a resolution in "canonical" form: x[_] */ +/* FIXME: Maybe errors here should not be fatal... */ +void change_resolution(const struct resolution* rin) { + char* cmd = "findres"; + char arg1[32], arg2[32], buffer[256]; + int c; + char* args[] = {cmd, arg1, arg2, NULL}; + char* endptr; + + /* Setup parameters and run command */ + c = snprintf(arg1, sizeof(arg1), "%d", rin->width); + trueorabort(c > 0, "snprintf"); + c = snprintf(arg2, sizeof(arg2), "%d", rin->height); + trueorabort(c > 0, "snprintf"); + log(2, "Running %s %s %s", cmd, arg1, arg2); + c = popen2(cmd, args, NULL, 0, buffer, sizeof(buffer)); + trueorabort(c > 0, "popen2"); + + /* Parse output */ + buffer[c < sizeof(buffer) ? c : (sizeof(buffer)-1)] = 0; + log(2, "Result: %s", buffer); + char* cut = strchr(buffer, '_'); + if (cut) *cut = 0; + cut = strchr(buffer, 'x'); + trueorabort(cut, "Invalid answer: %s", buffer); + *cut = 0; + + long nwidth = strtol(buffer, &endptr, 10); + trueorabort(buffer != endptr && *endptr == '\0', + "Invalid width: '%s'", buffer); + long nheight = strtol(cut+1, &endptr, 10); + trueorabort(cut+1 != endptr && *endptr == '\0', + "Invalid height: '%s'", cut+1); + log(1, "New resolution %ld x %ld", nwidth, nheight); + + char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct resolution)]; + struct resolution* r = (struct resolution*)(reply_raw+FRAMEMAXHEADERSIZE); + r->type = 'R'; + r->width = nwidth; + r->height = nheight; + socket_client_write_frame(reply_raw, sizeof(*r), WS_OPCODE_BINARY, 1); +} + +void close_mmap(struct cache_entry* entry) { + if (!entry->map) + return; + + log(2, "Closing mmap %p %zu %d", entry->map, entry->length, entry->fd); + munmap(entry->map, entry->length); + close(entry->fd); + entry->map = NULL; +} + +/* Find NaCl/Chromium shm memory using external handler. + * Reply must be in the form PID:file */ +struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { + struct cache_entry* entry = NULL; + int try; + + /* Find entry in cache */ + if (cache[0].paddr == paddr) + entry = &cache[0]; + else if (cache[1].paddr == paddr) + entry = &cache[1]; + else { + /* Not found: erase an existing entry. */ + entry = &cache[next_entry]; + next_entry = (next_entry + 1) % 2; + close_mmap(entry); + } + + for (try = 0; try < 2; try++) { + /* Check signature */ + if (entry->map) { + if (*((uint64_t*)entry->map) == sig) { + return entry; + } + error("Invalid signature, fetching new shm!"); + close_mmap(entry); + } + + /* Setup parameters and run command */ + char* cmd = "croutonfindnacl"; + char arg1[32], arg2[32], buffer[256]; + char* args[] = {cmd, arg1, arg2, NULL}; + int c, i, p = 0; + char* endptr; + + c = snprintf(arg1, sizeof(arg1), "%08lx", (long)paddr & 0xffffffff); + trueorabort(c > 0, "snprintf"); + p = 0; + for (i = 0; i < 8; i++) { + c = snprintf(arg2+p, sizeof(arg2)-p, "%02x", + ((uint8_t*)&sig)[i]); + trueorabort(c > 0, "snprintf"); + p += c; + } + log(2, "Running %s %s %s", cmd, arg1, arg2); + c = popen2(cmd, args, NULL, 0, buffer, sizeof(buffer)); + if (c <= 0) { + error("Error running helper."); + return NULL; + } + buffer[c < sizeof(buffer) ? c : (sizeof(buffer)-1)] = 0; + log(2, "Result: %s", buffer); + + /* Parse PID:file output */ + char* cut = strchr(buffer, ':'); + if (!cut) { + error("No ':' in helper reply: %s.", cut); + return NULL; + } + *cut = 0; + + long pid = strtol(buffer, &endptr, 10); + if(buffer == endptr || *endptr != '\0') { + error("Invalid pid: %s", buffer); + return NULL; + } + char* file = cut+1; + log(2, "PID:%ld, FILE:%s", pid, file); + + entry->paddr = paddr; + entry->fd = open(file, O_RDWR); + if (entry->fd < 0) { + error("Cannot open file %s\n", file); + return NULL; + } + + entry->length = length; + entry->map = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, + entry->fd, 0); + if (!entry->map) { + error("Cannot mmap %s\n", file); + close(entry->fd); + return NULL; + } + + log(2, "mmap ok %p %zu %d", entry->map, entry->length, entry->fd); + } + + error("Cannot find shm."); + return NULL; +} + +/* WebSocket functions */ + +XImage* img = NULL; +XShmSegmentInfo shminfo; + +/* Write framebuffer image to websocket/shm */ +int write_image(const struct screen* screen) { + char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct screen_reply)]; + struct screen_reply* reply = + (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + Window root = DefaultRootWindow(dpy); + int refresh = 0; + XEvent ev; + int ret; + + memset(reply_raw, 0, sizeof(*reply_raw)); + + reply->type = 'S'; + reply->width = screen->width; + reply->height = screen->height; + + /* Allocate XShmImage */ + if (!img || img->width != screen->width || img->height != screen->height) { + if (img) { + XDestroyImage(img); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + } + + /* FIXME: Some error checking should happen here... */ + img = XShmCreateImage(dpy, DefaultVisual(dpy, 0), 24, + ZPixmap, NULL, &shminfo, + screen->width, screen->height); + trueorabort(img, "XShmCreateImage"); + shminfo.shmid = shmget(IPC_PRIVATE, img->bytes_per_line*img->height, + IPC_CREAT|0777); + trueorabort(shminfo.shmid != -1, "shmget"); + shminfo.shmaddr = img->data = shmat(shminfo.shmid, 0, 0); + trueorabort(shminfo.shmaddr != (void*)-1, "shmat"); + shminfo.readOnly = False; + ret = XShmAttach(dpy, &shminfo); + trueorabort(ret, "XShmAttach"); + /* Force refresh */ + refresh = 1; + } + + if (screen->refresh) { + log(1, "Force refresh from client."); + /* refresh forced by the client */ + refresh = 1; + } + + /* Register damage on new windows */ + while (XCheckTypedEvent(dpy, MapNotify, &ev)) { + register_damage(dpy, ev.xcreatewindow.window); + refresh = 1; + } + + /* Check for damage */ + while (XCheckTypedEvent(dpy, damageEvent+XDamageNotify, &ev)) { + refresh = 1; + } + + /* Check for cursor events */ + reply->cursor_updated = 0; + while (XCheckTypedEvent(dpy, fixesEvent+XFixesCursorNotify, &ev)) { + XFixesCursorNotifyEvent* curev = (XFixesCursorNotifyEvent*)&ev; + char* name = XGetAtomName(dpy, curev->cursor_name); + log(2, "cursor! %ld %s", curev->cursor_serial, name); + XFree(name); + reply->cursor_updated = 1; + reply->cursor_serial = curev->cursor_serial; + } + + /* No update */ + if (!refresh) { + reply->shm = 0; + reply->updated = 0; + socket_client_write_frame(reply_raw, sizeof(*reply), + WS_OPCODE_BINARY, 1); + return 0; + } + + /* Get new image from framebuffer */ + XShmGetImage(dpy, root, img, 0, 0, AllPlanes); + + int size = img->bytes_per_line * img->height; + + trueorabort(size == screen->width*screen->height*4, + "Invalid screen byte count"); + + if (screen->shm) { + struct cache_entry* entry = find_shm(screen->paddr, screen->sig, size); + + reply->shm = 1; + reply->updated = 1; + reply->shmfailed = 0; + + if (entry && entry->map) { + if (size == entry->length) { + memcpy(entry->map, img->data, size); + msync(entry->map, size, MS_SYNC); + } else { + /* This should never happen (it means the client passed an + * outdated buffer to us). */ + error("Invalid shm entry length (client bug!)."); + reply->shmfailed = 1; + } + } else { + /* Keep the flow going, even if we cannot find the shm. Next time + * the NaCl client reallocates the buffer, we are likely to be able + * to find it. */ + error("Cannot find shm, moving on..."); + reply->shmfailed = 1; + } + + /* Confirm write is done */ + socket_client_write_frame(reply_raw, sizeof(*reply), + WS_OPCODE_BINARY, 1); + } else { + trueorabort(0, "Non-SHM path is currently broken!"); + /* Confirm write is done */ + reply->shm = 0; + reply->updated = 1; + socket_client_write_frame(reply_raw, sizeof(*reply), + WS_OPCODE_BINARY, 0); + /* FIXME: This is broken with current API... */ + socket_client_write_frame(img->data, size, WS_OPCODE_BINARY, 1); + } + + return 0; +} + +/* Write cursor image to websocket */ +int write_cursor() { + XFixesCursorImage *img = XFixesGetCursorImage(dpy); + int size = img->width*img->height; + const int replylength = sizeof(struct cursor_reply)+sizeof(uint32_t)*size; + char reply_raw[FRAMEMAXHEADERSIZE+replylength]; + struct cursor_reply* reply = + (struct cursor_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + int i = 0; + + memset(reply_raw, 0, sizeof(*reply_raw)); + + reply->type = 'P'; + reply->width = img->width; + reply->height = img->height; + reply->xhot = img->xhot; + reply->yhot = img->yhot; + reply->cursor_serial = img->cursor_serial; + /* This casts long[] to uint32_t[] */ + for (i = 0; i < size; i++) + reply->pixels[i] = img->pixels[i]; + +#if 0 + /* Debug: dump cursor image */ + int x, y; + for (y = 0; y < img->height; y++) { + for (x = 0; x < img->width; x++) { + printf("%01x", (reply->pixels[y*img->width+x] >> 28) & 0xf); + } + printf("\n"); + } +#endif + + socket_client_write_frame(reply_raw, replylength, WS_OPCODE_BINARY, 1); + XFree(img); + + return 0; +} + +/* Check if a packet size is correct */ +int check_size(int length, int target, char* error) { + if (length != target) { + error("Invalid %s packet (%d != %d)", error, length, target); + socket_client_close(0); + return 0; + } + return 1; +} + +void usage(char* argv0) { + fprintf(stderr, "%s [-v 0-3] display\n", argv0); + exit(1); +} + +int main(int argc, char** argv) { + int c; + char* display; + char* endptr; + int displaynum; + + while ((c = getopt(argc, argv, "v:")) != -1) { + switch (c) { + case 'v': + verbose = atoi(optarg); + break; + default: + usage(argv[0]); + } + } + + if (optind != argc-1) + usage(argv[0]); + + display = argv[optind]; + + trueorabort(display[0] == ':', "Invalid display: '%s'", display); + + displaynum = (int)strtol(display+1, &endptr, 10); + trueorabort(display+1 != endptr && (*endptr == '\0' || *endptr == '.'), + "Invalid display number: '%s'", display); + + init_display(display); + socket_server_init(PORT_BASE+displaynum); + + unsigned char buffer[BUFFERSIZE]; + int length; + + while (1) { + struct key* k; + struct mouseclick* mc; + struct mousemove* mm; + KeyCode kc; + + socket_server_accept(VERSION); + while (1) { + length = socket_client_read_frame((char*)buffer, sizeof(buffer)); + if (length < 0) { + socket_client_close(1); + break; + } + + if (length < 1) { + error("Invalid packet from client (size <1)."); + socket_client_close(0); + break; + } + + switch (buffer[0]) { + case 'S': /* Screen */ + if (!check_size(length, sizeof(struct screen), "screen")) + break; + write_image((struct screen*)buffer); + break; + case 'P': /* Cursor */ + if (!check_size(length, sizeof(struct cursor), "cursor")) + break; + write_cursor(); + break; + case 'R': /* Resolution */ + if (!check_size(length, sizeof(struct resolution), + "resolution")) + break; + change_resolution((struct resolution*)buffer); + break; + case 'K': /* Key */ + if (!check_size(length, sizeof(struct key), "key")) + break; + k = (struct key*)buffer; + kc = XKeysymToKeycode(dpy, k->keysym); + log(2, "Key: ks=%04x kc=%04x\n", k->keysym, kc); + if (kc != 0) { + XTestFakeKeyEvent(dpy, kc, k->down, CurrentTime); + if (k->down) kb_add(KEYBOARD, kc); + else kb_remove(KEYBOARD, kc); + } else { + error("Invalid keysym %04x.", k->keysym); + } + break; + case 'C': /* Click */ + if (!check_size(length, sizeof(struct mouseclick), + "mouseclick")) + break; + mc = (struct mouseclick*)buffer; + XTestFakeButtonEvent(dpy, mc->button, mc->down, CurrentTime); + if (mc->down) kb_add(MOUSE, mc->button); + else kb_remove(MOUSE, mc->button); + break; + case 'M': /* Mouse move */ + if (!check_size(length, sizeof(struct mousemove), "mousemove")) + break; + mm = (struct mousemove*)buffer; + XTestFakeMotionEvent(dpy, 0, mm->x, mm->y, CurrentTime); + break; + case 'Q': /* "Quit": release all keys */ + kb_release_all(); + break; + default: + error("Invalid packet from client (%d).", buffer[0]); + socket_client_close(0); + } + } + socket_client_close(0); + kb_release_all(); + close_mmap(&cache[0]); + close_mmap(&cache[1]); + } + + return 0; +} From b845260acca92249fb8547de36e2df144364be92 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:26:53 -0700 Subject: [PATCH 012/334] croutonfindnacl: Helper script to find NaCl process and shm. --- chroot-bin/croutonfindnacl | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100755 chroot-bin/croutonfindnacl diff --git a/chroot-bin/croutonfindnacl b/chroot-bin/croutonfindnacl new file mode 100755 index 000000000..dd42f3714 --- /dev/null +++ b/chroot-bin/croutonfindnacl @@ -0,0 +1,54 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +VERBOSE= + +ADDRESS="$1" +PATTERN="$2" +PIDS="${3:-"`pgrep nacl_helper`"}" + +MATCH="" + +# Iterate over all NaCl helper processes +for pid in $PIDS; do + if [ -n "$VERBOSE" ]; then echo "pid:$pid" 1>&2; fi + # Find candidate mappings + file="`awk '$1 ~ /^[0-9a-f]*'"$ADDRESS"'-/ && $2 == "rw-s" \ + && $6 ~ /\/shm\/.com.google.Chrome/ { print $6 } + ' "/proc/$pid/maps"`" + if [ -n "$VERBOSE" ]; then echo "file:$file" 1>&2; fi + if [ -z "$file" ]; then + continue + fi + + # Iterate over mappings, and check signature + for fd in "/proc/$pid/fd"/*; do + link="`readlink "$fd"`" + link="${link% (deleted)}" + if [ "$link" = "$file" ]; then + # Check if signature matches + pattern="`od -An -t x1 -N8 "$fd" | tr -d ' '`" + if [ -n "$VERBOSE" ]; then echo "FD:$fd ($pattern)" 1>&2; fi + if [ "$pattern" = "$PATTERN" ]; then + # Second match? This should never happen + if [ -n "$MATCH" ]; then + echo -n "-1:ambiguous" + exit 1 + fi + MATCH="$pid:$fd" + fi + fi + done +done + +if [ -n "$MATCH" ]; then + echo -n "$MATCH" + exit 0 +else + echo -n "-1:no match" + exit 1 +fi From dd160e466eba2c1e6415c663cd3ce4f2c21a3555 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:27:20 -0700 Subject: [PATCH 013/334] findres: Helper script to find best resolution. --- chroot-bin/findres | 53 ++++++++++++++++++++++++++++++++++++++++++++++ src/websocket.h | 3 ++- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100755 chroot-bin/findres diff --git a/chroot-bin/findres b/chroot-bin/findres new file mode 100755 index 000000000..99c3db308 --- /dev/null +++ b/chroot-bin/findres @@ -0,0 +1,53 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Changes the resolution of the current display, taking the closest, +# smaller, available resolution in xrandr. +# If no smaller resolution is available, pick the closest one. +# Outputs the applied resolution + +set -e + +if [ -z "$1" -o -z "$2" ]; then + echo "Usage: $0 width height" 1>&2 + exit 2 +fi + +TARGETW="$1" +TARGETH="$2" + +# This assumes modes have "canonical" names, in the form "x[_]" +res="`xrandr | awk ' + function abs(x) { return x > 0 ? x : -x } + BEGIN { + tw='"$TARGETW"' + th='"$TARGETH"' + penalty = 1000000 + } + $1 ~ /[0-9]+x[0-9]+/ { + res = $1; sub(/_[0-9]+/, "", res) + w = res; sub(/x[0-9]*/, "", w); w = int(w) + h = res; sub(/[0-9]*x/, "", h); h = int(h) + score = abs(tw-w) + abs(th-h) + if (w > tw) score += penalty + if (h > th) score += penalty + if (!cmode || score < cscore) { + cmode = $1 + cscore = score + } + } + END { + if (!cmode) exit 1 + print cmode + } +'`" + +if [ -z "$res" ]; then + echo Cannot find resolution 1>&2 + exit 1 +fi + +xrandr -s "$res" 2>/dev/null +echo -n "$res" diff --git a/src/websocket.h b/src/websocket.h index ffd629d30..fb1b78c14 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -57,7 +57,8 @@ static int verbose = 0; #define trueorabort(expr, str, ...) do { \ if (!(expr)) { \ - printf("%s: " str "\n", __func__, ##__VA_ARGS__); \ + printf("%s: ASSERTION " #expr " FAILED (" str ")\n", \ + __func__, ##__VA_ARGS__); \ abort(); \ } \ } while (0) From a1df70b526d700e0bfbb42ad7ec325a7437eaf84 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:28:49 -0700 Subject: [PATCH 014/334] xorg-dummy.conf: Xorg configuration for Xorg dummy server. --- chroot-etc/xorg-dummy.conf | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 chroot-etc/xorg-dummy.conf diff --git a/chroot-etc/xorg-dummy.conf b/chroot-etc/xorg-dummy.conf new file mode 100644 index 000000000..bbf0470cd --- /dev/null +++ b/chroot-etc/xorg-dummy.conf @@ -0,0 +1,43 @@ +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +Section "Monitor" + Identifier "Monitor0" + HorizSync 5.0-1000.0 + VertRefresh 5.0-200.0 + # This display size corresponds to 96 dpi at 1024x768 + DisplaySize 271 203 + # The following lines are automatically generated +###MODELINES### +EndSection + +Section "Device" + Identifier "Card0" + Driver "dummy" + # Enough memory for 4096x2048 + VideoRam 32768 +EndSection + +Section "Screen" + DefaultDepth 24 + Identifier "Screen0" + Device "Card0" + Monitor "Monitor0" + SubSection "Display" + Depth 24 + EndSubSection +EndSection + +Section "ServerLayout" + Identifier "Layout0" + Screen "Screen0" +EndSection + +Section "ServerFlags" + Option "AutoAddDevices" "false" + Option "DontVTSwitch" "true" + Option "AllowMouseOpenFail" "true" + Option "PciForceNone" "true" + Option "AutoEnableDevices" "false" +EndSection From c1184d1e006b78c545d8dba9ae3d3623d1f4dc6c Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:31:52 -0700 Subject: [PATCH 015/334] criat: crouton-in-a-tab NaCl C++ source code --- host-ext/nacl_src/.gitignore | 1 + host-ext/nacl_src/Makefile | 34 ++ host-ext/nacl_src/criat.cc | 871 +++++++++++++++++++++++++++++++++++ 3 files changed, 906 insertions(+) create mode 100644 host-ext/nacl_src/.gitignore create mode 100644 host-ext/nacl_src/Makefile create mode 100644 host-ext/nacl_src/criat.cc diff --git a/host-ext/nacl_src/.gitignore b/host-ext/nacl_src/.gitignore new file mode 100644 index 000000000..b370218a8 --- /dev/null +++ b/host-ext/nacl_src/.gitignore @@ -0,0 +1 @@ +pnacl diff --git a/host-ext/nacl_src/Makefile b/host-ext/nacl_src/Makefile new file mode 100644 index 000000000..fc5f0a556 --- /dev/null +++ b/host-ext/nacl_src/Makefile @@ -0,0 +1,34 @@ +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# GNU Makefile based on shared rules provided by the Native Client SDK. +# See README.Makefiles for more details. + +VALID_TOOLCHAINS := pnacl + +NACL_SDK_ROOT ?= $(HOME)/nacl_sdk/pepper_35 + +../crouton/criat.pexe: pnacl/Release/criat.pexe + cp pnacl/Release/criat.pexe ../crouton/criat.pexe + +include $(NACL_SDK_ROOT)/tools/common.mk + + +TARGET = criat +LIBS = ppapi_cpp ppapi + +CFLAGS = -Wall +SOURCES = criat.cc + +# Build rules generated by macros from common.mk: +$(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) + +ifeq ($(CONFIG),Release) +$(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) +$(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) +else +$(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) +endif + +$(eval $(call NMF_RULE,$(TARGET),)) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc new file mode 100644 index 000000000..aa4656df8 --- /dev/null +++ b/host-ext/nacl_src/criat.cc @@ -0,0 +1,871 @@ +/* Copyright (c) 2014 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include + +#include "ppapi/cpp/graphics_2d.h" +#include "ppapi/cpp/image_data.h" +#include "ppapi/cpp/input_event.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/message_loop.h" +#include "ppapi/cpp/module.h" +#include "ppapi/cpp/mouse_cursor.h" +#include "ppapi/cpp/point.h" +#include "ppapi/cpp/var.h" +#include "ppapi/cpp/var_array_buffer.h" +#include "ppapi/cpp/websocket.h" +#include "ppapi/utility/completion_callback_factory.h" + +namespace { + /* Protocol data structures */ +#include "../../src/fbserver-proto.h" + /* SuperL keycode (search key) */ + const uint32_t SUPER_L = 0xffeb; + + const int FULLFPS = 30; /* Maximum fps */ + const int BLURFPS = 5; /* fps when window is possibly hidden */ + const int HIDDENFPS = 0; /* fps when window is hidden */ +} /* namespace */ + +class CriatInstance : public pp::Instance { +public: + explicit CriatInstance(PP_Instance instance) + : pp::Instance(instance), + callback_factory_(this), + scale_(1.0f), + image_data_(NULL), + k_(0), + websocket_(this), + connected_(false), + target_fps_(FULLFPS), + pending_mouse_move_(false), + mouse_pos_(-1, -1), + avgfps_(0), + display_(-1), + debug_(0), + hidpi_(false) {} + + virtual ~CriatInstance() {} + + /* Register events */ + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | + PP_INPUTEVENT_CLASS_WHEEL | + PP_INPUTEVENT_CLASS_TOUCH); + RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); + + srand(pp::Module::Get()->core()->GetTime()); + + return true; + } + + /** Interface with Javascript **/ +public: + /* Handle message from Javascript */ + /* Format: : */ + virtual void HandleMessage(const pp::Var& var_message) { + if (!var_message.is_string()) + return; + + std::string message = var_message.AsString(); + + LogMessage(2, "message=" + message); + + size_t pos = message.find(':'); + if (pos != std::string::npos) { + std::string type = message.substr(0, pos); + if (type == "resize") { + size_t pos2 = message.find('/', pos+1); + if (pos2 != std::string::npos) { + int width = stoi(message.substr(pos+1, pos2-pos-1)); + int height = stoi(message.substr(pos2+1)); + ChangeResolution(width*scale_, height*scale_); + } + } else if (type == "display") { + int display = stoi(message.substr(pos+1)); + if (display != display_) { + display_ = display; + SocketConnect(); + } + } else if (type == "blur" || type == "hide") { + /* Release all keys */ + SocketSend(pp::Var("Q"), false); + /* Throttle/stop refresh */ + SetTargetFPS((type == "blur") ? BLURFPS : HIDDENFPS); + } else if (type == "focus") { + /* Force refresh and ask for next frame */ + SetTargetFPS(FULLFPS); + } else if (type == "debug") { + debug_ = stoi(message.substr(pos+1)); + } else if (type == "hidpi") { + bool newhidpi = stoi(message.substr(pos+1)); + if (newhidpi != hidpi_) { + hidpi_ = newhidpi; + InitContext(); + } + } + } + } + +private: + /* Send a status message to Javascript */ + void StatusMessage(std::string str) { + ControlMessage("status", str); + } + + /* Send a logging message to Javascript */ + void LogMessage(int level, std::string str) { + double delta = (pp::Module::Get()->core()->GetTime()-lasttime_)*1000; + + if (level <= debug_) { + std::ostringstream status; + status << "(" << level << ") " << (int)delta << " " << str; + ControlMessage("log", status.str()); + } + } + + /* Send a control message to Javascript */ + /* Format: : */ + void ControlMessage(std::string type, std::string str) { + std::ostringstream status; + status << type << ":" << str; + PostMessage(status.str()); + } + + /** WebSocket interface **/ +private: + /* Connect to WebSocket server */ + void SocketConnect(int32_t result = 0) { + if (display_ < 0) { + LogMessage(-1, "SocketConnect: No display defined yet."); + return; + } + + std::ostringstream url; + url << "ws://localhost:" << (PORT_BASE+display_) << "/"; + websocket_.Connect(pp::Var(url.str()), NULL, 0, + callback_factory_.NewCallback( + &CriatInstance::OnSocketConnectCompletion)); + StatusMessage("Connecting..."); + } + + /* WebSocket connected (or failed to connect) */ + void OnSocketConnectCompletion(int32_t result) { + if (result != PP_OK) { + std::ostringstream status; + status << "Connection failed (" << result << "), retrying..."; + StatusMessage(status.str()); + pp::Module::Get()->core()->CallOnMainThread(1000, + callback_factory_.NewCallback(&CriatInstance::SocketConnect)); + return; + } + + cursor_cache_.clear(); + + SocketReceive(); + + StatusMessage("Connected."); + } + + /* WebSocket closed */ + void OnSocketClosed(int32_t result = 0) { + StatusMessage("Disconnected..."); + ControlMessage("disconnected", "Socket closed"); + connected_ = false; + screen_flying_ = false; + Paint(true); + } + + /* Check if a packet size is valid */ + bool CheckSize(int length, int target, std::string type) { + if (length == target) + return true; + + std::stringstream status; + status << "Invalid " << type << " packet (" << length + << " != " << target << ")."; + LogMessage(-1, status.str()); + return false; + } + + /* Received a frame from WebSocket server */ + void OnSocketReceiveCompletion(int32_t result) { + std::stringstream status; + status << "ReadCompletion: " << result << "."; + LogMessage(5, status.str()); + + if (result == PP_ERROR_INPROGRESS) { + LogMessage(0, "Receive error INPROGRESS (should not happen)."); + /* We called SocketReceive too many times. */ + /* Not fatal: just wait for next call */ + return; + } else if (result != PP_OK) { + goto error; + } + + /* Get ready to receive next frame */ + pp::Module::Get()->core()->CallOnMainThread(0, + callback_factory_.NewCallback(&CriatInstance::SocketReceive)); + + /* Convert binary/text to char* */ + const char* data; + int datalen; + if (receive_var_.is_array_buffer()) { + pp::VarArrayBuffer array_buffer(receive_var_); + data = static_cast(array_buffer.Map()); + datalen = array_buffer.ByteLength(); + std::stringstream status; + status << "receive (binary): " << data[0]; + LogMessage(data[0] == 'S' ? 3 : 2, status.str()); + } else { + LogMessage(3, "receive (text): " + receive_var_.AsString()); + std::string str = receive_var_.AsString(); + data = str.c_str(); + datalen = str.length(); + } + + if (data[0] == 'V') { /* Version */ + if (connected_) { + LogMessage(-1, "Got a version while connected?!?"); + goto error; + } + if (strcmp(data, VERSION)) { + LogMessage(-1, "Invalid version received (" + + std::string(data) + ")."); + goto error; + } + connected_ = true; + SocketSend(pp::Var("VOK"), false); + ControlMessage("connected", "Version received"); + ChangeResolution(size_.width(), size_.height()); + // Start requesting frames + OnFlush(); + return; + } + + if (!connected_) { + LogMessage(-1, "Got some packet before version..."); + goto error; + } + + if (data[0] == 'S') { /* Screen */ + if (!CheckSize(datalen, sizeof(struct screen_reply), "screen_reply")) + goto error; + struct screen_reply* reply = (struct screen_reply*)data; + if (reply->updated) { + if (!reply->shmfailed) { + Paint(); + } else { + /* Blank the frame if shm failed */ + Paint(true); + force_refresh_ = true; + } + } else { + screen_flying_ = false; + /* No update: Ask for next frame in 1000/target_fps_ */ + if (target_fps_ > 0) + pp::Module::Get()->core()->CallOnMainThread( + 1000/target_fps_, + callback_factory_.NewCallback(&CriatInstance::RequestScreen), + request_token_); + } + + if (reply->cursor_updated) { + /* Cursor updated: find it in cache */ + std::unordered_map::iterator it = + cursor_cache_.find(reply->cursor_serial); + if (it == cursor_cache_.end()) { + /* No cache entry, ask for data. */ + SocketSend(pp::Var("P"), false); + } else { + std::ostringstream status; + status << "Cursor use cache for " << (reply->cursor_serial); + LogMessage(2, status.str()); + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + it->second.img, it->second.hot); + } + } + + return; + } else if (data[0] == 'P') { /* New cursor data is received */ + if (datalen < sizeof(struct cursor_reply)) { + std::stringstream status; + status << "Invalid cursor_reply packet (" << datalen + << " < " << sizeof(struct cursor_reply) << ")."; + LogMessage(-1, status.str()); + goto error; + } + + struct cursor_reply* cursor = (struct cursor_reply*)data; + if (!CheckSize(datalen, + sizeof(struct cursor_reply) + + 4*cursor->width*cursor->height, + "cursor_reply")) + goto error; + + std::ostringstream status; + status << "Cursor " << (cursor->width) << "/" << (cursor->height); + status << " " << (cursor->xhot) << "/" << (cursor->yhot); + status << " " << (cursor->cursor_serial); + LogMessage(0, status.str()); + + /* Scale down if needed */ + int scale = 1; + while (cursor->width/scale > 32 || cursor->height/scale > 32) + scale *= 2; + + int w = cursor->width/scale; + int h = cursor->height/scale; + pp::ImageData img(this, pp::ImageData::GetNativeImageDataFormat(), + pp::Size(w, h), true); + uint32_t* data = (uint32_t*)img.data(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + /* Nearest neighbour is least ugly */ + data[y*w+x] = cursor->pixels[scale*y*scale*w+scale*x]; + } + } + pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); + + cursor_cache_[cursor->cursor_serial].img = img; + cursor_cache_[cursor->cursor_serial].hot = hot; + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + img, hot); + return; + } else if (data[0] == 'R') { /* Resolution request reply */ + if (!CheckSize(datalen, sizeof(struct resolution), "resolution")) + goto error; + struct resolution* r = (struct resolution*)data; + std::ostringstream newres; + newres << (r->width/scale_) << "/" << (r->height/scale_); + /* Tell Javascript so that it can center us on the page */ + ControlMessage("resize", newres.str()); + force_refresh_ = true; + return; + } else { + std::stringstream status; + status << "Error: first char " << (int)data[0]; + LogMessage(0, status.str()); + /* fall-through: disconnect */ + } + + error: + LogMessage(-1, "Receive error."); + websocket_.Close(0, pp::Var("Receive error"), + callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); + } + + /* Ask to receive the next WebSocket frame */ + void SocketReceive(int32_t result = 0) { + websocket_.ReceiveMessage(&receive_var_, callback_factory_.NewCallback( + &CriatInstance::OnSocketReceiveCompletion)); + } + + /* Send a WebSocket Frame, possibly flushing current mouse position first */ + void SocketSend(const pp::Var& var, bool flushmouse) { + if (!connected_) { + LogMessage(-1, "SocketSend: not connected!"); + return; + } + + if (pending_mouse_move_ && flushmouse) { + struct mousemove* mm; + pp::VarArrayBuffer array_buffer(sizeof(*mm)); + mm = static_cast(array_buffer.Map()); + mm->type = 'M'; + mm->x = mouse_pos_.x(); + mm->y = mouse_pos_.y(); + array_buffer.Unmap(); + websocket_.SendMessage(array_buffer); + pending_mouse_move_ = false; + } + + websocket_.SendMessage(var); + } + + /** UI functions **/ +public: + virtual void DidChangeView(const pp::View& view) { + view_scale_ = view.GetDeviceScale(); + view_rect_ = view.GetRect(); + InitContext(); + } + + virtual bool HandleInputEvent(const pp::InputEvent& event) { + if (event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN || + event.GetType() == PP_INPUTEVENT_TYPE_KEYUP) { + pp::KeyboardInputEvent key_event(event); + + uint32_t keycode = key_event.GetKeyCode(); + std::string keystr = key_event.GetCode().AsString(); + uint32_t keysym = KeyCodeToKeySym(keycode, keystr); + bool down = event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN; + + std::ostringstream status; + status << "Key " << (down ? "DOWN" : "UP"); + status << ": C:" << keystr; + status << "/KC:" << std::hex << keycode; + status << "/KS:" << std::hex << keysym; + + if (keysym == 0) { + status << " (KEY UNKNOWN!)"; + LogMessage(0, status.str()); + return PP_TRUE; + } + + LogMessage(1, status.str()); + + if (keycode == 183) { /* Fullscreen => toggle fullscreen */ + if (!down) + ControlMessage("state", "fullscreen"); + return PP_TRUE; + } else if (keycode == 182) { /* Expos'e => minimize window */ + if (!down) + ControlMessage("state", "hide"); + return PP_TRUE; + } + + /* We delay sending Super-L, and only "press" it on mouse clicks and + * letter keys (a-z). This way, Home (Search+Left) appears without + * modifiers (instead of Super_L+Home) */ + if (keystr == "OSLeft") { + pending_super_l_ = down; + return PP_TRUE; + } + + bool letter = (keycode >= 65 && keycode <= 90); + if (letter && pending_super_l_ && down) SendKey(SUPER_L, 1); + SendKey(keysym, down ? 1 : 0); + if (letter && pending_super_l_ && !down) SendKey(SUPER_L, 0); + } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN || + event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || + event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { + pp::MouseInputEvent mouse_event(event); + pp::Point mouse_event_pos( + mouse_event.GetPosition().x() * scale_, + mouse_event.GetPosition().y() * scale_); + bool down = event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN; + + if (mouse_pos_.x() != mouse_event_pos.x() || + mouse_pos_.y() != mouse_event_pos.y()) { + pending_mouse_move_ = true; + mouse_pos_ = mouse_event_pos; + } + + std::ostringstream status; + status << "Mouse " << mouse_event_pos.x() << "x" + << mouse_event_pos.y(); + + if (event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE) { + status << " " << (down ? "DOWN" : "UP"); + status << " " << (mouse_event.GetButton()); + + SendClick(mouse_event.GetButton()+1, down ? 1 : 0); + } + + LogMessage(3, status.str()); + } else if (event.GetType() == PP_INPUTEVENT_TYPE_WHEEL) { + pp::WheelInputEvent wheel_event(event); + + mouse_wheel_x += wheel_event.GetDelta().x(); + mouse_wheel_y += wheel_event.GetDelta().y(); + + std::ostringstream status; + status << "MWd " << wheel_event.GetDelta().x() << "x" + << wheel_event.GetDelta().y(); + status << "MWt " << wheel_event.GetTicks().x() << "x" + << wheel_event.GetTicks().y(); + status << "acc " << mouse_wheel_x << "x" + << mouse_wheel_y; + LogMessage(2, status.str()); + + while (mouse_wheel_x <= -16) { + SendClick(6, 1); SendClick(6, 0); + mouse_wheel_x += 16; + } + while (mouse_wheel_x >= 16) { + SendClick(7, 1); SendClick(7, 0); + mouse_wheel_x -= 16; + } + + while (mouse_wheel_y <= -16) { + SendClick(5, 1); SendClick(5, 0); + mouse_wheel_y += 16; + } + while (mouse_wheel_y >= 16) { + SendClick(4, 1); SendClick(4, 0); + mouse_wheel_y -= 16; + } + } else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART || + event.GetType() == PP_INPUTEVENT_TYPE_TOUCHEND) { + /* FIXME: To be implemented */ + + pp::TouchInputEvent touch_event(event); + + int count = touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); + std::ostringstream status; + status << "TOUCH " << count; + for (int i = 0; i < count; i++) { + pp::TouchPoint tp = touch_event.GetTouchByIndex(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); + status << std::endl << tp.id() << "//" << tp.position().x() << "/" << tp.position().y() << "@" << tp.pressure(); + } + + LogMessage(0, status.str()); + } /* FIXME: Handle IMEInputEvents too */ + + return PP_TRUE; + } + +private: + /* Initialize Graphics context */ + void InitContext() { + if (view_rect_.width() <= 0 || view_rect_.height() <= 0) + return; + + scale_ = hidpi_ ? view_scale_ : 1.0f; + pp::Size new_size = pp::Size(view_rect_.width() * scale_, + view_rect_.height() * scale_); + + std::ostringstream status; + status << "InitContext " << new_size.width() << "x" << new_size.height() + << "s" << scale_; + LogMessage(0, status.str()); + + const bool kIsAlwaysOpaque = true; + context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque); + context_.SetScale(1.0f / scale_); + if (!BindGraphics(context_)) { + LogMessage(0, "Unable to bind 2d context!"); + context_ = pp::Graphics2D(); + return; + } + + size_ = new_size; + force_refresh_ = true; + } + + void ChangeResolution(int width, int height) { + std::ostringstream status; + status << "Asked for resolution " << width << "x" << height; + LogMessage(1, status.str()); + + if (connected_) { + struct resolution* r; + pp::VarArrayBuffer array_buffer(sizeof(*r)); + r = static_cast(array_buffer.Map()); + r->type = 'R'; + r->width = width; + r->height = height; + array_buffer.Unmap(); + SocketSend(array_buffer, false); + } else { /* Just assume we can take up the space */ + std::ostringstream status; + status << width/scale_ << "/" << height/scale_; + ControlMessage("resize", status.str()); + } + } + + /* Convert "IE"/JavaScript keycode to X11 KeySym, + * see http://unixpapa.com/js/key.html */ + uint32_t KeyCodeToKeySym(uint32_t keycode, std::string code) { + if (code == "ControlLeft") return 0xffe3; + if (code == "ControlRight") return 0xffe4; + if (code == "AltLeft") return 0xffe9; + if (code == "AltRight") return 0xffea; + if (code == "ShiftLeft") return 0xffe1; + if (code == "ShiftRight") return 0xffe2; + + if (keycode >= 65 && keycode <= 90) /* A to Z */ + return keycode+32; + if (keycode >= 48 && keycode <= 57) /* 0 to 9 */ + return keycode; + if (keycode >= 96 && keycode <= 105) /* KP 0 to 9 */ + return keycode-96+0xffb0; + if (keycode >= 112 && keycode <= 123) /* F1-F12 */ + return keycode-112+0xffbe; + + switch(keycode) { + case 8: return 0xff08; // backspace + case 9: return 0xff09; // tab + case 12: return 0xff9d; // num 5 + case 13: return 0xff0d; // enter + case 16: return 0xffe1; // shift (caught earlier!) + case 17: return 0xffe3; // control (caught earlier!) + case 18: return 0xffe9; // alt (caught earlier!) + case 19: return 0xff13; // pause + case 20: return 0xffe5; // caps lock + case 27: return 0xff1b; // esc + case 32: return 0x20; // space + case 33: return 0xff55; // page up + case 34: return 0xff56; // page down + case 35: return 0xff57; // end + case 36: return 0xff50; // home + case 37: return 0xff51; // left + case 38: return 0xff52; // top + case 39: return 0xff53; // right + case 40: return 0xff54; // bottom + case 42: return 0xff61; // print screen + case 45: return 0xff63; // insert + case 46: return 0xffff; // delete + case 91: return SUPER_L; // super + case 106: return 0xffaa; // num multiply + case 107: return 0xffab; // num plus + case 109: return 0xffad; // num minus + case 110: return 0xffae; // num dot + case 111: return 0xffaf; // num divide + case 144: return 0xff7f; // num lock (maybe better not to pass through???) + case 145: return 0xff14; // scroll lock + case 151: return 0x1008ff95; // WLAN + case 166: return 0x1008ff26; // back + case 167: return 0x1008ff27; // forward + case 168: return 0x1008ff73; // refresh + case 182: return 0x1008ff51; // "expos'e" ("F5") + case 183: return 0x1008ff59; // fullscreen/display + case 186: return 0x3b; // ; + case 187: return 0x3d; // = + case 188: return 0x2c; // , + case 189: return 0x2d; // - + case 190: return 0x2e; // . + case 191: return 0x2f; // / + case 192: return 0x60; // ` + case 219: return 0x5b; // [ + case 220: return 0x5c; // '\' + case 221: return 0x5d; // ] + case 222: return 0x27; // ' + case 229: return 0; // dead key (', `, ~): no way of knowing which... + } + + return 0x00; + } + + void SetTargetFPS(int new_target_fps) { + /* When increasing the fps, immediately ask for a frame, and force refresh + * the display (we probably just gained focus). */ + if (new_target_fps > target_fps_) { + force_refresh_ = true; + RequestScreen(request_token_); + } + target_fps_ = new_target_fps; + } + + /* Send a mouse click */ + void SendClick(int button, int down) { + struct mouseclick* mc; + + if (pending_super_l_ && down) SendKey(SUPER_L, 1); + + pp::VarArrayBuffer array_buffer(sizeof(*mc)); + mc = static_cast(array_buffer.Map()); + mc->type = 'C'; + mc->down = down; + mc->button = button; + array_buffer.Unmap(); + SocketSend(array_buffer, true); + + if (pending_super_l_ && !down) SendKey(SUPER_L, 0); + + /* That means we have focus */ + SetTargetFPS(FULLFPS); + } + + void SendKey(uint32_t keysym, int down) { + struct key* k; + pp::VarArrayBuffer array_buffer(sizeof(*k)); + k = static_cast(array_buffer.Map()); + k->type = 'K'; + k->down = down; + k->keysym = keysym; + array_buffer.Unmap(); + SocketSend(array_buffer, true); + + /* That means we have focus */ + SetTargetFPS(FULLFPS); + } + + /* Request the next framebuffer grab */ + /* The parameter is a token that must be equal to request_token_. + * This makes sure only one screen requests is waiting at one time + * (e.g. when changing frame rate), since we have no way of cancelling + * scheduled callbacks. */ + void RequestScreen(int32_t token) { + std::stringstream status; + status << "OnWaitEnd " << token << "/" << request_token_; + LogMessage(3, status.str()); + + if (!connected_) { + LogMessage(-1, "!connected"); + return; + } + + /* Check that this request is up to date, and that no other + * request is flying */ + if (token != request_token_ || screen_flying_) { + LogMessage(2, "Old token, or screen flying..."); + return; + } + screen_flying_ = true; + request_token_++; + + struct screen* s; + pp::VarArrayBuffer array_buffer(sizeof(*s)); + s = static_cast(array_buffer.Map()); + + s->type = 'S'; + s->shm = 1; + s->refresh = force_refresh_; + force_refresh_ = false; + s->width = image_data_->size().width(); + s->height = image_data_->size().height(); + s->paddr = (uint64_t)image_data_->data(); + uint64_t sig = ((uint64_t)rand() << 32) ^ rand(); + uint64_t* data = static_cast(image_data_->data()); + *data = sig; + s->sig = sig; + + array_buffer.Unmap(); + SocketSend(array_buffer, true); + } + + /* Last frame was displayed (Vsync-ed): allocate next buffer and request + frame. */ + void OnFlush(int32_t result = 0) { + PP_Time time_ = pp::Module::Get()->core()->GetTime(); + PP_Time deltat = time_-lasttime_; + + double delay = (target_fps_ > 0) ? (1.0/target_fps_ - deltat) : INFINITY; + + double cfps = deltat > 0 ? 1.0/deltat : 1000; + lasttime_ = time_; + k_++; + + avgfps_ = 0.9*avgfps_ + 0.1*cfps; + if ((k_ % ((int)avgfps_+1)) == 0 || debug_ >= 1) { + std::stringstream ss; + ss << "fps: " << (int)(cfps+0.5) << " (" << (int)(avgfps_+0.5) << ")" + << " delay: " << (int)(delay*1000) + << " deltat: " << (int)(deltat*1000) + << " target fps: " << (int)(target_fps_) + << " " << size_.width() << "x" << size_.height(); + LogMessage(0, ss.str()); + } + + LogMessage(5, "OnFlush"); + + screen_flying_ = false; + + /* Allocate next image. If size_ is the same, the previous buffer will + * be reused. */ + PP_ImageDataFormat format = pp::ImageData::GetNativeImageDataFormat(); + image_data_ = new pp::ImageData(this, format, size_, false); + + /* Request for next frame */ + if (isinf(delay)) { + return; + } else if (delay >= 0) { + pp::Module::Get()->core()->CallOnMainThread( + delay*1000, + callback_factory_.NewCallback(&CriatInstance::RequestScreen), + request_token_); + } else { + RequestScreen(request_token_); + } + } + + /* Paint the frame. */ + void Paint(bool blank = false) { + if (context_.is_null()) { + /* The current Graphics2D context is null, so updating and rendering is + * pointless. */ + flush_context_ = context_; + return; + } + + if (blank) { + uint32_t* data = (uint32_t*)image_data_->data(); + int size = image_data_->size().width()*image_data_->size().height(); + for (int i = 0; i < size; i++) { + if (debug_ == 0) + data[i] = 0xFF000000; + else + data[i] = 0xFF800000+i; + } + } + + /* Using Graphics2D::ReplaceContents is the fastest way to update the + * entire canvas every frame. */ + context_.ReplaceContents(image_data_); + + /* Store a reference to the context that is being flushed; this ensures + * the callback is called, even if context_ changes before the flush + * completes. */ + flush_context_ = context_; + context_.Flush( + callback_factory_.NewCallback(&CriatInstance::OnFlush)); + } + +private: + pp::CompletionCallbackFactory callback_factory_; + pp::Graphics2D context_; + pp::Graphics2D flush_context_; + pp::Rect view_rect_; + float view_scale_; + pp::Size size_; + float scale_; + + pp::ImageData* image_data_; + int k_; + + pp::WebSocket websocket_; + bool connected_; + bool screen_flying_; + pp::Var receive_var_; + int target_fps_; + int request_token_; + bool force_refresh_; + + bool pending_mouse_move_; + pp::Point mouse_pos_; + /* Mouse wheel accumulators */ + int mouse_wheel_x; + int mouse_wheel_y; + /* Super_L press has been delayed */ + bool pending_super_l_; + + /* Performance metrics */ + PP_Time lasttime_; + double avgfps_; + + /* Cursor cache */ + class Cursor { +public: + pp::ImageData img; + pp::Point hot; + }; + std::unordered_map cursor_cache_; + + /* Display to connect to */ + int display_; + int debug_; + bool hidpi_; +}; + +class CriatModule : public pp::Module { +public: + CriatModule() : pp::Module() {} + virtual ~CriatModule() {} + + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new CriatInstance(instance); + } +}; + +namespace pp { + +Module* CreateModule() { + return new CriatModule(); +} + +} /* namespace pp */ From bdda27ddf7a651ba05b720d82c0de32fce96bc33 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 14:32:40 -0700 Subject: [PATCH 016/334] criat: Javascript/HTML helper --- host-ext/.gitignore | 1 + host-ext/crouton/criat.nmf | 9 ++ host-ext/crouton/manifest.json | 5 +- host-ext/crouton/window.html | 69 ++++++++++++ host-ext/crouton/window.js | 192 +++++++++++++++++++++++++++++++++ 5 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 host-ext/crouton/criat.nmf create mode 100644 host-ext/crouton/window.html create mode 100644 host-ext/crouton/window.js diff --git a/host-ext/.gitignore b/host-ext/.gitignore index aa3cfaa96..952926fed 100644 --- a/host-ext/.gitignore +++ b/host-ext/.gitignore @@ -1,3 +1,4 @@ crouton.crx crouton.zip crouton.pem +crouton/criat.pexe diff --git a/host-ext/crouton/criat.nmf b/host-ext/crouton/criat.nmf new file mode 100644 index 000000000..37051eb42 --- /dev/null +++ b/host-ext/crouton/criat.nmf @@ -0,0 +1,9 @@ +{ + "program": { + "portable": { + "pnacl-translate": { + "url": "criat.pexe" + } + } + } +} diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 4d18b6786..1ca2cfabb 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "1.0.0", + "version": "2.0.0", "icons": { "48": "icon-48.png", "128": "icon-128.png" @@ -24,6 +24,7 @@ }, "permissions": [ "clipboardRead", - "clipboardWrite" + "clipboardWrite", + "tabs" ] } diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html new file mode 100644 index 000000000..2d05f3072 --- /dev/null +++ b/host-ext/crouton/window.html @@ -0,0 +1,69 @@ + + + + + + + + Crouton in a tab + + + + +
+ +
Initializing...
+
+ +
+
+ + diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js new file mode 100644 index 000000000..a49a71061 --- /dev/null +++ b/host-ext/crouton/window.js @@ -0,0 +1,192 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var CLOSE_TIMEOUT = 2; /* Close window x seconds after disconnect */ +var DEBUG_LEVEL = 2; /* If debug is enabled, use this level in NaCl */ + +var CriatModule_ = null; /* NaCl module */ +var listener_ = null; /* listener div element */ +var debug_ = 0; /* Debuging level, passed to NaCl module */ +var hidpi_ = 0; /* HiDPI mode */ +var display_ = null; /* Display number to use */ +var connected_ = false; +var closing_ = false; /* Disconnected, and waiting for the window to close */ + +/* NaCl module loaded */ +function moduleDidLoad() { + CriatModule_ = document.getElementById('criat'); + updateStatus('Starting...'); + handleResize(); + CriatModule_.postMessage('display:' + display_); + CriatModule_.postMessage('debug:' + debug_); + CriatModule_.postMessage('hidpi:' + hidpi_); + /* Register window with background page */ + chrome.extension.getBackgroundPage().registerCriat(display_, window); +} + +/* Change debugging level */ +function setDebug(debug) { + debug_ = (debug > 0) ? DEBUG_LEVEL : 0; + if (debug_ > 0) { + document.getElementById('content').style.paddingTop = "16px"; + document.getElementById('header').style.display = 'block'; + } else { + document.getElementById('content').style.paddingTop = "0px"; + document.getElementById('header').style.display = 'none'; + } + if (CriatModule_) { + CriatModule_.postMessage('debug:' + debug_); + handleResize(); + } +} + +/* Change HiDPI mode */ +function setHiDPI(hidpi) { + hidpi_ = hidpi; + if (CriatModule_) { + CriatModule_.postMessage('hidpi:' + hidpi_); + handleResize(); + } +} + +function updateStatus(message) { + var status = document.getElementById('status'); + if (status) { + status.textContent = message; + status.style.display = connected_ ? 'none' : 'block'; + } +} + +/* This function is called when a message is received from the NaCl module. */ +/* Message format is type:payload */ +function handleMessage(message) { + var str = message.data; + var type, payload, i; + if ((i = str.indexOf(":")) > 0) { + type = str.substr(0, i); + payload = str.substr(i+1); + } else { + type = "log"; + payload = str; + } + + console.log(message.data); + + if (type == "log") { + var logEl = document.getElementById('log'); + if (logEl) + logEl.textContent = message.data; + } else if (type == "status") { + updateStatus(payload); + } else if (type == "connected") { + connected_ = true; + updateStatus("Connected"); + } else if (type == "disconnected") { + connected_ = false; + if (debug_ < 1) { + closing_ = true; + updateStatus("Disconnected, closing window in " + + CLOSE_TIMEOUT + " seconds."); + setTimeout(function() { window.close() }, CLOSE_TIMEOUT*1000); + } else { + updateStatus("Disconnected, please close the window."); + } + } else if (type == "state" && payload == "fullscreen") { + /* Toggle full screen */ + chrome.windows.getCurrent(function(win) { + var newstate = (win.state == "fullscreen") ? + "maximized" : "fullscreen"; + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': newstate}, function(win) {}) + }) + } else if (type == "state" && payload == "hide") { + /* Hide window */ + chrome.windows.getCurrent(function(win) { + /* To make restore nicer, first exit full screen, then minimize */ + if (win.state == "fullscreen") { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': 'maximized'}, function(win) { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': 'minimized'}, function(win) {}) + }) + } else { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': 'minimized'}, function(win) {}) + } + }) + } else if (type == "resize") { + i = payload.indexOf("/"); + if (i < 0) return; + /* FIXME: Show scroll bars if the window is too small */ + var width = payload.substr(0, i); + var height = payload.substr(i+1); + var lwidth = listener_.clientWidth; + var lheight = listener_.clientHeight; + var marginleft = (lwidth-width)/2; + var margintop = (lheight-height)/2; + CriatModule_.style.marginLeft = Math.max(marginleft, 0) + "px"; + CriatModule_.style.marginTop = Math.max(margintop, 0) + "px"; + CriatModule_.width = width; + CriatModule_.height = height; + } +} + +/* Tell the module that the window was resized (this triggers a change of + * resolution, followed by a resize message. */ +function handleResize() { + console.log("resize! " + listener_.clientWidth + "/" + listener_.clientHeight); + if (CriatModule_) + CriatModule_.postMessage('resize:' + listener_.clientWidth + "/" + listener_.clientHeight); +} + +/* Called when window changes focus/visiblity */ +function handleFocusBlur(evt) { + /* Unfortunately, hidden/visibilityState is not able to tell when a window + * is not visible at all (e.g. in the background). + * See http://crbug.com/403061 */ + console.log("focus/blur: " + evt.type + ", focus=" + document.hasFocus() + + ", hidden=" + document.hidden + "/" + document.visibilityState); + if (!CriatModule_) + return; + + if (document.hasFocus()) { + CriatModule_.postMessage("focus:"); + } else { + if (closing_) + window.close(); + + if (!document.hidden) + CriatModule_.postMessage("blur:"); + else + CriatModule_.postMessage("hide:"); + } +} + +/* Start in full screen */ +chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': "fullscreen"}, function(win) {}) + +document.addEventListener('DOMContentLoaded', function() { + listener_ = document.getElementById('listener'); + listener_.addEventListener('load', moduleDidLoad, true); + listener_.addEventListener('message', handleMessage, true); + window.addEventListener('resize', handleResize); + window.addEventListener('focus', handleFocusBlur); + window.addEventListener('blur', handleFocusBlur); + document.addEventListener('visibilitychange', handleFocusBlur); + + /* Parse arguments */ + var args = location.search.substring(1).split('&'); + display_ = -1; + debug_ = 0; + for (var i = 0; i < args.length; i++) { + var keyval = args[i].split('=') + if (keyval[0] == "display") + display_ = keyval[1]; + else if (keyval[0] == "debug") + setDebug(keyval[1]); + else if (keyval[0] == "hidpi") + setHiDPI(keyval[1]); + } +}) From 7d21c35fabbb1eb9dd6c2ea3a142539b398640fb Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 16:42:04 -0700 Subject: [PATCH 017/334] targets/xiat: crouton-in-a-tab target file --- chroot-etc/xserverrc-xiat | 20 +++++++++++ targets/xiat | 74 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 chroot-etc/xserverrc-xiat create mode 100644 targets/xiat diff --git a/chroot-etc/xserverrc-xiat b/chroot-etc/xserverrc-xiat new file mode 100644 index 000000000..84ca7012b --- /dev/null +++ b/chroot-etc/xserverrc-xiat @@ -0,0 +1,20 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +logfile="/tmp/Xorg.crouton.$$.log" +for arg in "$@"; do + disp="`echo "$arg" | sed -n 's/^\:\([0-9]*\)$/\1/p'`" + if [ -n "$disp" ]; then + logfile="/tmp/Xorg.crouton.$disp.log" + fi +done + +XMETHOD='xiat' +XARGS="-nolisten tcp -config xorg-dummy.conf -logfile $logfile" +if [ -f /etc/crouton/xserverrc-local ]; then + . /etc/crouton/xserverrc-local +fi + +exec /usr/bin/Xorg $XARGS "$@" diff --git a/targets/xiat b/targets/xiat new file mode 100644 index 000000000..dc9d047b1 --- /dev/null +++ b/targets/xiat @@ -0,0 +1,74 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +REQUIRES='core audio extension' +PROVIDES='x11' +DESCRIPTION='Crouton in a tab X11 backend. Improves compatibility but lacks GPU accel.' +CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper findres xinit' +CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiat xserverrc-local.example' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +XMETHOD="${XMETHOD:-xiat}" + +install xorg xserver-xorg-video-dummy + +# Compile croutonfbserver +compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ + libx11-dev libxfixes-dev libxdamage-dev libxext-dev libxtst-dev + +tmp="`mktemp crouton.XXXXXX --tmpdir=/tmp`" +addtrap "rm -f '$tmp'" + +awk '/###MODELINES###/{exit}; 1' /etc/crouton/xorg-dummy.conf > "$tmp" + +# Generate a set of reasonable resolutions +echo ' +1280 800 l # Early models +1366 768 l # HD +768 1366 l # "P"HD +2560 1700 h # Pixel +1280 850 l # Pixel half-res +1536 864 l # Down/upscaled resolution +1920 1080 l # FHD +1920 1200 l # Standard 20" +2560 1600 l # Standard 30" +' | while read w h flags _; do + if [ -z "$w" ]; then + continue + fi + rate=60 # 60Hz refresh + scale2=2 # 2*DPI scale + case "$flags" in + h) scale2=4;; # 2 + *) scale2=2;; # 1 (low dpi) + esac + # Width: Allow for dock on sides + half-width window + dws="0 $((50*scale2/2)) $(((w-50*scale2/2)/2)) $((w/2))" # Measured: 47 + for dw in $dws; do + nw="$((w-dw))" + # Height: Allow for title bar/dock, or title bar only + # Measured: 28,33,36,75,80,83,85,95 + dhs="0 28 33 36 60 75 85 95 100" + for dh in $dhs; do + nh="$((h-(dh*scale2+1)/2))" + mhz="$((rate*(nw+3)*(nh+3)/1000000))" + echo " Modeline \"${nw}x${nh}\" ${mhz} \ +${nw} $((nw+1)) $((nw+2)) $((nw+3)) \ +${nh} $((nh+1)) $((nh+2)) $((nh+3))" >> "$tmp" + done + done +done + +awk 'p; /###MODELINES###/{p=1}' /etc/crouton/xorg-dummy.conf >> "$tmp" +mv -f "$tmp" /etc/X11/xorg-dummy.conf +chmod 0644 /etc/X11/xorg-dummy.conf +undotrap + +TIPS="$TIPS +You can flip through your running chroot desktops and Chromium OS by hitting +Ctrl+Alt+Shift+Back and Ctrl+Alt+Shift+Forward. +" + +### append x11-common From 77ac835b36fd8603887d5d44afd3c7ec339a600b Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 20 Oct 2014 13:59:38 -0700 Subject: [PATCH 018/334] extension: Open criat windows on demand. --- chroot-bin/croutoncycle | 88 ++++++++++++++++++++--- host-ext/crouton/background.js | 126 ++++++++++++++++++++++++++++++++- host-ext/crouton/popup.html | 4 ++ 3 files changed, 206 insertions(+), 12 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 7e6c6174a..02c239ab4 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -18,6 +18,9 @@ Cycles through running graphical chroots. # - aura: Chromium OS # - 0: Chromium OS X11 display (non-aura window) # - 1-9: chroot displays +# s: informs of a Chromium OS window change (called from extension): +# - 0: any Chromium OS window +# - n: criat window: X11 display number case "$1" in [Ll]*) cmd='l';; @@ -25,11 +28,34 @@ case "$1" in [Cc]*) cmd='0';; [Pp]*) cmd='p';; [Nn]*) cmd='n';; +[Ss]*) cmd='s' disp="${1#s}";; :*) cmd=":$((${1#:}))";; [0-9]*) cmd="$(($1))";; *) echo "$USAGE" 1>&2; exit 2;; esac +# FIXME: This is our third copy, we need to move it to a common file +PIPEDIR='/tmp/crouton-ext' +PIPEIN="$PIPEDIR/in" +PIPEOUT="$PIPEDIR/out" +PIPELOCK="$PIPEDIR/lock" +CRIATDISPLAY="$PIPEDIR/criat-display" +CROUTONCLIPPIDFILE='/tmp/crouton-lock/clip' + +# Write a command to croutonwebsocket, and read back response +websocketcommand() { + # Check that $PIPEDIR and the FIFO pipes exist + if ! [ -d "$PIPEDIR" -a -p "$PIPEIN" -a -p "$PIPEOUT" ]; then + echo "EError $PIPEIN or $PIPEOUT are not pipes." + exit 0 + fi + + if ! timeout 3 \ + sh -c "flock 5; cat > '$PIPEIN'; cat '$PIPEOUT'" 5>"$PIPELOCK"; then + echo "EError timeout" + fi +} + # Returns the chroot name of an X11 display specified in $1 on stdout getname() { local name='' @@ -47,24 +73,42 @@ mkdir -m 775 -p /tmp/crouton-lock exec 3>/tmp/crouton-lock/cycle flock 3 +# set display command from extension +if [ "$cmd" = 's' ]; then + echo "$disp" > "$CRIATDISPLAY" + if [ -s "$CROUTONCLIPPIDFILE" ]; then + kill -USR1 "`cat "$CROUTONCLIPPIDFILE"`" || true + fi + exit 0 +fi + # Ensure environment sanity export XAUTHORITY='' +# Set to y if there is any xiat instance running +xiatactive='' + # Prepare display list for easier looping displist='' for disp in /tmp/.X*-lock; do disp="${disp#*X}" disp=":${disp%-lock}" - # Only add VT-based chroots here + # Only add VT-based and xiat-based chroots here (that excludes Xephyr) if [ "$disp" = ':0' ]; then continue elif DISPLAY="$disp" xprop -root 'XFree86_VT' 2>/dev/null \ | grep -q 'INTEGER'; then displist="$displist $disp" + elif DISPLAY="$disp" xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ + | grep -q '= "xiat"$'; then + displist="$displist $disp" + xiatactive='y' fi done # List windows on :0. Includes aura winlist="`host-x11 croutonwmtools list nim | sort | awk '{ printf $NF " " }'`" +aurawin="`host-x11 croutonwmtools list ni | \ + awk '$1 == "aura_root_0" { print $NF; exit }'`" # Combine the two fulllist="$winlist$displist" @@ -77,6 +121,13 @@ if [ "$tty" = 'tty1' ]; then for disp in $winlist; do if [ "${disp%"*"}" != "$disp" ]; then curdisp="$disp" + if [ -n "$xiatactive" -a "${disp%"*"}" = "$aurawin" -a \ + -s "$CRIATDISPLAY" ]; then + criatdisp="`cat $CRIATDISPLAY`" + if [ "$criatdisp" -ge 0 ]; then + curdisp=":$criatdisp" + fi + fi break fi done @@ -217,19 +268,38 @@ if [ "${destdisp#:}" = "$destdisp" ]; then sleep .1 fi croutonwmtools raise "${destdisp%"*"}" + + if [ -n "$xiatactive" -a "${destdisp%"*"}" = "$aurawin" ]; then + STATUS="`echo "X-1" | websocketcommand`" + if [ "$STATUS" != 'XOK' ]; then + echo "${STATUS#?}" 1>&2 + exit 1 + fi + fi else export DISPLAY="$destdisp" - dest="`xprop -root 'XFree86_VT' 2>/dev/null`" - dest="${dest##* }" - if [ "${dest#[1-9]}" = "$dest" ]; then - dest='1' + if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiat"$'; then + if [ "$tty" != 'tty1' ]; then + sudo -n chvt 1 + sleep .1 + fi + STATUS="`echo "X${destdisp#:}" | websocketcommand`" + if [ "$STATUS" != 'XOK' ]; then + echo "${STATUS#?}" 1>&2 + exit 1 + fi + else + dest="`xprop -root 'XFree86_VT' 2>/dev/null`" + dest="${dest##* }" + if [ "${dest#[1-9]}" = "$dest" ]; then + dest='1' + fi + sudo -n chvt "$dest" fi - sudo -n chvt "$dest" fi -CROUTONPIDFILE='/tmp/crouton-lock/clip' -if [ -s "$CROUTONPIDFILE" ]; then - kill -USR1 "`cat "$CROUTONPIDFILE"`" || true +if [ -s "$CROUTONCLIPPIDFILE" ]; then + kill -USR1 "`cat "$CROUTONCLIPPIDFILE"`" || true fi # Wait a flip and then refresh the display for good measure diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index c60e95e3a..454e7bc77 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -8,7 +8,7 @@ var VERSION = 2; /* Note: the extension must always be backward compatible */ var MAXLOGGERLEN = 20; var RETRY_TIMEOUT = 5; var UPDATE_CHECK_INTERVAL = 15*60; /* Check for updates every 15' at most */ -var WINDOW_UPDATE_INTERVAL = 15*60; /* Update window list every 15' at most */ +var WINDOW_UPDATE_INTERVAL = 15; /* Update window list every 15" at most */ /* String to copy to the clipboard if it should be empty */ var DUMMY_EMPTYSTRING = "%"; @@ -25,6 +25,7 @@ var websocket_ = null; /* Active connection */ /* State variables */ var debug_ = false; +var hidpi_ = false; /* true if criat windows should be opened in HiDPI mode */ var enabled_ = true; /* true if we are trying to connect */ var active_ = false; /* true if we are connected to a server */ var error_ = false; /* true if there was an error during the last connection */ @@ -39,6 +40,9 @@ var sversion_ = 0; /* Version of the websocket server */ var logger_ = []; /* Array of status messages: [LogLevel, time, message] */ var windows_ = []; /* Array of windows (xorg, xephyr, host-x11, etc) */ +var criat_win_ = {}; /* Array of criat windows (.id, .window: window element) */ +var focus_win_ = -1; /* Focused criat window. -1 if no criat window focused. */ + /* Set the current status string. * active is a boolean, true if the WebSocket connection is established. */ function setStatus(status, active) { @@ -96,11 +100,27 @@ function updateWindowList(force) { if (force || lastwindowlistupdate_ == null || (currenttime-lastwindowlistupdate_) > 1000*WINDOW_UPDATE_INTERVAL) { printLog("Sending window list request", LogLevel.DEBUG); + websocket_.send("Cs" + focus_win_); websocket_.send("Cl"); lastwindowlistupdate_ = currenttime; } } +/* Called from criat (window.js), so we can directly access each window */ +function registerCriat(display, window) { + if (criat_win_[display] && criat_win_[display].id >= 0) { + criat_win_[display].window = window; + } +} + +/* Close the popup window */ +function closePopup() { + var views = chrome.extension.getViews({type: "popup"}); + for (var i = 0; i < views.length; views++) { + views[i].close(); + } +} + /* Update the icon, and refresh the popup page */ function refreshUI() { updateWindowList(false); @@ -157,13 +177,37 @@ function refreshUI() { debugcheck.onclick = function() { debug_ = debugcheck.checked; refreshUI(); + var disps = Object.keys(criat_win_); + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].window) + criat_win_[disps[i]].window.setDebug(debug_?1:0); + } } debugcheck.checked = debug_; + /* Update hidpi mode according to checkbox state. */ + hidpicheck = view.document.getElementById("hidpicheck"); + if (window.devicePixelRatio > 1) { + hidpicheck.onclick = function() { + hidpi_ = hidpicheck.checked; + refreshUI(); + var disps = Object.keys(criat_win_); + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].window) + criat_win_[disps[i]].window.setHiDPI(hidpi_?1:0); + } + } + hidpicheck.disabled = false; + } else { + hidpicheck.disabled = true; + } + hidpicheck.checked = hidpi_; + /* Update status box */ view.document.getElementById("info").textContent = status_; /* Update window table */ + /* FIXME: Improve UI */ windowlist = view.document.getElementById("windowlist"); while (windowlist.rows.length > 0) { @@ -181,7 +225,10 @@ function refreshUI() { cell2.className = "name"; cell2.innerHTML = windows_[i].substring(3) cell2.onclick = (function(i) { return function() { - websocket_.send("C" + i); + if (active_) { + websocket_.send("C" + i); + closePopup(); + } } })(i); } @@ -359,6 +406,49 @@ function websocketMessage(evt) { } refreshUI(); break; + case 'X': /* Ask to open a crouton-in-a-tab window */ + display = parseInt(payload); + if (display <= 0) { + /* Get out of full screen, for the current window */ + var disps = Object.keys(criat_win_); + for (var i = 0; i < disps.length; i++) { + var winid = criat_win_[disps[i]].id; + chrome.windows.update(winid, {focused: false}); + chrome.windows.get(winid, function(win) { + chrome.windows.update(winid, {'state': 'minimized'}, + function(win) {}); + }) + } + } else if (criat_win_[display] && criat_win_[display].id >= 0 && + (!criat_win_[display].window || + !criat_win_[display].window.closing)) { + /* focus/full screen an existing window */ + var winid = criat_win_[disps[i]].id; + chrome.windows.update(winid, {focused: true}); + chrome.windows.get(winid, function(win) { + if (win.state == "maximized") + chrome.windows.update(winid, {'state': 'fullscreen'}, + function(win) {}) + }) + } else { + /* Open a new window */ + criat_win_[display] = new Object(); + criat_win_[display].id = -1; + criat_win_[display].window = null; + chrome.windows.create({ 'url': "window.html?display=" + display + + "&debug=" + (debug_ ? 1 : 0) + + "&hidpi=" + (hidpi_ ? 1 : 0), + 'type': "popup" }, + function(newwin) { + criat_win_[display].id = newwin.id; + focus_win_ = display; + if (active_ && sversion_ >= 2) + websocket_.send("Cs" + focus_win_); + }); + } + websocket_.send("XOK"); + closePopup(); + break; case 'P': /* Ping */ websocket_.send(received_msg); break; @@ -431,7 +521,8 @@ function error(str, enabled) { enabled_ = enabled; error_ = true; refreshUI(); - websocket_.close(); + if (websocket != null) + websocket_.close(); /* Force check for extension update (possible reason for the error) */ checkUpdate(true); } @@ -468,6 +559,35 @@ chrome.runtime.getPlatformInfo(function(platforminfo) { chrome.runtime.onUpdateAvailable.addListener(function(details) { updateAvailable(details.version); }); + + /* Monitor window focus changes and report to croutonclip */ + chrome.windows.onFocusChanged.addListener(function(windowid) { + var disps = Object.keys(criat_win_); + nextfocus_win_ = -1; + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].id == windowid) { + nextfocus_win_ = disps[i]; + break; + } + } + if (focus_win_ != nextfocus_win_) { + focus_win_ = nextfocus_win_; + if (active_ && sversion_ >= 2) + websocket_.send("Cs" + focus_win_); + printLog("Window " + focus_win_ + " focused", LogLevel.DEBUG); + } + }) + + chrome.windows.onRemoved.addListener(function(windowid) { + var disps = Object.keys(criat_win_); + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].id == windowid) { + criat_win_[disps[i]].id = -1; + criat_win_[disps[i]].window = null; + printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); + } + } + }) } else { /* Disable the icon on non-Chromium OS. */ chrome.browserAction.setTitle( diff --git a/host-ext/crouton/popup.html b/host-ext/crouton/popup.html index c7c97c9f9..c4f901e19 100644 --- a/host-ext/crouton/popup.html +++ b/host-ext/crouton/popup.html @@ -63,6 +63,8 @@ td.value.debug { color: #808080; } td.value.info { color: #008000; } td.value.error { color: #800000; } + + input:disabled+label { color:#cccccc; } @@ -75,6 +77,8 @@

crouton integration

+ + From bbc200bc85ac4c39a1b55400b3dff190b6aab5c8 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 7 Oct 2014 18:01:19 -0700 Subject: [PATCH 019/334] xinitrc-wrapper: Add xiat handling. No xbindkeys, croutoncycle to show up display. --- chroot-bin/croutonxinitrc-wrapper | 122 +++++++++++++++++++----------- 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 928b3d1bd..1be08125b 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -42,12 +42,17 @@ else fi # Run crouton-specific commands: +xmethod="`readlink -f '/etc/X11/xinit/xserverrc'`" +xmethod="${xmethod##*-}" # Record the name of the chroot in the root window properties if [ -f '/etc/crouton/name' ] && hash xprop 2>/dev/null; then xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME "`cat '/etc/crouton/name'`" fi +# Record the crouton XMETHOD in the root window properties +xprop -root -f CROUTON_XMETHOD 8s -set CROUTON_XMETHOD "$xmethod" + # Launch the powerd poker daemon croutonpowerd --daemon & @@ -56,27 +61,6 @@ if hash croutonclip 2>/dev/null; then croutonclip & fi -# Apply the Chromebook keyboard map if installed. -if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then - setxkbmap -model chromebook -fi - -# Launch key binding daemon -xmethod="`readlink -f '/etc/X11/xinit/xserverrc'`" -xmethod="${xmethod##*-}" - -XMETHOD="$xmethod" xbindkeys -fg /etc/crouton/xbindkeysrc.scm - -# Launch xbindkeys for the Chromium OS X server if it isn't running -mkdir -m 775 -p /tmp/crouton-lock -{ - # Only let one instance *really* run at a time - flock 3 - XMETHOD='' host-x11 xbindkeys -n -fg /etc/crouton/xbindkeysrc.scm & - trap "kill '$!' 2>/dev/null" HUP INT TERM - wait "$!" || true -} 3>/tmp/crouton-lock/xbindkeys & - # Pass through the host cursor and correct mousewheels on xephyr if [ "$xmethod" = 'xephyr' ]; then host-x11 croutoncursor "$DISPLAY" & @@ -86,32 +70,84 @@ if [ "$xmethod" = 'xephyr' ]; then croutonwheel $CROUTON_WHEEL_PARAMS & fi -# Launch touchegg if it is requested. -toucheggconf='/etc/touchegg.conf' -if [ -f "$toucheggconf" ]; then - mkdir -p "$HOME/.config/touchegg" - ln -sf "$toucheggconf" "$HOME/.config/touchegg/" - touchegg 2>/dev/null & -fi +# xbindkeys and other input-related apps are not needed for criat +if [ "$xmethod" != "xiat" ]; then + # Apply the Chromebook keyboard map if installed. + if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then + setxkbmap -model chromebook + fi + + # Launch key binding daemon + XMETHOD="$xmethod" xbindkeys -fg /etc/crouton/xbindkeysrc.scm + + # Launch xbindkeys for the Chromium OS X server if it isn't running + mkdir -m 775 -p /tmp/crouton-lock + { + # Only let one instance *really* run at a time + flock 3 + XMETHOD='' host-x11 xbindkeys -n -fg /etc/crouton/xbindkeysrc.scm & + trap "kill '$!' 2>/dev/null" HUP INT TERM + wait "$!" || true + } 3>/tmp/crouton-lock/xbindkeys & + + # Launch touchegg if it is requested. + toucheggconf='/etc/touchegg.conf' + if [ -f "$toucheggconf" ]; then + mkdir -p "$HOME/.config/touchegg" + ln -sf "$toucheggconf" "$HOME/.config/touchegg/" + touchegg 2>/dev/null & + fi + + # Configure trackpad settings if needed + if synclient >/dev/null 2>&1; then + case "`awk -F= '/_RELEASE_BOARD=/{print $2}' '/var/host/lsb-release'`" in + butterfly*|falco*) + SYNCLIENT="FingerLow=1 FingerHigh=5 $SYNCLIENT";; + parrot*|peppy*|wolf*) + SYNCLIENT="FingerLow=5 FingerHigh=10 $SYNCLIENT";; + esac + if [ -n "$SYNCLIENT" ]; then + synclient $SYNCLIENT + fi + fi -# Configure trackpad settings if needed -if synclient >/dev/null 2>&1; then - case "`awk -F= '/_RELEASE_BOARD=/{print $2}' '/var/host/lsb-release'`" in - butterfly*|falco*) - SYNCLIENT="FingerLow=1 FingerHigh=5 $SYNCLIENT";; - parrot*|peppy*|wolf*) - SYNCLIENT="FingerLow=5 FingerHigh=10 $SYNCLIENT";; - esac - if [ -n "$SYNCLIENT" ]; then - synclient $SYNCLIENT + # Make sure tap-to-click is enabled + if hash xinput 2>/dev/null; then + for id in `host-x11 xinput --list --id-only`; do + host-x11 xinput set-prop "$id" 'Tap Paused' 0 2>/dev/null || true + done fi fi -# Make sure tap-to-click is enabled -if hash xinput 2>/dev/null; then - for id in `host-x11 xinput --list --id-only`; do - host-x11 xinput set-prop "$id" 'Tap Paused' 0 2>/dev/null || true - done +# Crouton-in-a-tab: Start fbserver and launch display +if [ "$xmethod" = 'xiat' ]; then + # Set resolution to a default 1024x768, this is important so that the DPI + # looks reasonable when the WM/DE start. + findres 1024 768 + croutonfbserver "$DISPLAY" & + ( + try=1 + PIPEDIR='/tmp/crouton-ext' + CRIATDISPLAY="$PIPEDIR/criat-display" + # criat-display may be out of date, clear it so we start fresh. + # FIXME: It would be cleaner to simply force croutoncycle + rm -f "$CRIATDISPLAY" 2>/dev/null + while ! croutoncycle "${DISPLAY#:}"; do + echo "Cannot connect to extension, retrying..." + if [ "$try" -eq 10 ]; then + echo "\ +Unable to start display, make sure the crouton extension is installed +and enabled. (download from http://goo.gl/OVQOEt)" 1>&2 + elif [ "$try" -ge 20 ]; then + # Send sigterm to the parent process + kill "$$" + break + fi + sleep 1 + try="$((try+1))" + done + echo "Connected to extension, launched crouton in a tab window." 1>&2 + ) & fi # Shell is the leader of a process group, so signals sent to this process are From 4a39af554dfcff4a4c741c9779282de1431da4de Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 14 Oct 2014 11:30:59 -0700 Subject: [PATCH 020/334] croutonurlhandler: 3 seconds timeout for commands. --- chroot-bin/croutonurlhandler | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/chroot-bin/croutonurlhandler b/chroot-bin/croutonurlhandler index b86d5ae59..64973e497 100755 --- a/chroot-bin/croutonurlhandler +++ b/chroot-bin/croutonurlhandler @@ -20,11 +20,10 @@ websocketcommand() { exit 0 fi - ( - flock 5 - cat > "$PIPEIN" - cat "$PIPEOUT" - ) 5>"$PIPELOCK" + if ! timeout 3 \ + sh -c "flock 5; cat > '$PIPEIN'; cat '$PIPEOUT'" 5>"$PIPELOCK"; then + echo "EError timeout" + fi } noswitch='' From 59edcc0443125fe216b998ec9e3f81b3f12c1c3f Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 15 Oct 2014 16:50:55 -0700 Subject: [PATCH 021/334] croutoncycle: Fix for non-x11 aura --- chroot-bin/croutoncycle | 51 ++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 02c239ab4..2f2c1e7b1 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -88,6 +88,9 @@ export XAUTHORITY='' # Set to y if there is any xiat instance running xiatactive='' +# Set to y if aura is running without a X server +noaurax='' + # Prepare display list for easier looping displist='' for disp in /tmp/.X*-lock; do @@ -105,17 +108,31 @@ for disp in /tmp/.X*-lock; do xiatactive='y' fi done -# List windows on :0. Includes aura -winlist="`host-x11 croutonwmtools list nim | sort | awk '{ printf $NF " " }'`" -aurawin="`host-x11 croutonwmtools list ni | \ +# Detect if a server is running on :0 +if host-x11 croutonwmtools list 1 >/dev/null 2>&1; then + # List windows on :0. Includes aura + winlist="`host-x11 croutonwmtools list nim | \ + sort | awk '{ printf $NF " " }'`" + aurawin="`host-x11 croutonwmtools list ni | \ awk '$1 == "aura_root_0" { print $NF; exit }'`" +else + # No X11 server + noaurax='y' + winlist="aura*" + aurawin="aura" +fi # Combine the two fulllist="$winlist$displist" fulllist="${fulllist% }" # Figure out the current display number -tty="`cat '/sys/class/tty/tty0/active'`" +if [ -z "$noaurax" ]; then + tty="`cat '/sys/class/tty/tty0/active'`" +else + tty="tty1" +fi + if [ "$tty" = 'tty1' ]; then # Either in Chromium OS, xephyr chroot, or window. Active window is starred. for disp in $winlist; do @@ -150,7 +167,13 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then chromiumos="`awk -F= '/_RELEASE_NAME=/{print $2}' \ '/var/host/lsb-release'`" fi - host-x11 croutonwmtools list nim | sort | while read -r line; do + ( + if [ -z "$noaurax" ]; then + host-x11 croutonwmtools list nim + else + echo "aura_root_0 aura*" + fi + ) | sort | while read -r line; do disp="${line##* }" line="${line% *}" number='0' @@ -253,7 +276,7 @@ if [ "$destdisp" = "$curdisp" ]; then fi # Make sure tap-to-click is enabled -if hash xinput 2>/dev/null; then +if [ -z "$noaurax" ] && hash xinput 2>/dev/null; then for id in `host-x11 xinput --list --id-only`; do host-x11 xinput set-prop "$id" 'Tap Paused' 0 2>/dev/null || true done @@ -261,13 +284,15 @@ fi # Determine if the target display is on a VT if [ "${destdisp#:}" = "$destdisp" ]; then - eval "`host-x11`" - # Raise the right window after chvting, so that it can update - if [ "$tty" != 'tty1' ]; then - sudo -n chvt 1 - sleep .1 + if [ -z "$noaurax" ]; then + eval "`host-x11`" + # Raise the right window after chvting, so that it can update + if [ "$tty" != 'tty1' ]; then + sudo -n chvt 1 + sleep .1 + fi + croutonwmtools raise "${destdisp%"*"}" fi - croutonwmtools raise "${destdisp%"*"}" if [ -n "$xiatactive" -a "${destdisp%"*"}" = "$aurawin" ]; then STATUS="`echo "X-1" | websocketcommand`" @@ -279,7 +304,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then else export DISPLAY="$destdisp" if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiat"$'; then - if [ "$tty" != 'tty1' ]; then + if [ -z "$noaurax" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 fi From 6c96cc8dd81ee46d35e80830bca63f56aac86c86 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 20 Oct 2014 18:28:08 -0700 Subject: [PATCH 022/334] xinit: Never use display :0 --- chroot-bin/xinit | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chroot-bin/xinit b/chroot-bin/xinit index 3116ab072..f1256a9d3 100755 --- a/chroot-bin/xinit +++ b/chroot-bin/xinit @@ -22,7 +22,8 @@ for arg in "$@"; do fi done -disp=0 +# Never use display :0 (confusing if aura does not use X11) +disp=1 while [ -f "/tmp/.X$disp-lock" ]; do disp=$((disp+1)) done From b41a911461785ed66f2922c6f5ac6a2b923ef688 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 21 Oct 2014 10:05:42 -0700 Subject: [PATCH 023/334] gencrx.sh: Compile NaCl module. --- host-ext/gencrx.sh | 23 ++++++++++++++++------- host-ext/nacl_src/Makefile | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/host-ext/gencrx.sh b/host-ext/gencrx.sh index 142858ffd..f847015ff 100755 --- a/host-ext/gencrx.sh +++ b/host-ext/gencrx.sh @@ -6,17 +6,17 @@ # This script generates a crx extension package, and is meant to be used by # developers: Users should download the extension from the Web Store. # -# We could leverage on Chrome to build the extension, using something like -# /opt/google/chrome/chrome --pack-extension=crouton -# However many versions cannot build the package without crashing: -# - 27.0.1453.116 aborts at the end of the process, but the package still -# looks fine. -# - 28.0.1500.95 aborts before creating the extension. +# Prerequistes: +# - NaCl SDK. Path specified with NACL_SDK_ROOT (e.g. ~/naclsdk/pepper_35) +# - zip, openssl # -# This code is loosely based a script found along the CRX file format +# This code is loosely based on a script found along the CRX file format # specification: http://developer.chrome.com/extensions/crx.html +set -e + EXTNAME="crouton" +CRIAT_PEXE="$EXTNAME/criat.pexe" cd "`dirname "$0"`" @@ -24,6 +24,15 @@ rm -f "$EXTNAME.crx" "$EXTNAME.zip" trap "rm -f '$EXTNAME.sig' '$EXTNAME.pub'" 0 +rm "$CRIAT_PEXE" +# Build NaCl module +make -C nacl_src clean +make -C nacl_src +if [ ! -f "$CRIAT_PEXE" ]; then + echo "$CRIAT_PEXE not created as expected" 1>&2 + exit 1 +fi + # Create zip file ( cd $EXTNAME; zip -qr -9 -X "../$EXTNAME.zip" . ) diff --git a/host-ext/nacl_src/Makefile b/host-ext/nacl_src/Makefile index fc5f0a556..642b9f308 100644 --- a/host-ext/nacl_src/Makefile +++ b/host-ext/nacl_src/Makefile @@ -7,7 +7,7 @@ VALID_TOOLCHAINS := pnacl -NACL_SDK_ROOT ?= $(HOME)/nacl_sdk/pepper_35 +NACL_SDK_ROOT ?= $(HOME)/nacl_sdk/pepper_37 ../crouton/criat.pexe: pnacl/Release/criat.pexe cp pnacl/Release/criat.pexe ../crouton/criat.pexe From 274916e1f98ccc041801ea277996da6c883fbe3d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 09:46:30 -0700 Subject: [PATCH 024/334] croutonfindnacl: documentation. --- chroot-bin/croutonfindnacl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/chroot-bin/croutonfindnacl b/chroot-bin/croutonfindnacl index dd42f3714..bb2f0bdcc 100755 --- a/chroot-bin/croutonfindnacl +++ b/chroot-bin/croutonfindnacl @@ -3,10 +3,34 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# croutonfindnacl address signature ["pids"] +# +# This script is used by croutonfbserver to find the nacl_helper process it is +# connected to, and, in particular, the file descriptor corresponding to the shm +# memory that the nacl_helper process shares with Chromium. +# +# - address: NaCl-space address of the shared memory (hexadecimal). We assume +# that the NaCl/hardware memory mapping conserves the address, +# possibly with a prefix in the MSBs. +# - signature: random 8 byte pattern (hexadecimal, machine byte order) that is +# written at the beginning of the shared buffer by the NaCl +# application. The first 8 bytes of each candidate buffer is read, +# guaranteeing that the correct buffer is returned. +# - pids: (normally ununsed, defaults to all processes named "nacl_helper") +# Space-separated list of PIDs to scan for. +# +# On success, prints "pid:filename" and exits with code 0. +# On error (shm not found, invalid parameters), exits with code >0. + set -e VERBOSE= +if [ "$#" -lt 2 -o "$#" -gt 3 ]; then + echo "Invalid parameters" + exit 2 +fi + ADDRESS="$1" PATTERN="$2" PIDS="${3:-"`pgrep nacl_helper`"}" From 8f755e40f40dd1a20c644d16212fef4b0c5b94b3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 10:04:00 -0700 Subject: [PATCH 025/334] findres: Fixups. --- chroot-bin/findres | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chroot-bin/findres b/chroot-bin/findres index 99c3db308..db9e60048 100755 --- a/chroot-bin/findres +++ b/chroot-bin/findres @@ -24,15 +24,15 @@ res="`xrandr | awk ' BEGIN { tw='"$TARGETW"' th='"$TARGETH"' - penalty = 1000000 + oversize_penalty = 1000000 } $1 ~ /[0-9]+x[0-9]+/ { res = $1; sub(/_[0-9]+/, "", res) w = res; sub(/x[0-9]*/, "", w); w = int(w) h = res; sub(/[0-9]*x/, "", h); h = int(h) score = abs(tw-w) + abs(th-h) - if (w > tw) score += penalty - if (h > th) score += penalty + if (w > tw) score += oversize_penalty + if (h > th) score += oversize_penalty if (!cmode || score < cscore) { cmode = $1 cscore = score @@ -45,7 +45,7 @@ res="`xrandr | awk ' '`" if [ -z "$res" ]; then - echo Cannot find resolution 1>&2 + echo "Cannot find resolution" 1>&2 exit 1 fi From 003f0704c324f519742a5013f3f2d0f68d86cbd5 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 10:42:09 -0700 Subject: [PATCH 026/334] croutonxinitrc-wrapper: Only start DE/WM after extension has connected --- chroot-bin/croutonxinitrc-wrapper | 74 ++++++++++++++++--------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 1be08125b..328275ba7 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -11,6 +11,7 @@ cmd='' extraargs='' binary='' +ret=0 # This part is a translation of what is found in xorg's xinit.c @@ -123,50 +124,51 @@ fi if [ "$xmethod" = 'xiat' ]; then # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. - findres 1024 768 + findres 1024 768 > /dev/null croutonfbserver "$DISPLAY" & - ( - try=1 - PIPEDIR='/tmp/crouton-ext' - CRIATDISPLAY="$PIPEDIR/criat-display" - # criat-display may be out of date, clear it so we start fresh. - # FIXME: It would be cleaner to simply force croutoncycle - rm -f "$CRIATDISPLAY" 2>/dev/null - while ! croutoncycle "${DISPLAY#:}"; do - echo "Cannot connect to extension, retrying..." - if [ "$try" -eq 10 ]; then - echo "\ + + try=1 + PIPEDIR='/tmp/crouton-ext' + CRIATDISPLAY="$PIPEDIR/criat-display" + # criat-display may be out of date, clear it so we start fresh. + # FIXME: It would be cleaner to simply force croutoncycle + rm -f "$CRIATDISPLAY" 2>/dev/null + while ! croutoncycle "${DISPLAY#:}"; do + echo "Cannot connect to extension, retrying..." + if [ "$try" -ge 10 ]; then + echo "\ Unable to start display, make sure the crouton extension is installed -and enabled. (download from http://goo.gl/OVQOEt)" 1>&2 - elif [ "$try" -ge 20 ]; then - # Send sigterm to the parent process - kill "$$" - break - fi - sleep 1 - try="$((try+1))" - done +and enabled, and up to date. (download from http://goo.gl/OVQOEt)" 1>&2 + ret=1 + break + fi + sleep 1 + try="$((try+1))" + done + if [ "$ret" -eq 0 ]; then echo "Connected to extension, launched crouton in a tab window." 1>&2 - ) & + fi fi -# Shell is the leader of a process group, so signals sent to this process are -# propagated to its children. We ignore signals in this process, but the child -# handles them and exits. We use a no-op handler, as "" causes the signal to be -# ignored in children as well (see NOTES in "man 2 sigaction" for details). -# This process then runs exit commands, and terminates. -trap "true" HUP INT TERM +# Only run if no error occured before (e.g. cannot connect to extension) +if [ "$ret" -eq 0 ]; then + # Shell is the leader of a process group, so signals sent to this process + # are propagated to its children. We ignore signals in this process, but the + # child handles them and exits. We use a no-op handler, as "" causes the + # signal to be ignored in children as well (see NOTES in "man 2 sigaction" + # for details). This process then runs exit commands, and terminates. + trap "true" HUP INT TERM + + # Run the client itself if it is executable, otherwise run it in a shell. + if [ -n "$binary" -o -x "$cmd" ]; then + "$cmd" $extraargs "$@" || ret=$? + else + /bin/sh "$cmd" $extraargs "$@" || ret=$? + fi -# Run the client itself if it is executable, otherwise run it in a shell. -ret=0 -if [ -n "$binary" -o -x "$cmd" ]; then - "$cmd" $extraargs "$@" || ret=$? -else - /bin/sh "$cmd" $extraargs "$@" || ret=$? + trap - HUP INT TERM fi -trap - HUP INT TERM - # Run crouton-specific commands before the server exits: echo "Running exit commands..." 1>&2 From a69100cdd58f13dedcbef44d9e4148781d3af49a Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 11:07:33 -0700 Subject: [PATCH 027/334] host-x11: Fail if no Chromium OS X server exist --- chroot-bin/croutoncycle | 17 +++++++---------- chroot-bin/host-x11 | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 2f2c1e7b1..fe13de6af 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -108,33 +108,30 @@ for disp in /tmp/.X*-lock; do xiatactive='y' fi done -# Detect if a server is running on :0 -if host-x11 croutonwmtools list 1 >/dev/null 2>&1; then + +# host-x11 fails if no Chromium OS server exist +if host-x11 true 2>/dev/null; then # List windows on :0. Includes aura winlist="`host-x11 croutonwmtools list nim | \ sort | awk '{ printf $NF " " }'`" aurawin="`host-x11 croutonwmtools list ni | \ awk '$1 == "aura_root_0" { print $NF; exit }'`" + tty="`cat '/sys/class/tty/tty0/active'`" else # No X11 server noaurax='y' winlist="aura*" aurawin="aura" + tty="tty1" fi # Combine the two fulllist="$winlist$displist" fulllist="${fulllist% }" -# Figure out the current display number -if [ -z "$noaurax" ]; then - tty="`cat '/sys/class/tty/tty0/active'`" -else - tty="tty1" -fi - if [ "$tty" = 'tty1' ]; then - # Either in Chromium OS, xephyr chroot, or window. Active window is starred. + # Either in Chromium OS, xephyr/xiat chroot, or window. + # Active window is starred. for disp in $winlist; do if [ "${disp%"*"}" != "$disp" ]; then curdisp="$disp" diff --git a/chroot-bin/host-x11 b/chroot-bin/host-x11 index e2e640e95..4e2977af3 100755 --- a/chroot-bin/host-x11 +++ b/chroot-bin/host-x11 @@ -6,9 +6,20 @@ # Either runs the specified command with the environment set to use the host's # X11 server, or prints out the environment changes required. -export DISPLAY=':0' XAUTHORITY='/var/host/Xauthority' -if [ "$#" = 0 ]; then - echo "export DISPLAY='$DISPLAY' XAUTHORITY='$XAUTHORITY'" +# If tty0 does not exit, no Chromium OS X11 server exist +if [ ! -f "/sys/class/tty/tty0/active" ]; then + err="No Chromium OS X server is available." + if [ "$#" = 0 ]; then + echo "echo '$err' 1>&2" + else + echo "$err" 1>&2 + exit 1 + fi else - exec "$@" + export DISPLAY=':0' XAUTHORITY='/var/host/Xauthority' + if [ "$#" = 0 ]; then + echo "export DISPLAY='$DISPLAY' XAUTHORITY='$XAUTHORITY'" + else + exec "$@" + fi fi From edc4adf958be6e2d11198db73be771106598a139 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 11:17:31 -0700 Subject: [PATCH 028/334] croutonclip: Fix usage of host-x11 --- chroot-bin/croutonclip | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/chroot-bin/croutonclip b/chroot-bin/croutonclip index 4dfa38b32..968197fa3 100755 --- a/chroot-bin/croutonclip +++ b/chroot-bin/croutonclip @@ -31,15 +31,17 @@ websocketcommand() { } # rundisplay :X cmd ... -# Run a command on the specified display (uses host-x11 on :0) +# Run a command on the specified display rundisplay() { local disp="$1" shift - if [ "$disp" = ":0" ]; then - host-x11 "$@" - else + ( + # If display is :0, setup XAUTHORITY + if [ "$disp" = ":0" ]; then + eval "`host-x11`" 2>/dev/null + fi DISPLAY="$disp" "$@" - fi + ) } copyclip() { From de2e0de94fffd52f218930c7821fed99e06aa717 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 11:42:02 -0700 Subject: [PATCH 029/334] croutoncycle/xinitrc-wrapper: Force croutoncycle. Instead of deleting status file, just force the cycle. --- chroot-bin/croutoncycle | 10 +++++++++- chroot-bin/croutonxinitrc-wrapper | 7 +------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index fe13de6af..1f7453424 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -21,6 +21,14 @@ Cycles through running graphical chroots. # s: informs of a Chromium OS window change (called from extension): # - 0: any Chromium OS window # - n: criat window: X11 display number +# force [command]: Force switching display, even if it does not appear +# to be necessary. + +force='' +if [ "${1#[Ff]}" != "$1" ]; then + force='y' + shift +fi case "$1" in [Ll]*) cmd='l';; @@ -268,7 +276,7 @@ else fi # No-op on no-op -if [ "$destdisp" = "$curdisp" ]; then +if [ "$destdisp" = "$curdisp" -a -z "$force" ]; then exit 0 fi diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 328275ba7..826f566c1 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -128,12 +128,7 @@ if [ "$xmethod" = 'xiat' ]; then croutonfbserver "$DISPLAY" & try=1 - PIPEDIR='/tmp/crouton-ext' - CRIATDISPLAY="$PIPEDIR/criat-display" - # criat-display may be out of date, clear it so we start fresh. - # FIXME: It would be cleaner to simply force croutoncycle - rm -f "$CRIATDISPLAY" 2>/dev/null - while ! croutoncycle "${DISPLAY#:}"; do + while ! croutoncycle force "${DISPLAY#:}"; do echo "Cannot connect to extension, retrying..." if [ "$try" -ge 10 ]; then echo "\ From bd8ab94b431272233c5bf219059791761603bae4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 12:07:20 -0700 Subject: [PATCH 030/334] window.js: Report NaCl errors/crashes --- host-ext/crouton/window.js | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index a49a71061..7e5d34a2f 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -13,6 +13,11 @@ var display_ = null; /* Display number to use */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ +function registerWindow(register) { + chrome.extension.getBackgroundPage(). + registerCriat(display_, register ? window : null); +} + /* NaCl module loaded */ function moduleDidLoad() { CriatModule_ = document.getElementById('criat'); @@ -21,8 +26,25 @@ function moduleDidLoad() { CriatModule_.postMessage('display:' + display_); CriatModule_.postMessage('debug:' + debug_); CriatModule_.postMessage('hidpi:' + hidpi_); - /* Register window with background page */ - chrome.extension.getBackgroundPage().registerCriat(display_, window); +} + +/* NaCl module failed to load */ +function handleError(event) { + // We can't use common.naclModule yet because the module has not been + // loaded. + CriatModule_ = document.getElementById('criat'); + updateStatus('ERROR: ' + CriatModule_.lastError); + registerWindow(false); +} + +/* NaCl module crashed */ +function handleCrash(event) { + if (CriatModule_.exitStatus == -1) { + updateStatus('NaCl module crashed.'); + } else { + updateStatus('NaCl module exited: ' + CriatModule_.exitStatus); + } + registerWindow(false); } /* Change debugging level */ @@ -92,6 +114,7 @@ function handleMessage(message) { } else { updateStatus("Disconnected, please close the window."); } + registerWindow(false); } else if (type == "state" && payload == "fullscreen") { /* Toggle full screen */ chrome.windows.getCurrent(function(win) { @@ -170,6 +193,8 @@ chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, document.addEventListener('DOMContentLoaded', function() { listener_ = document.getElementById('listener'); listener_.addEventListener('load', moduleDidLoad, true); + listener_.addEventListener('error', handleError, true); + listener_.addEventListener('crash', handleCrash, true); listener_.addEventListener('message', handleMessage, true); window.addEventListener('resize', handleResize); window.addEventListener('focus', handleFocusBlur); @@ -189,4 +214,6 @@ document.addEventListener('DOMContentLoaded', function() { else if (keyval[0] == "hidpi") setHiDPI(keyval[1]); } + + registerWindow(true); }) From 6ea446b86f5b61708fc709874e1eae01bef9d323 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 13:13:56 -0700 Subject: [PATCH 031/334] background.js: Bug fix when switching window. --- host-ext/crouton/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 454e7bc77..03ccadbda 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -423,7 +423,7 @@ function websocketMessage(evt) { (!criat_win_[display].window || !criat_win_[display].window.closing)) { /* focus/full screen an existing window */ - var winid = criat_win_[disps[i]].id; + var winid = criat_win_[display].id; chrome.windows.update(winid, {focused: true}); chrome.windows.get(winid, function(win) { if (win.state == "maximized") From 3d38ec178bfdf5b4ec155bfadbe4bd866368a27e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 13:28:32 -0700 Subject: [PATCH 032/334] window.js: Force focus on CriatModule That still does not work (window does not have focus just after it is created?) --- host-ext/crouton/window.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 7e5d34a2f..f0a2f543b 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -26,6 +26,7 @@ function moduleDidLoad() { CriatModule_.postMessage('display:' + display_); CriatModule_.postMessage('debug:' + debug_); CriatModule_.postMessage('hidpi:' + hidpi_); + CriatModule_.focus(); } /* NaCl module failed to load */ @@ -184,6 +185,8 @@ function handleFocusBlur(evt) { else CriatModule_.postMessage("hide:"); } + console.log("active: " + document.activeElement); + CriatModule_.focus(); } /* Start in full screen */ From e5c96543c4f1c2cb4d66572005c19782d270eeb2 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 14:13:28 -0700 Subject: [PATCH 033/334] window.js: Rate-limit the number of resize events. --- host-ext/crouton/window.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index f0a2f543b..003bba335 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -13,6 +13,10 @@ var display_ = null; /* Display number to use */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ + /* Rate limit resize events */ +var resizePending_ = false; +var resizeLimited_ = false; + function registerWindow(register) { chrome.extension.getBackgroundPage(). registerCriat(display_, register ? window : null); @@ -22,7 +26,7 @@ function registerWindow(register) { function moduleDidLoad() { CriatModule_ = document.getElementById('criat'); updateStatus('Starting...'); - handleResize(); + criatResize(); CriatModule_.postMessage('display:' + display_); CriatModule_.postMessage('debug:' + debug_); CriatModule_.postMessage('hidpi:' + hidpi_); @@ -60,7 +64,7 @@ function setDebug(debug) { } if (CriatModule_) { CriatModule_.postMessage('debug:' + debug_); - handleResize(); + criatResize(); } } @@ -69,7 +73,7 @@ function setHiDPI(hidpi) { hidpi_ = hidpi; if (CriatModule_) { CriatModule_.postMessage('hidpi:' + hidpi_); - handleResize(); + criatResize(); } } @@ -158,12 +162,27 @@ function handleMessage(message) { /* Tell the module that the window was resized (this triggers a change of * resolution, followed by a resize message. */ -function handleResize() { +function criatResize() { console.log("resize! " + listener_.clientWidth + "/" + listener_.clientHeight); if (CriatModule_) CriatModule_.postMessage('resize:' + listener_.clientWidth + "/" + listener_.clientHeight); } +/* Window was resize, limit to one event per second */ +function handleResize() { + if (!resizeLimited_) { + criatResize(); + setTimeout(function() { + if (resizePending_) + criatResize(); + resizeLimited_ = resizePending_ = false; + }, 1000); + resizeLimited_ = true; + } else { + resizePending_ = true; + } +} + /* Called when window changes focus/visiblity */ function handleFocusBlur(evt) { /* Unfortunately, hidden/visibilityState is not able to tell when a window From 7c0d944f03edebf326574db301fca8ab26904f15 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 16:33:00 -0700 Subject: [PATCH 034/334] functions: Add websocketcommand And make use of it in crouton* scripts. --- chroot-bin/croutonclip | 20 +------------------ chroot-bin/croutoncycle | 38 +++++++----------------------------- chroot-bin/croutonurlhandler | 24 +++-------------------- installer/functions | 19 ++++++++++++++++++ 4 files changed, 30 insertions(+), 71 deletions(-) diff --git a/chroot-bin/croutonclip b/chroot-bin/croutonclip index 968197fa3..630dd2fb0 100755 --- a/chroot-bin/croutonclip +++ b/chroot-bin/croutonclip @@ -10,25 +10,7 @@ VERBOSE='' . "`dirname "$0"`/../installer/functions" -PIPEDIR='/tmp/crouton-ext' -PIPEIN="$PIPEDIR/in" -PIPEOUT="$PIPEDIR/out" -PIPELOCK="$PIPEDIR/lock" - -# Write a command to croutonwebsocket, and read back response -websocketcommand() { - # Check that $PIPEDIR and the FIFO pipes exist - if ! [ -d "$PIPEDIR" -a -p "$PIPEIN" -a -p "$PIPEOUT" ]; then - echo "EError $PIPEIN or $PIPEOUT are not pipes." - exit 0 - fi - - ( - flock 5 - cat > "$PIPEIN" - cat "$PIPEOUT" - ) 5>"$PIPELOCK" -} +CROUTONCLIPPIDFILE='/tmp/crouton-lock/clip' # rundisplay :X cmd ... # Run a command on the specified display diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 1f7453424..e2c53cd16 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -3,6 +3,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +. "`dirname "$0"`/../installer/functions" + USAGE="${0##*/} next|prev|cros|list|# Cycles through running graphical chroots. next: switch to the next display @@ -39,31 +41,9 @@ case "$1" in [Ss]*) cmd='s' disp="${1#s}";; :*) cmd=":$((${1#:}))";; [0-9]*) cmd="$(($1))";; -*) echo "$USAGE" 1>&2; exit 2;; +*) error 2 "$USAGE";; esac -# FIXME: This is our third copy, we need to move it to a common file -PIPEDIR='/tmp/crouton-ext' -PIPEIN="$PIPEDIR/in" -PIPEOUT="$PIPEDIR/out" -PIPELOCK="$PIPEDIR/lock" -CRIATDISPLAY="$PIPEDIR/criat-display" -CROUTONCLIPPIDFILE='/tmp/crouton-lock/clip' - -# Write a command to croutonwebsocket, and read back response -websocketcommand() { - # Check that $PIPEDIR and the FIFO pipes exist - if ! [ -d "$PIPEDIR" -a -p "$PIPEIN" -a -p "$PIPEOUT" ]; then - echo "EError $PIPEIN or $PIPEOUT are not pipes." - exit 0 - fi - - if ! timeout 3 \ - sh -c "flock 5; cat > '$PIPEIN'; cat '$PIPEOUT'" 5>"$PIPELOCK"; then - echo "EError timeout" - fi -} - # Returns the chroot name of an X11 display specified in $1 on stdout getname() { local name='' @@ -245,8 +225,7 @@ if [ -n "${cmd#[pn]}" ]; then i="$((i+1))" done if [ -z "$destdisp" ]; then - echo "Display number out of range." 1>&2 - exit 2 + error 2 "Display number out of range." fi fi elif [ "$cmd" = 'p' ]; then @@ -271,8 +250,7 @@ elif [ "$cmd" = 'n' ]; then destdisp="${fulllist%% *}" fi else - echo "Bad command $cmd." 1>&2 - exit 3 + error 3 "Bad command $cmd." fi # No-op on no-op @@ -302,8 +280,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then if [ -n "$xiatactive" -a "${destdisp%"*"}" = "$aurawin" ]; then STATUS="`echo "X-1" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then - echo "${STATUS#?}" 1>&2 - exit 1 + error 1 "${STATUS#?}" fi fi else @@ -315,8 +292,7 @@ else fi STATUS="`echo "X${destdisp#:}" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then - echo "${STATUS#?}" 1>&2 - exit 1 + error 1 "${STATUS#?}" fi else dest="`xprop -root 'XFree86_VT' 2>/dev/null`" diff --git a/chroot-bin/croutonurlhandler b/chroot-bin/croutonurlhandler index 64973e497..d28e63362 100755 --- a/chroot-bin/croutonurlhandler +++ b/chroot-bin/croutonurlhandler @@ -7,24 +7,7 @@ USAGE="${0##*/} [-n] URL Open an URL in Chromium OS (requires crouton extension). Switches back to Chromium OS unless -n is specified." -PIPEDIR='/tmp/crouton-ext' -PIPEIN="$PIPEDIR/in" -PIPEOUT="$PIPEDIR/out" -PIPELOCK="$PIPEDIR/lock" - -# Write a command to croutonwebsocket, and read back response -websocketcommand() { - # Check that $PIPEDIR and the FIFO pipes exist - if ! [ -d "$PIPEDIR" -a -p "$PIPEIN" -a -p "$PIPEOUT" ]; then - echo "EError $PIPEIN or $PIPEOUT are not pipes." - exit 0 - fi - - if ! timeout 3 \ - sh -c "flock 5; cat > '$PIPEIN'; cat '$PIPEOUT'" 5>"$PIPELOCK"; then - echo "EError timeout" - fi -} +. "`dirname "$0"`/../installer/functions" noswitch='' if [ "$1" = '-n' ]; then @@ -33,14 +16,13 @@ if [ "$1" = '-n' ]; then fi if [ -z "$*" ]; then - echo "$USAGE" 1>&2; exit 2; + error 2 "$USAGE" fi STATUS="`echo -n U"$*" | websocketcommand`" if [ ! "$STATUS" = 'UOK' ]; then - echo "${STATUS#?}" 1>&2 - exit 1 + error 1 "${STATUS#?}" fi if [ -z "$noswitch" ]; then diff --git a/installer/functions b/installer/functions index 98c565af9..cde840d55 100644 --- a/installer/functions +++ b/installer/functions @@ -152,3 +152,22 @@ validate_name() { fi return 0 } + +# Websocket interface +PIPEDIR='/tmp/crouton-ext' +CRIATDISPLAY="$PIPEDIR/criat-display" + +# Write a command to croutonwebsocket, and read back response +websocketcommand() { + # Check that $PIPEDIR and the FIFO pipes exist + if ! [ -d "$PIPEDIR" -a -p "$PIPEDIR/in" -a -p "$PIPEDIR/out" ]; then + echo "EError $PIPEDIR/in or $PIPEDIR/out are not pipes." + exit 0 + fi + + if ! timeout 3 \ + sh -c "flock 5; cat > '$PIPEDIR/in'; + cat '$PIPEDIR/out'" 5>"$PIPEDIR/lock"; then + echo "EError timeout" + fi +} From d9fa27df165d31b10428e128d27aae17a6d7ad59 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 27 Oct 2014 17:59:19 -0700 Subject: [PATCH 035/334] extension: Set criat window title --- host-ext/crouton/background.js | 35 ++++++++++++++++++++++++++-------- host-ext/crouton/window.js | 9 +++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 03ccadbda..fee4c4a89 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -38,9 +38,10 @@ var lastwindowlistupdate_ = null; var status_ = ""; var sversion_ = 0; /* Version of the websocket server */ var logger_ = []; /* Array of status messages: [LogLevel, time, message] */ -var windows_ = []; /* Array of windows (xorg, xephyr, host-x11, etc) */ +var windows_ = []; /* Array of windows. (.display, .name) */ -var criat_win_ = {}; /* Array of criat windows (.id, .window: window element) */ +var criat_win_ = {}; /* Map of criat windows. Key is display, value is object + (.id, .window: window element) */ var focus_win_ = -1; /* Focused criat window. -1 if no criat window focused. */ /* Set the current status string. @@ -215,15 +216,13 @@ function refreshUI() { } for (var i = 0; i < windows_.length; i++) { - if (!windows_[i].length) - continue; var row = windowlist.insertRow(-1); var cell1 = row.insertCell(0); var cell2 = row.insertCell(1); cell1.className = "display"; - cell1.innerHTML = windows_[i][0]; + cell1.innerHTML = windows_[i].display; cell2.className = "name"; - cell2.innerHTML = windows_[i].substring(3) + cell2.innerHTML = windows_[i].name; cell2.onclick = (function(i) { return function() { if (active_) { websocket_.send("C" + i); @@ -402,7 +401,22 @@ function websocketMessage(evt) { case 'C': /* Returned data from a croutoncycle command */ /* Non-zero length has a window list; otherwise it's a cycle signal */ if (payload.length > 0) { - windows_ = payload.split('\n'); + windows_ = payload.split('\n').filter( + function(x) { return x.length > 3 } + ).map( + function(x) { + k = new Object() + k.display = x[0]; + k.name = x.substring(3); + return k; + } + ) + + windows_.forEach(function(k) { + if (criat_win_[k.display] && criat_win_[k.display].window) { + criat_win_[k.display].window.setTitle(k.name); + } + }) } refreshUI(); break; @@ -435,9 +449,14 @@ function websocketMessage(evt) { criat_win_[display] = new Object(); criat_win_[display].id = -1; criat_win_[display].window = null; + + win = windows_.filter(function(x){ return x.display == display })[0] + name = win ? win.name : "crouton in a tab"; + chrome.windows.create({ 'url': "window.html?display=" + display + "&debug=" + (debug_ ? 1 : 0) + - "&hidpi=" + (hidpi_ ? 1 : 0), + "&hidpi=" + (hidpi_ ? 1 : 0) + + "&title=" + encodeURIComponent(name), 'type': "popup" }, function(newwin) { criat_win_[display].id = newwin.id; diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 003bba335..6e5e3950a 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -10,6 +10,7 @@ var listener_ = null; /* listener div element */ var debug_ = 0; /* Debuging level, passed to NaCl module */ var hidpi_ = 0; /* HiDPI mode */ var display_ = null; /* Display number to use */ +var title_ = "crouton in a tab"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ @@ -77,6 +78,10 @@ function setHiDPI(hidpi) { } } +function setTitle(title) { + document.title = "crouton in a tab: " + title + " (" + display_ + ")"; +} + function updateStatus(message) { var status = document.getElementById('status'); if (status) { @@ -231,11 +236,15 @@ document.addEventListener('DOMContentLoaded', function() { var keyval = args[i].split('=') if (keyval[0] == "display") display_ = keyval[1]; + else if (keyval[0] == "title") + title_ = decodeURIComponent(keyval[1]); else if (keyval[0] == "debug") setDebug(keyval[1]); else if (keyval[0] == "hidpi") setHiDPI(keyval[1]); } + setTitle(title_); + registerWindow(true); }) From a09fb1ce1477f29330cc61c44a8d6998c2e0819c Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 28 Oct 2014 10:37:58 -0700 Subject: [PATCH 036/334] background.js: Fix infinite loop in updateWindowList Also, ask for refresh in more appropriate places. --- host-ext/crouton/background.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index fee4c4a89..bcb70eb70 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -54,9 +54,6 @@ function setStatus(status, active) { if (update_ && !active_) chrome.runtime.reload(); - /* Force a window list update */ - updateWindowList(true); - refreshUI(); } @@ -100,10 +97,10 @@ function updateWindowList(force) { if (force || lastwindowlistupdate_ == null || (currenttime-lastwindowlistupdate_) > 1000*WINDOW_UPDATE_INTERVAL) { + lastwindowlistupdate_ = currenttime; printLog("Sending window list request", LogLevel.DEBUG); websocket_.send("Cs" + focus_win_); websocket_.send("Cl"); - lastwindowlistupdate_ = currenttime; } } @@ -345,6 +342,8 @@ function websocketMessage(evt) { websocket_.send("VOK"); /* Set active_ to true */ setStatus(sversion_ >= 2 ? "" : "Connected", true); + /* Force a window list update */ + updateWindowList(true); return; } else { error("Received frame while waiting for version", false); @@ -467,6 +466,8 @@ function websocketMessage(evt) { } websocket_.send("XOK"); closePopup(); + /* Force a window list update */ + updateWindowList(true); break; case 'P': /* Ping */ websocket_.send(received_msg); From 4df44e1f757f6493d57d7a16c5599e1e26cd7523 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 28 Oct 2014 10:41:26 -0700 Subject: [PATCH 037/334] websocket: Double-fork to avoid zombies. --- src/websocket.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/websocket.c b/src/websocket.c index 1c3f04c51..9ebdac696 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -348,17 +348,25 @@ static void socket_client_read() { /* Launch command in background (this is necessary as croutoncycle may send a websocket command, leaving us deadlocked...) */ - /* FIXME: These processes are not reaped and leave lots of - * zombies. Scary. */ pid_t pid = fork(); if (pid < 0) { syserror("Fork error."); exit(1); } else if (pid == 0) { - execvp(cmd, args); - error("Error running '%s'.", cmd); - exit(127); + /* Double-fork to avoid zombies */ + pid_t pid2 = fork(); + if (pid2 < 0) { + syserror("Fork error."); + exit(1); + } else if (pid2 == 0) { + execvp(cmd, args); + error("Error running '%s'.", cmd); + exit(127); + } + exit(0); } + /* Wait for first fork to complete. */ + waitpid(pid, NULL, 0); length = 1; } if (socket_client_write_frame(buffer, length, From f8b8c2e339ed553d931bc6d5ee63e793a2d62dc3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 28 Oct 2014 10:44:13 -0700 Subject: [PATCH 038/334] window.js: Reduce resize rate limit to 300ms Makes resize slightly more reactive. --- host-ext/crouton/window.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 6e5e3950a..44957b470 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -4,6 +4,7 @@ var CLOSE_TIMEOUT = 2; /* Close window x seconds after disconnect */ var DEBUG_LEVEL = 2; /* If debug is enabled, use this level in NaCl */ +var RESIZE_RATE_LIMIT = 300; /* No more than 1 resize query every x ms */ var CriatModule_ = null; /* NaCl module */ var listener_ = null; /* listener div element */ @@ -181,7 +182,7 @@ function handleResize() { if (resizePending_) criatResize(); resizeLimited_ = resizePending_ = false; - }, 1000); + }, RESIZE_RATE_LIMIT); resizeLimited_ = true; } else { resizePending_ = true; From cdc76cc07120be7a89979bdf3e1f028103382a2e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 30 Oct 2014 10:45:51 -0700 Subject: [PATCH 039/334] window.js: Display loading while loading... Wishing the percentage was more meaningful... --- host-ext/crouton/window.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 44957b470..f49d7911c 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -35,6 +35,13 @@ function moduleDidLoad() { CriatModule_.focus(); } +/* NaCl is loading... */ +function handleProgress(event) { + /* We could compute a percentage, but loading gets stuck at 89% (while + * translating?), so it's not very useful... */ + updateStatus('Loading...'); +} + /* NaCl module failed to load */ function handleError(event) { // We can't use common.naclModule yet because the module has not been @@ -221,6 +228,7 @@ chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, document.addEventListener('DOMContentLoaded', function() { listener_ = document.getElementById('listener'); listener_.addEventListener('load', moduleDidLoad, true); + listener_.addEventListener('progress', handleProgress, true); listener_.addEventListener('error', handleError, true); listener_.addEventListener('crash', handleCrash, true); listener_.addEventListener('message', handleMessage, true); From 640c20cca597e4a474851b0a8aa20fa10734971c Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 30 Oct 2014 10:53:48 -0700 Subject: [PATCH 040/334] gencrx.sh: Force remove criat pexe. --- host-ext/gencrx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/gencrx.sh b/host-ext/gencrx.sh index f847015ff..44a7e0e26 100755 --- a/host-ext/gencrx.sh +++ b/host-ext/gencrx.sh @@ -24,7 +24,7 @@ rm -f "$EXTNAME.crx" "$EXTNAME.zip" trap "rm -f '$EXTNAME.sig' '$EXTNAME.pub'" 0 -rm "$CRIAT_PEXE" +rm -f "$CRIAT_PEXE" # Build NaCl module make -C nacl_src clean make -C nacl_src From 65242c9ea3ae41bd09171cb32db733271daffa2c Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 3 Nov 2014 11:13:27 -0800 Subject: [PATCH 041/334] xinitrc-wrapper: Switch to display :x Without :, the nth element in the list is taken, which is probably wrong. --- chroot-bin/croutonxinitrc-wrapper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 826f566c1..dc268886d 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -128,7 +128,7 @@ if [ "$xmethod" = 'xiat' ]; then croutonfbserver "$DISPLAY" & try=1 - while ! croutoncycle force "${DISPLAY#:}"; do + while ! croutoncycle force "$DISPLAY"; do echo "Cannot connect to extension, retrying..." if [ "$try" -ge 10 ]; then echo "\ From 33f9bcb94f5219c3dfdac9785b5399d14d6a60dc Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 10:12:13 -0800 Subject: [PATCH 042/334] findres: Unify with setres. Unify setres and findres. setres uses XMETHOD to figure out the best way to resize the display: cvt on XMETHOD != criat. On XMETHOD=criat, try exact resolution (patched xorg-dummy), or best approximation. --- chroot-bin/croutonxinitrc-wrapper | 4 +- chroot-bin/findres | 53 ---------------- chroot-bin/setres | 102 +++++++++++++++++++++++++++--- src/fbserver.c | 4 +- targets/xiat | 2 +- 5 files changed, 99 insertions(+), 66 deletions(-) delete mode 100755 chroot-bin/findres diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index dc268886d..44984debf 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -124,8 +124,8 @@ fi if [ "$xmethod" = 'xiat' ]; then # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. - findres 1024 768 > /dev/null - croutonfbserver "$DISPLAY" & + XMETHOD="xiat" setres 1024 768 > /dev/null + XMETHOD="xiat" croutonfbserver "$DISPLAY" & try=1 while ! croutoncycle force "$DISPLAY"; do diff --git a/chroot-bin/findres b/chroot-bin/findres deleted file mode 100755 index db9e60048..000000000 --- a/chroot-bin/findres +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh -e -# Copyright (c) 2014 The crouton Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Changes the resolution of the current display, taking the closest, -# smaller, available resolution in xrandr. -# If no smaller resolution is available, pick the closest one. -# Outputs the applied resolution - -set -e - -if [ -z "$1" -o -z "$2" ]; then - echo "Usage: $0 width height" 1>&2 - exit 2 -fi - -TARGETW="$1" -TARGETH="$2" - -# This assumes modes have "canonical" names, in the form "x[_]" -res="`xrandr | awk ' - function abs(x) { return x > 0 ? x : -x } - BEGIN { - tw='"$TARGETW"' - th='"$TARGETH"' - oversize_penalty = 1000000 - } - $1 ~ /[0-9]+x[0-9]+/ { - res = $1; sub(/_[0-9]+/, "", res) - w = res; sub(/x[0-9]*/, "", w); w = int(w) - h = res; sub(/[0-9]*x/, "", h); h = int(h) - score = abs(tw-w) + abs(th-h) - if (w > tw) score += oversize_penalty - if (h > th) score += oversize_penalty - if (!cmode || score < cscore) { - cmode = $1 - cscore = score - } - } - END { - if (!cmode) exit 1 - print cmode - } -'`" - -if [ -z "$res" ]; then - echo "Cannot find resolution" 1>&2 - exit 1 -fi - -xrandr -s "$res" 2>/dev/null -echo -n "$res" diff --git a/chroot-bin/setres b/chroot-bin/setres index 642442094..3308eb316 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -3,6 +3,16 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# Changes the resolution of the current display. +# If XMETHOD is criat, tries to create a new exact resolution, and change mode +# to that. If that fails (e.g. non-patched xorg-dummy), take the closest, +# smaller, available resolution in xrandr, and if no smaller resolution is +# available, pick the closest one. +# If XMETHOD is anything else, set a resolution from cvt output. +# In all cases, outputs the applied resolution. + +set -e + if [ "$#" -lt 2 -o "$#" -gt 4 ]; then echo "USAGE: ${0##*/} x y [r [output]]" 1>&2 exit 2 @@ -14,12 +24,88 @@ o="${4}" if [ -z "$o" ]; then o="`xrandr -q | awk 'x{print $1;exit}/^Screen 0/{x=1}'`" fi -cvt "$x" "$y" "$r" | { - read -r _ - read -r _ mode data - mode="${mode#\"}" - mode="${mode%\"}" - xrandr --newmode "$mode" $data 2>/dev/null || true - xrandr --addmode "$o" "$mode" - xrandr --output "$o" --mode "$mode" + +if [ "$XMETHOD" != "xiat" ]; then + cvt "$x" "$y" "$r" | { + read -r _ + read -r _ mode data + mode="${mode#\"}" + mode="${mode%\"}" + xrandr --newmode "$mode" $data 2>/dev/null || true + xrandr --addmode "$o" "$mode" + xrandr --output "$o" --mode "$mode" + echo "$o" + } + exit 0 +fi + +# Replace mode $2 in output $1, with new data $3..$# +# Deletes the mode if $3 is not provided +replacemode() { + local o="$1" + local mode="$2" + shift 2 + xrandr --delmode "$o" "$mode" 2>/dev/null || true + xrandr --rmmode "$mode" 2>/dev/null || true + if [ "$#" -gt 0 ]; then + xrandr --newmode "$mode" "$@" + xrandr --addmode "$o" "$mode" + fi } + +# Try to change to arbitrary resolution +mhz="$((r*x*y/1000000))" +name="Crouton in a tab" + +# Tweak the temp mode so xrandr knows the 2 modes are different, and +# switches away from the temp mode. +replacemode "$o" "$name (tmp)" $mhz $x $x $x $((x+1)) $y $y $y $y + +# This fails on non-patched xorg-dummy +if xrandr --output "$o" --mode "$name (tmp)"; then + replacemode "$o" "$name" $mhz $x $x $x $x $y $y $y $y + xrandr --output "$o" --mode "$name" + replacemode "$o" "$name (tmp)" + echo "${x}x${y}_${r}" + exit 0 +else + # Delete the temp mode + replacemode "$o" "$name (tmp)" +fi + +# Setting a custom mode failed, fall back on best match + +# This assumes modes have "canonical" names, in the form "x[_]" +res="`xrandr | awk ' + function abs(x) { return x > 0 ? x : -x } + BEGIN { + tw='"$x"' + th='"$y"' + oversize_penalty = 1000000 + } + $1 ~ /[0-9]+x[0-9]+/ { + res = $1; sub(/_[0-9]+/, "", res) + w = res; sub(/x[0-9]*/, "", w); w = int(w) + h = res; sub(/[0-9]*x/, "", h); h = int(h) + score = abs(tw-w) + abs(th-h) + if (w > tw) score += oversize_penalty + if (h > th) score += oversize_penalty + if (!cmode || score < cscore) { + cmode = $1 + cscore = score + } + } + END { + if (!cmode) exit 1 + print cmode + } +'`" + +if [ -z "$res" ]; then + echo "Cannot find resolution" 1>&2 + exit 1 +fi + +xrandr -s "$res" 2>/dev/null +echo "$res" +exit 0 diff --git a/src/fbserver.c b/src/fbserver.c index dcd2b4751..c1f8e8e65 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -168,7 +168,7 @@ static int init_display(char* name) { * Reply must be a resolution in "canonical" form: x[_] */ /* FIXME: Maybe errors here should not be fatal... */ void change_resolution(const struct resolution* rin) { - char* cmd = "findres"; + char* cmd = "setres"; char arg1[32], arg2[32], buffer[256]; int c; char* args[] = {cmd, arg1, arg2, NULL}; @@ -196,7 +196,7 @@ void change_resolution(const struct resolution* rin) { trueorabort(buffer != endptr && *endptr == '\0', "Invalid width: '%s'", buffer); long nheight = strtol(cut+1, &endptr, 10); - trueorabort(cut+1 != endptr && *endptr == '\0', + trueorabort(cut+1 != endptr && (*endptr == '\0' || *endptr == '\n'), "Invalid height: '%s'", cut+1); log(1, "New resolution %ld x %ld", nwidth, nheight); diff --git a/targets/xiat b/targets/xiat index dc9d047b1..9acfab55e 100644 --- a/targets/xiat +++ b/targets/xiat @@ -5,7 +5,7 @@ REQUIRES='core audio extension' PROVIDES='x11' DESCRIPTION='Crouton in a tab X11 backend. Improves compatibility but lacks GPU accel.' -CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper findres xinit' +CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiat xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" From 50400701efe5def83f735bf881621532ef2dcf2e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 10:23:05 -0800 Subject: [PATCH 043/334] croutonfindnacl: Replace ifs by && --- chroot-bin/croutonfindnacl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chroot-bin/croutonfindnacl b/chroot-bin/croutonfindnacl index bb2f0bdcc..ac10658d4 100755 --- a/chroot-bin/croutonfindnacl +++ b/chroot-bin/croutonfindnacl @@ -39,12 +39,12 @@ MATCH="" # Iterate over all NaCl helper processes for pid in $PIDS; do - if [ -n "$VERBOSE" ]; then echo "pid:$pid" 1>&2; fi + [ -n "$VERBOSE" ] && echo "pid:$pid" 1>&2 # Find candidate mappings file="`awk '$1 ~ /^[0-9a-f]*'"$ADDRESS"'-/ && $2 == "rw-s" \ && $6 ~ /\/shm\/.com.google.Chrome/ { print $6 } ' "/proc/$pid/maps"`" - if [ -n "$VERBOSE" ]; then echo "file:$file" 1>&2; fi + [ -n "$VERBOSE" ] && echo "file:$file" 1>&2 if [ -z "$file" ]; then continue fi @@ -56,7 +56,7 @@ for pid in $PIDS; do if [ "$link" = "$file" ]; then # Check if signature matches pattern="`od -An -t x1 -N8 "$fd" | tr -d ' '`" - if [ -n "$VERBOSE" ]; then echo "FD:$fd ($pattern)" 1>&2; fi + [ -n "$VERBOSE" ] && echo "FD:$fd ($pattern)" 1>&2 if [ "$pattern" = "$PATTERN" ]; then # Second match? This should never happen if [ -n "$MATCH" ]; then From 4f457f87be19cffa2c1fdd8514eb69cf54d3ee03 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 10:25:15 -0800 Subject: [PATCH 044/334] Indentation fixes. --- host-ext/crouton/window.html | 2 +- host-ext/nacl_src/criat.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 2d05f3072..963e5b61c 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -17,7 +17,7 @@ overflow: hidden; background-color: rgba(0, 0, 0, 1); } - #content { + #content { position: absolute; top: 0; right: 0; diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index aa4656df8..947289308 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -35,7 +35,7 @@ class CriatInstance : public pp::Instance { explicit CriatInstance(PP_Instance instance) : pp::Instance(instance), callback_factory_(this), - scale_(1.0f), + scale_(1.0f), image_data_(NULL), k_(0), websocket_(this), From 0a90f13cb8b86ef2f7386f11e5581312a28ce390 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 10:34:47 -0800 Subject: [PATCH 045/334] background.js: Registering windows event in a better place. --- host-ext/crouton/background.js | 64 +++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index bcb70eb70..1c7056c98 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -268,6 +268,10 @@ function clipboardStart() { LogLevel.INFO); setStatus("Started...", false); + /* Monitor window focus changes/removals and report to croutonclip */ + chrome.windows.onFocusChanged.addListener(windowFocusChanged) + chrome.windows.onRemoved.addListener(windowRemoved) + clipboardholder_ = document.getElementById("clipboardholder"); websocketConnect(); @@ -506,6 +510,37 @@ function websocketClose() { checkUpdate(false); } +/* Called when window in focus changes: feeback to the extension so the + * clipboard can be transfered. */ +function windowFocusChanged(windowid) { + var disps = Object.keys(criat_win_); + nextfocus_win_ = -1; + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].id == windowid) { + nextfocus_win_ = disps[i]; + break; + } + } + if (focus_win_ != nextfocus_win_) { + focus_win_ = nextfocus_win_; + if (active_ && sversion_ >= 2) + websocket_.send("Cs" + focus_win_); + printLog("Window " + focus_win_ + " focused", LogLevel.DEBUG); + } +} + +/* Called when a window is removed, so we can delete its reference. */ +function windowRemoved(windowid) { + var disps = Object.keys(criat_win_); + for (var i = 0; i < disps.length; i++) { + if (criat_win_[disps[i]].id == windowid) { + criat_win_[disps[i]].id = -1; + criat_win_[disps[i]].window = null; + printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); + } + } +} + function padstr0(i) { var s = i + ""; if (s.length < 2) @@ -579,35 +614,6 @@ chrome.runtime.getPlatformInfo(function(platforminfo) { chrome.runtime.onUpdateAvailable.addListener(function(details) { updateAvailable(details.version); }); - - /* Monitor window focus changes and report to croutonclip */ - chrome.windows.onFocusChanged.addListener(function(windowid) { - var disps = Object.keys(criat_win_); - nextfocus_win_ = -1; - for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].id == windowid) { - nextfocus_win_ = disps[i]; - break; - } - } - if (focus_win_ != nextfocus_win_) { - focus_win_ = nextfocus_win_; - if (active_ && sversion_ >= 2) - websocket_.send("Cs" + focus_win_); - printLog("Window " + focus_win_ + " focused", LogLevel.DEBUG); - } - }) - - chrome.windows.onRemoved.addListener(function(windowid) { - var disps = Object.keys(criat_win_); - for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].id == windowid) { - criat_win_[disps[i]].id = -1; - criat_win_[disps[i]].window = null; - printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); - } - } - }) } else { /* Disable the icon on non-Chromium OS. */ chrome.browserAction.setTitle( From 68fa99b4997fdaaff2cc85441696296cc2a48051 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 10:41:52 -0800 Subject: [PATCH 046/334] window.js: Simplify hide logic. --- host-ext/crouton/window.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index f49d7911c..d507e43b4 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -144,16 +144,15 @@ function handleMessage(message) { } else if (type == "state" && payload == "hide") { /* Hide window */ chrome.windows.getCurrent(function(win) { + minimize = function(win) { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': 'minimized'}, function(win) {})} /* To make restore nicer, first exit full screen, then minimize */ if (win.state == "fullscreen") { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': 'maximized'}, function(win) { - chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': 'minimized'}, function(win) {}) - }) + {'state': 'maximized'}, minimize) } else { - chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': 'minimized'}, function(win) {}) + minimize() } }) } else if (type == "resize") { From 7da11cd6e05641de0fd8a770d52f3ff3e1103be4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 13:55:51 -0800 Subject: [PATCH 047/334] xiat: s/read/read -r/ --- targets/xiat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/xiat b/targets/xiat index 9acfab55e..93f5368da 100644 --- a/targets/xiat +++ b/targets/xiat @@ -34,7 +34,7 @@ echo ' 1920 1080 l # FHD 1920 1200 l # Standard 20" 2560 1600 l # Standard 30" -' | while read w h flags _; do +' | while read -r w h flags _; do if [ -z "$w" ]; then continue fi From beade43658f9729b98cbe298b074e547bb011eaf Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 17:50:23 -0800 Subject: [PATCH 048/334] criat: only compute delta when useful. --- host-ext/nacl_src/criat.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 947289308..d5f457461 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -118,10 +118,9 @@ class CriatInstance : public pp::Instance { /* Send a logging message to Javascript */ void LogMessage(int level, std::string str) { - double delta = (pp::Module::Get()->core()->GetTime()-lasttime_)*1000; - if (level <= debug_) { std::ostringstream status; + double delta = (pp::Module::Get()->core()->GetTime()-lasttime_)*1000; status << "(" << level << ") " << (int)delta << " " << str; ControlMessage("log", status.str()); } From 50fd1d797f29f8cca8e46153c14922679f4aca33 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 18:01:18 -0800 Subject: [PATCH 049/334] setres: Auto-detect xmethod Use xprop on root window instead of an environment variable. --- chroot-bin/croutonxinitrc-wrapper | 4 ++-- chroot-bin/setres | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 44984debf..3d2b0cc7a 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -124,8 +124,8 @@ fi if [ "$xmethod" = 'xiat' ]; then # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. - XMETHOD="xiat" setres 1024 768 > /dev/null - XMETHOD="xiat" croutonfbserver "$DISPLAY" & + setres 1024 768 > /dev/null + croutonfbserver "$DISPLAY" & try=1 while ! croutoncycle force "$DISPLAY"; do diff --git a/chroot-bin/setres b/chroot-bin/setres index 3308eb316..b049b69a2 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -25,7 +25,9 @@ if [ -z "$o" ]; then o="`xrandr -q | awk 'x{print $1;exit}/^Screen 0/{x=1}'`" fi -if [ "$XMETHOD" != "xiat" ]; then +xmethod="`xprop -root 'CROUTON_XMETHOD' | sed -n 's/^.*\"\(.*\)\"/\1/p'`" + +if [ "$xmethod" != "xiat" ]; then cvt "$x" "$y" "$r" | { read -r _ read -r _ mode data From 57a49c6ce5ca1b286107b402f01463aff6b0b3a5 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 09:29:24 -0800 Subject: [PATCH 050/334] criat: Switch to C++11, using in-class initializers. --- host-ext/nacl_src/Makefile | 2 +- host-ext/nacl_src/criat.cc | 64 +++++++++++++++----------------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/host-ext/nacl_src/Makefile b/host-ext/nacl_src/Makefile index 642b9f308..87364f5b3 100644 --- a/host-ext/nacl_src/Makefile +++ b/host-ext/nacl_src/Makefile @@ -18,7 +18,7 @@ include $(NACL_SDK_ROOT)/tools/common.mk TARGET = criat LIBS = ppapi_cpp ppapi -CFLAGS = -Wall +CFLAGS = -Wall -std=gnu++11 SOURCES = criat.cc # Build rules generated by macros from common.mk: diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index d5f457461..6f7a2f156 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -32,21 +32,7 @@ namespace { class CriatInstance : public pp::Instance { public: - explicit CriatInstance(PP_Instance instance) - : pp::Instance(instance), - callback_factory_(this), - scale_(1.0f), - image_data_(NULL), - k_(0), - websocket_(this), - connected_(false), - target_fps_(FULLFPS), - pending_mouse_move_(false), - mouse_pos_(-1, -1), - avgfps_(0), - display_(-1), - debug_(0), - hidpi_(false) {} + explicit CriatInstance(PP_Instance instance): pp::Instance(instance) {} virtual ~CriatInstance() {} @@ -443,13 +429,13 @@ class CriatInstance : public pp::Instance { event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { pp::MouseInputEvent mouse_event(event); - pp::Point mouse_event_pos( + pp::Point mouse_event_pos( mouse_event.GetPosition().x() * scale_, mouse_event.GetPosition().y() * scale_); bool down = event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN; if (mouse_pos_.x() != mouse_event_pos.x() || - mouse_pos_.y() != mouse_event_pos.y()) { + mouse_pos_.y() != mouse_event_pos.y()) { pending_mouse_move_ = true; mouse_pos_ = mouse_event_pos; } @@ -526,7 +512,7 @@ class CriatInstance : public pp::Instance { scale_ = hidpi_ ? view_scale_ : 1.0f; pp::Size new_size = pp::Size(view_rect_.width() * scale_, - view_rect_.height() * scale_); + view_rect_.height() * scale_); std::ostringstream status; status << "InitContext " << new_size.width() << "x" << new_size.height() @@ -535,7 +521,7 @@ class CriatInstance : public pp::Instance { const bool kIsAlwaysOpaque = true; context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque); - context_.SetScale(1.0f / scale_); + context_.SetScale(1.0f / scale_); if (!BindGraphics(context_)) { LogMessage(0, "Unable to bind 2d context!"); context_ = pp::Graphics2D(); @@ -806,36 +792,36 @@ class CriatInstance : public pp::Instance { } private: - pp::CompletionCallbackFactory callback_factory_; + pp::CompletionCallbackFactory callback_factory_{this}; pp::Graphics2D context_; pp::Graphics2D flush_context_; pp::Rect view_rect_; - float view_scale_; + float view_scale_ = 1.0f; pp::Size size_; - float scale_; + float scale_ = 1.0f; - pp::ImageData* image_data_; - int k_; + pp::ImageData* image_data_ = NULL; + int k_ = 0; - pp::WebSocket websocket_; - bool connected_; - bool screen_flying_; + pp::WebSocket websocket_{this}; + bool connected_ = false; + bool screen_flying_ = false; pp::Var receive_var_; - int target_fps_; - int request_token_; - bool force_refresh_; + int target_fps_ = FULLFPS; + int request_token_ = 0; + bool force_refresh_ = false; - bool pending_mouse_move_; - pp::Point mouse_pos_; + bool pending_mouse_move_ = false; + pp::Point mouse_pos_{-1, -1}; /* Mouse wheel accumulators */ - int mouse_wheel_x; - int mouse_wheel_y; + int mouse_wheel_x = 0; + int mouse_wheel_y = 0; /* Super_L press has been delayed */ - bool pending_super_l_; + bool pending_super_l_ = false; /* Performance metrics */ PP_Time lasttime_; - double avgfps_; + double avgfps_ = 0.0; /* Cursor cache */ class Cursor { @@ -846,9 +832,9 @@ class CriatInstance : public pp::Instance { std::unordered_map cursor_cache_; /* Display to connect to */ - int display_; - int debug_; - bool hidpi_; + int display_ = -1; + int debug_ = 0; + bool hidpi_ = false; }; class CriatModule : public pp::Module { From 05bf364e1de5908d3adf3f3d9f66d290f4b492c0 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 09:42:57 -0800 Subject: [PATCH 051/334] Constant Names according to style guide. --- host-ext/nacl_src/criat.cc | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 6f7a2f156..576be9e83 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -22,12 +22,6 @@ namespace { /* Protocol data structures */ #include "../../src/fbserver-proto.h" - /* SuperL keycode (search key) */ - const uint32_t SUPER_L = 0xffeb; - - const int FULLFPS = 30; /* Maximum fps */ - const int BLURFPS = 5; /* fps when window is possibly hidden */ - const int HIDDENFPS = 0; /* fps when window is hidden */ } /* namespace */ class CriatInstance : public pp::Instance { @@ -80,10 +74,10 @@ class CriatInstance : public pp::Instance { /* Release all keys */ SocketSend(pp::Var("Q"), false); /* Throttle/stop refresh */ - SetTargetFPS((type == "blur") ? BLURFPS : HIDDENFPS); + SetTargetFPS((type == "blur") ? kBlurFPS : kHiddenFPS); } else if (type == "focus") { /* Force refresh and ask for next frame */ - SetTargetFPS(FULLFPS); + SetTargetFPS(kFullFPS); } else if (type == "debug") { debug_ = stoi(message.substr(pos+1)); } else if (type == "hidpi") { @@ -422,9 +416,9 @@ class CriatInstance : public pp::Instance { } bool letter = (keycode >= 65 && keycode <= 90); - if (letter && pending_super_l_ && down) SendKey(SUPER_L, 1); + if (letter && pending_super_l_ && down) SendKey(kSUPER_L, 1); SendKey(keysym, down ? 1 : 0); - if (letter && pending_super_l_ && !down) SendKey(SUPER_L, 0); + if (letter && pending_super_l_ && !down) SendKey(kSUPER_L, 0); } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { @@ -595,7 +589,7 @@ class CriatInstance : public pp::Instance { case 42: return 0xff61; // print screen case 45: return 0xff63; // insert case 46: return 0xffff; // delete - case 91: return SUPER_L; // super + case 91: return kSUPER_L; // super case 106: return 0xffaa; // num multiply case 107: return 0xffab; // num plus case 109: return 0xffad; // num minus @@ -640,7 +634,7 @@ class CriatInstance : public pp::Instance { void SendClick(int button, int down) { struct mouseclick* mc; - if (pending_super_l_ && down) SendKey(SUPER_L, 1); + if (pending_super_l_ && down) SendKey(kSUPER_L, 1); pp::VarArrayBuffer array_buffer(sizeof(*mc)); mc = static_cast(array_buffer.Map()); @@ -650,10 +644,10 @@ class CriatInstance : public pp::Instance { array_buffer.Unmap(); SocketSend(array_buffer, true); - if (pending_super_l_ && !down) SendKey(SUPER_L, 0); + if (pending_super_l_ && !down) SendKey(kSUPER_L, 0); /* That means we have focus */ - SetTargetFPS(FULLFPS); + SetTargetFPS(kFullFPS); } void SendKey(uint32_t keysym, int down) { @@ -667,7 +661,7 @@ class CriatInstance : public pp::Instance { SocketSend(array_buffer, true); /* That means we have focus */ - SetTargetFPS(FULLFPS); + SetTargetFPS(kFullFPS); } /* Request the next framebuffer grab */ @@ -792,6 +786,15 @@ class CriatInstance : public pp::Instance { } private: + /* Constants */ + /* SuperL keycode (search key) */ + const uint32_t kSUPER_L = 0xffeb; + + const int kFullFPS = 30; /* Maximum fps */ + const int kBlurFPS = 5; /* fps when window is possibly hidden */ + const int kHiddenFPS = 0; /* fps when window is hidden */ + + /* Class members */ pp::CompletionCallbackFactory callback_factory_{this}; pp::Graphics2D context_; pp::Graphics2D flush_context_; @@ -807,7 +810,7 @@ class CriatInstance : public pp::Instance { bool connected_ = false; bool screen_flying_ = false; pp::Var receive_var_; - int target_fps_ = FULLFPS; + int target_fps_ = kFullFPS; int request_token_ = 0; bool force_refresh_ = false; From 3a7ee41fd914f273cf1bd0d71bdc58b8ff3ae50d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 09:49:24 -0800 Subject: [PATCH 052/334] criat: No namespace around include fb-server-proto.h --- host-ext/nacl_src/criat.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 576be9e83..c7c5c4532 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -19,10 +19,8 @@ #include "ppapi/cpp/websocket.h" #include "ppapi/utility/completion_callback_factory.h" -namespace { - /* Protocol data structures */ +/* Protocol data structures */ #include "../../src/fbserver-proto.h" -} /* namespace */ class CriatInstance : public pp::Instance { public: From 8744c8d76e5c7c1de8e2b4dd8e38f2b2bb559f45 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 09:59:09 -0800 Subject: [PATCH 053/334] criat: Clarified that SendClick takes in X11-numbered button id. --- host-ext/nacl_src/criat.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index c7c5c4532..cc79202dc 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -440,6 +440,10 @@ class CriatInstance : public pp::Instance { status << " " << (down ? "DOWN" : "UP"); status << " " << (mouse_event.GetButton()); + /* SendClick calls SocketSend, which flushes the mouse position + * before sending the click event. + * Also, Javascript button numbers are 0-based (left=0), while + * X11 numbers are 1-based (left=1). */ SendClick(mouse_event.GetButton()+1, down ? 1 : 0); } @@ -628,7 +632,8 @@ class CriatInstance : public pp::Instance { target_fps_ = new_target_fps; } - /* Send a mouse click */ + /* Send a mouse click. + * button is a X11 button number (e.g. 1 is left click) */ void SendClick(int button, int down) { struct mouseclick* mc; From 9316a3eff330b718c5cfb775d52ee519dfd4a711 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 10:35:33 -0800 Subject: [PATCH 054/334] criat: Improve function comments/coding style. Also remove default parameter in Paint. --- host-ext/nacl_src/criat.cc | 121 +++++++++++++++++++++++-------------- src/fbserver-proto.h | 5 ++ 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index cc79202dc..87735c9b4 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -1,6 +1,13 @@ /* Copyright (c) 2014 The crouton Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. + * + * This is a NaCl module, used by the crouton extension, to provide + * a display for crouton-in-a-tab. + * On one end, it communicates with the Javascript module window.js, on the + * other, it requests, via WebSocket, frames from croutonfbserver, and sends + * inputs events. + * */ #include @@ -28,7 +35,7 @@ class CriatInstance : public pp::Instance { virtual ~CriatInstance() {} - /* Register events */ + /* Registers events */ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL | @@ -42,8 +49,8 @@ class CriatInstance : public pp::Instance { /** Interface with Javascript **/ public: - /* Handle message from Javascript */ - /* Format: : */ + /* Handles message from Javascript + * Format: : */ virtual void HandleMessage(const pp::Var& var_message) { if (!var_message.is_string()) return; @@ -89,23 +96,24 @@ class CriatInstance : public pp::Instance { } private: - /* Send a status message to Javascript */ + /* Sends a status message to Javascript */ void StatusMessage(std::string str) { ControlMessage("status", str); } - /* Send a logging message to Javascript */ + /* Sends a logging message to Javascript */ void LogMessage(int level, std::string str) { if (level <= debug_) { std::ostringstream status; - double delta = (pp::Module::Get()->core()->GetTime()-lasttime_)*1000; + double delta = 1000 * + (pp::Module::Get()->core()->GetTime() - lasttime_); status << "(" << level << ") " << (int)delta << " " << str; ControlMessage("log", status.str()); } } - /* Send a control message to Javascript */ - /* Format: : */ + /* Sends a control message to Javascript + * Format: : */ void ControlMessage(std::string type, std::string str) { std::ostringstream status; status << type << ":" << str; @@ -114,8 +122,9 @@ class CriatInstance : public pp::Instance { /** WebSocket interface **/ private: - /* Connect to WebSocket server */ - void SocketConnect(int32_t result = 0) { + /* Connects to WebSocket server + * Parameter is ignored: used for callbacks */ + void SocketConnect(int32_t /*result*/ = 0) { if (display_ < 0) { LogMessage(-1, "SocketConnect: No display defined yet."); return; @@ -129,7 +138,7 @@ class CriatInstance : public pp::Instance { StatusMessage("Connecting..."); } - /* WebSocket connected (or failed to connect) */ + /* Called when WebSocket is connected (or failed to connect) */ void OnSocketConnectCompletion(int32_t result) { if (result != PP_OK) { std::ostringstream status; @@ -147,8 +156,8 @@ class CriatInstance : public pp::Instance { StatusMessage("Connected."); } - /* WebSocket closed */ - void OnSocketClosed(int32_t result = 0) { + /* Called when WebSocket is closed */ + void OnSocketClosed(int32_t result) { StatusMessage("Disconnected..."); ControlMessage("disconnected", "Socket closed"); connected_ = false; @@ -156,19 +165,23 @@ class CriatInstance : public pp::Instance { Paint(true); } - /* Check if a packet size is valid */ - bool CheckSize(int length, int target, std::string type) { + /* Checks if a WebSocket request size is valid: + * - length: payload length + * - target: expected length for request type + * - type: request type, to be printed on error + */ + bool CheckSize(int length, int target, const std::string& type) { if (length == target) return true; std::stringstream status; - status << "Invalid " << type << " packet (" << length + status << "Invalid " << type << " request (" << length << " != " << target << ")."; LogMessage(-1, status.str()); return false; } - /* Received a frame from WebSocket server */ + /* Called when a frame is received from WebSocket server */ void OnSocketReceiveCompletion(int32_t result) { std::stringstream status; status << "ReadCompletion: " << result << "."; @@ -229,12 +242,13 @@ class CriatInstance : public pp::Instance { } if (data[0] == 'S') { /* Screen */ - if (!CheckSize(datalen, sizeof(struct screen_reply), "screen_reply")) + if (!CheckSize(datalen, sizeof(struct screen_reply), + "screen_reply")) goto error; struct screen_reply* reply = (struct screen_reply*)data; if (reply->updated) { if (!reply->shmfailed) { - Paint(); + Paint(false); } else { /* Blank the frame if shm failed */ Paint(true); @@ -243,11 +257,13 @@ class CriatInstance : public pp::Instance { } else { screen_flying_ = false; /* No update: Ask for next frame in 1000/target_fps_ */ - if (target_fps_ > 0) + if (target_fps_ > 0) { pp::Module::Get()->core()->CallOnMainThread( 1000/target_fps_, - callback_factory_.NewCallback(&CriatInstance::RequestScreen), + callback_factory_.NewCallback( + &CriatInstance::RequestScreen), request_token_); + } } if (reply->cursor_updated) { @@ -332,16 +348,18 @@ class CriatInstance : public pp::Instance { error: LogMessage(-1, "Receive error."); websocket_.Close(0, pp::Var("Receive error"), - callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); + callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); } - /* Ask to receive the next WebSocket frame */ - void SocketReceive(int32_t result = 0) { + /* Asks to receive the next WebSocket frame + * Parameter is ignored: used for callbacks */ + void SocketReceive(int32_t /*result*/ = 0) { websocket_.ReceiveMessage(&receive_var_, callback_factory_.NewCallback( - &CriatInstance::OnSocketReceiveCompletion)); + &CriatInstance::OnSocketReceiveCompletion)); } - /* Send a WebSocket Frame, possibly flushing current mouse position first */ + /* Sends a WebSocket request, possibly flushing current mouse position + * first */ void SocketSend(const pp::Var& var, bool flushmouse) { if (!connected_) { LogMessage(-1, "SocketSend: not connected!"); @@ -365,12 +383,14 @@ class CriatInstance : public pp::Instance { /** UI functions **/ public: + /* Called when the NaCl module view changes (size, visibility) */ virtual void DidChangeView(const pp::View& view) { view_scale_ = view.GetDeviceScale(); view_rect_ = view.GetRect(); InitContext(); } + /* Called when an input event is received */ virtual bool HandleInputEvent(const pp::InputEvent& event) { if (event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN || event.GetType() == PP_INPUTEVENT_TYPE_KEYUP) { @@ -486,12 +506,16 @@ class CriatInstance : public pp::Instance { pp::TouchInputEvent touch_event(event); - int count = touch_event.GetTouchCount(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); + int count = touch_event.GetTouchCount( + PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); std::ostringstream status; status << "TOUCH " << count; for (int i = 0; i < count; i++) { - pp::TouchPoint tp = touch_event.GetTouchByIndex(PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); - status << std::endl << tp.id() << "//" << tp.position().x() << "/" << tp.position().y() << "@" << tp.pressure(); + pp::TouchPoint tp = touch_event.GetTouchByIndex( + PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); + status << std::endl << tp.id() << "//" + << tp.position().x() << "/" << tp.position().y() + << "@" << tp.pressure(); } LogMessage(0, status.str()); @@ -501,7 +525,7 @@ class CriatInstance : public pp::Instance { } private: - /* Initialize Graphics context */ + /* Initializes Graphics context */ void InitContext() { if (view_rect_.width() <= 0 || view_rect_.height() <= 0) return; @@ -528,6 +552,7 @@ class CriatInstance : public pp::Instance { force_refresh_ = true; } + /* Requests the server for a resolution change. */ void ChangeResolution(int width, int height) { std::ostringstream status; status << "Asked for resolution " << width << "x" << height; @@ -549,8 +574,8 @@ class CriatInstance : public pp::Instance { } } - /* Convert "IE"/JavaScript keycode to X11 KeySym, - * see http://unixpapa.com/js/key.html */ + /* Converts "IE"/JavaScript keycode to X11 KeySym. + * See http://unixpapa.com/js/key.html */ uint32_t KeyCodeToKeySym(uint32_t keycode, std::string code) { if (code == "ControlLeft") return 0xffe3; if (code == "ControlRight") return 0xffe4; @@ -622,6 +647,7 @@ class CriatInstance : public pp::Instance { return 0x00; } + /* Changes the target FPS: avoid unecessary refreshes to save CPU */ void SetTargetFPS(int new_target_fps) { /* When increasing the fps, immediately ask for a frame, and force refresh * the display (we probably just gained focus). */ @@ -632,8 +658,9 @@ class CriatInstance : public pp::Instance { target_fps_ = new_target_fps; } - /* Send a mouse click. - * button is a X11 button number (e.g. 1 is left click) */ + /* Sends a mouse click. + * - button is a X11 button number (e.g. 1 is left click) + * SocketSend flushes the mouse position before the click is sent. */ void SendClick(int button, int down) { struct mouseclick* mc; @@ -653,6 +680,7 @@ class CriatInstance : public pp::Instance { SetTargetFPS(kFullFPS); } + /* Sends a key press */ void SendKey(uint32_t keysym, int down) { struct key* k; pp::VarArrayBuffer array_buffer(sizeof(*k)); @@ -667,8 +695,8 @@ class CriatInstance : public pp::Instance { SetTargetFPS(kFullFPS); } - /* Request the next framebuffer grab */ - /* The parameter is a token that must be equal to request_token_. + /* Requests the next framebuffer grab. + * The parameter is a token that must be equal to request_token_. * This makes sure only one screen requests is waiting at one time * (e.g. when changing frame rate), since we have no way of cancelling * scheduled callbacks. */ @@ -711,13 +739,14 @@ class CriatInstance : public pp::Instance { SocketSend(array_buffer, true); } - /* Last frame was displayed (Vsync-ed): allocate next buffer and request - frame. */ - void OnFlush(int32_t result = 0) { + /* Called when the last frame was displayed (Vsync-ed): allocates next + * buffer and requests next frame. + * Parameter is ignored: used for callbacks */ + void OnFlush(int32_t /*result*/ = 0) { PP_Time time_ = pp::Module::Get()->core()->GetTime(); PP_Time deltat = time_-lasttime_; - double delay = (target_fps_ > 0) ? (1.0/target_fps_ - deltat) : INFINITY; + double delay = (target_fps_>0) ? (1.0/target_fps_ - deltat) : INFINITY; double cfps = deltat > 0 ? 1.0/deltat : 1000; lasttime_ = time_; @@ -726,7 +755,8 @@ class CriatInstance : public pp::Instance { avgfps_ = 0.9*avgfps_ + 0.1*cfps; if ((k_ % ((int)avgfps_+1)) == 0 || debug_ >= 1) { std::stringstream ss; - ss << "fps: " << (int)(cfps+0.5) << " (" << (int)(avgfps_+0.5) << ")" + ss << "fps: " << (int)(cfps+0.5) + << " (" << (int)(avgfps_+0.5) << ")" << " delay: " << (int)(delay*1000) << " deltat: " << (int)(deltat*1000) << " target fps: " << (int)(target_fps_) @@ -756,11 +786,12 @@ class CriatInstance : public pp::Instance { } } - /* Paint the frame. */ - void Paint(bool blank = false) { + /* Paints the frame. In our context, simply replace the front buffer + * content with image_data_. */ + void Paint(bool blank) { if (context_.is_null()) { - /* The current Graphics2D context is null, so updating and rendering is - * pointless. */ + /* The current Graphics2D context is null, so updating and rendering + * is pointless. */ flush_context_ = context_; return; } diff --git a/src/fbserver-proto.h b/src/fbserver-proto.h index 982783b51..26b01db95 100644 --- a/src/fbserver-proto.h +++ b/src/fbserver-proto.h @@ -6,6 +6,9 @@ * */ +#ifndef FB_SERVER_PROTO_H_ +#define FB_SERVER_PROTO_H_ + #include /* WebSocket constants */ @@ -70,3 +73,5 @@ struct __attribute__((__packed__)) mouseclick { uint8_t down:1; uint8_t button; }; + +#endif /* FB_SERVER_PROTO_H_ */ From cd2d4bbea2f476283085cda265d4a2a013ca5ee4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 11:30:06 -0800 Subject: [PATCH 055/334] cirat: OnSocketReceiveCompletion, split into multiple functions. --- host-ext/nacl_src/criat.cc | 274 ++++++++++++++++++++----------------- 1 file changed, 152 insertions(+), 122 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 87735c9b4..234b879d7 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -156,6 +156,12 @@ class CriatInstance : public pp::Instance { StatusMessage("Connected."); } + /* Closes the WebSocket connection. */ + void SocketClose(std::string reason) { + websocket_.Close(0, pp::Var(reason), + callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); + } + /* Called when WebSocket is closed */ void OnSocketClosed(int32_t result) { StatusMessage("Disconnected..."); @@ -181,6 +187,131 @@ class CriatInstance : public pp::Instance { return false; } + /* Receives and handles a version request */ + bool SocketParseVersion(const char* data, int datalen) { + if (connected_) { + LogMessage(-1, "Got a version while connected?!?"); + return false; + } + if (strcmp(data, VERSION)) { + LogMessage(-1, "Invalid version received (" + + std::string(data) + ")."); + return false; + } + connected_ = true; + SocketSend(pp::Var("VOK"), false); + ControlMessage("connected", "Version received"); + ChangeResolution(size_.width(), size_.height()); + // Start requesting frames + OnFlush(); + return true; + } + + /* Receives and handles a screen_reply request */ + bool SocketParseScreen(const char* data, int datalen) { + if (!CheckSize(datalen, sizeof(struct screen_reply), "screen_reply")) + return false; + + struct screen_reply* reply = (struct screen_reply*)data; + if (reply->updated) { + if (!reply->shmfailed) { + Paint(false); + } else { + /* Blank the frame if shm failed */ + Paint(true); + force_refresh_ = true; + } + } else { + screen_flying_ = false; + /* No update: Ask for next frame in 1000/target_fps_ */ + if (target_fps_ > 0) { + pp::Module::Get()->core()->CallOnMainThread( + 1000/target_fps_, + callback_factory_.NewCallback( + &CriatInstance::RequestScreen), + request_token_); + } + } + + if (reply->cursor_updated) { + /* Cursor updated: find it in cache */ + std::unordered_map::iterator it = + cursor_cache_.find(reply->cursor_serial); + if (it == cursor_cache_.end()) { + /* No cache entry, ask for data. */ + SocketSend(pp::Var("P"), false); + } else { + std::ostringstream status; + status << "Cursor use cache for " << (reply->cursor_serial); + LogMessage(2, status.str()); + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + it->second.img, it->second.hot); + } + } + return true; + } + + /* Receives and handles a cursor_reply request */ + bool SocketParseCursor(const char* data, int datalen) { + if (datalen < sizeof(struct cursor_reply)) { + std::stringstream status; + status << "Invalid cursor_reply packet (" << datalen + << " < " << sizeof(struct cursor_reply) << ")."; + LogMessage(-1, status.str()); + return false; + } + + struct cursor_reply* cursor = (struct cursor_reply*)data; + if (!CheckSize(datalen, + sizeof(struct cursor_reply) + + 4*cursor->width*cursor->height, + "cursor_reply")) + return false; + + std::ostringstream status; + status << "Cursor " << (cursor->width) << "/" << (cursor->height); + status << " " << (cursor->xhot) << "/" << (cursor->yhot); + status << " " << (cursor->cursor_serial); + LogMessage(0, status.str()); + + /* Scale down if needed */ + int scale = 1; + while (cursor->width/scale > 32 || cursor->height/scale > 32) + scale *= 2; + + int w = cursor->width/scale; + int h = cursor->height/scale; + pp::ImageData img(this, pp::ImageData::GetNativeImageDataFormat(), + pp::Size(w, h), true); + uint32_t* imgdata = (uint32_t*)img.data(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + /* Nearest neighbour is least ugly */ + imgdata[y*w+x] = cursor->pixels[scale*y*scale*w+scale*x]; + } + } + pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); + + cursor_cache_[cursor->cursor_serial].img = img; + cursor_cache_[cursor->cursor_serial].hot = hot; + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + img, hot); + return true; + } + + /* Receives and handles a resolution request */ + bool SocketParseResolution(const char* data, int datalen) { + if (!CheckSize(datalen, sizeof(struct resolution), "resolution")) + return false; + struct resolution* r = (struct resolution*)data; + std::ostringstream newres; + newres << (r->width/scale_) << "/" << (r->height/scale_); + /* Tell Javascript so that it can center us on the page */ + ControlMessage("resize", newres.str()); + force_refresh_ = true; + return true; + } + /* Called when a frame is received from WebSocket server */ void OnSocketReceiveCompletion(int32_t result) { std::stringstream status; @@ -193,7 +324,8 @@ class CriatInstance : public pp::Instance { /* Not fatal: just wait for next call */ return; } else if (result != PP_OK) { - goto error; + LogMessage(-1, "Receive error."); + SocketClose("Receive error."); } /* Get ready to receive next frame */ @@ -218,137 +350,35 @@ class CriatInstance : public pp::Instance { } if (data[0] == 'V') { /* Version */ - if (connected_) { - LogMessage(-1, "Got a version while connected?!?"); - goto error; - } - if (strcmp(data, VERSION)) { - LogMessage(-1, "Invalid version received (" + - std::string(data) + ")."); - goto error; - } - connected_ = true; - SocketSend(pp::Var("VOK"), false); - ControlMessage("connected", "Version received"); - ChangeResolution(size_.width(), size_.height()); - // Start requesting frames - OnFlush(); - return; - } + if (!SocketParseVersion(data, datalen)) + SocketClose("Incorrect version."); - if (!connected_) { - LogMessage(-1, "Got some packet before version..."); - goto error; + return; } - if (data[0] == 'S') { /* Screen */ - if (!CheckSize(datalen, sizeof(struct screen_reply), - "screen_reply")) - goto error; - struct screen_reply* reply = (struct screen_reply*)data; - if (reply->updated) { - if (!reply->shmfailed) { - Paint(false); - } else { - /* Blank the frame if shm failed */ - Paint(true); - force_refresh_ = true; - } - } else { - screen_flying_ = false; - /* No update: Ask for next frame in 1000/target_fps_ */ - if (target_fps_ > 0) { - pp::Module::Get()->core()->CallOnMainThread( - 1000/target_fps_, - callback_factory_.NewCallback( - &CriatInstance::RequestScreen), - request_token_); - } - } - - if (reply->cursor_updated) { - /* Cursor updated: find it in cache */ - std::unordered_map::iterator it = - cursor_cache_.find(reply->cursor_serial); - if (it == cursor_cache_.end()) { - /* No cache entry, ask for data. */ - SocketSend(pp::Var("P"), false); - } else { - std::ostringstream status; - status << "Cursor use cache for " << (reply->cursor_serial); - LogMessage(2, status.str()); - pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, - it->second.img, it->second.hot); - } - } - - return; - } else if (data[0] == 'P') { /* New cursor data is received */ - if (datalen < sizeof(struct cursor_reply)) { + if (connected_) { + switch(data[0]) { + case 'S': /* Screen */ + if (SocketParseScreen(data, datalen)) return; + break; + case 'P': /* New cursor data is received */ + if (SocketParseCursor(data, datalen)) return; + break; + case 'R': /* Resolution request reply */ + if (SocketParseResolution(data, datalen)) return; + break; + default: { std::stringstream status; - status << "Invalid cursor_reply packet (" << datalen - << " < " << sizeof(struct cursor_reply) << ")."; + status << "Invalid request. First char: " << (int)data[0]; LogMessage(-1, status.str()); - goto error; + /* Fall-through: disconnect. */ } - - struct cursor_reply* cursor = (struct cursor_reply*)data; - if (!CheckSize(datalen, - sizeof(struct cursor_reply) + - 4*cursor->width*cursor->height, - "cursor_reply")) - goto error; - - std::ostringstream status; - status << "Cursor " << (cursor->width) << "/" << (cursor->height); - status << " " << (cursor->xhot) << "/" << (cursor->yhot); - status << " " << (cursor->cursor_serial); - LogMessage(0, status.str()); - - /* Scale down if needed */ - int scale = 1; - while (cursor->width/scale > 32 || cursor->height/scale > 32) - scale *= 2; - - int w = cursor->width/scale; - int h = cursor->height/scale; - pp::ImageData img(this, pp::ImageData::GetNativeImageDataFormat(), - pp::Size(w, h), true); - uint32_t* data = (uint32_t*)img.data(); - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - /* Nearest neighbour is least ugly */ - data[y*w+x] = cursor->pixels[scale*y*scale*w+scale*x]; - } } - pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); - - cursor_cache_[cursor->cursor_serial].img = img; - cursor_cache_[cursor->cursor_serial].hot = hot; - pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, - img, hot); - return; - } else if (data[0] == 'R') { /* Resolution request reply */ - if (!CheckSize(datalen, sizeof(struct resolution), "resolution")) - goto error; - struct resolution* r = (struct resolution*)data; - std::ostringstream newres; - newres << (r->width/scale_) << "/" << (r->height/scale_); - /* Tell Javascript so that it can center us on the page */ - ControlMessage("resize", newres.str()); - force_refresh_ = true; - return; } else { - std::stringstream status; - status << "Error: first char " << (int)data[0]; - LogMessage(0, status.str()); - /* fall-through: disconnect */ + LogMessage(-1, "Got some packet before version..."); } - error: - LogMessage(-1, "Receive error."); - websocket_.Close(0, pp::Var("Receive error"), - callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); + SocketClose("Invalid payload."); } /* Asks to receive the next WebSocket frame From 4412895860c12f73a024829515023600a96793b9 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 12:45:14 -0800 Subject: [PATCH 056/334] criat: image_data_ as simple variable, instead of pointer. --- host-ext/nacl_src/criat.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 234b879d7..e08ca6d8a 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -757,11 +757,11 @@ class CriatInstance : public pp::Instance { s->shm = 1; s->refresh = force_refresh_; force_refresh_ = false; - s->width = image_data_->size().width(); - s->height = image_data_->size().height(); - s->paddr = (uint64_t)image_data_->data(); + s->width = image_data_.size().width(); + s->height = image_data_.size().height(); + s->paddr = (uint64_t)image_data_.data(); uint64_t sig = ((uint64_t)rand() << 32) ^ rand(); - uint64_t* data = static_cast(image_data_->data()); + uint64_t* data = static_cast(image_data_.data()); *data = sig; s->sig = sig; @@ -801,7 +801,7 @@ class CriatInstance : public pp::Instance { /* Allocate next image. If size_ is the same, the previous buffer will * be reused. */ PP_ImageDataFormat format = pp::ImageData::GetNativeImageDataFormat(); - image_data_ = new pp::ImageData(this, format, size_, false); + image_data_ = pp::ImageData(this, format, size_, false); /* Request for next frame */ if (isinf(delay)) { @@ -827,8 +827,8 @@ class CriatInstance : public pp::Instance { } if (blank) { - uint32_t* data = (uint32_t*)image_data_->data(); - int size = image_data_->size().width()*image_data_->size().height(); + uint32_t* data = (uint32_t*)image_data_.data(); + int size = image_data_.size().width()*image_data_.size().height(); for (int i = 0; i < size; i++) { if (debug_ == 0) data[i] = 0xFF000000; @@ -839,7 +839,7 @@ class CriatInstance : public pp::Instance { /* Using Graphics2D::ReplaceContents is the fastest way to update the * entire canvas every frame. */ - context_.ReplaceContents(image_data_); + context_.ReplaceContents(&image_data_); /* Store a reference to the context that is being flushed; this ensures * the callback is called, even if context_ changes before the flush @@ -867,7 +867,7 @@ class CriatInstance : public pp::Instance { pp::Size size_; float scale_ = 1.0f; - pp::ImageData* image_data_ = NULL; + pp::ImageData image_data_; int k_ = 0; pp::WebSocket websocket_{this}; From 19d7df04fe2d38ad92f91538e696064b1912881f Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 13:05:27 -0800 Subject: [PATCH 057/334] criat: Fix receive error code path. --- host-ext/nacl_src/criat.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index e08ca6d8a..9615086e7 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -326,6 +326,7 @@ class CriatInstance : public pp::Instance { } else if (result != PP_OK) { LogMessage(-1, "Receive error."); SocketClose("Receive error."); + return; } /* Get ready to receive next frame */ From a23969cf21d073a95d218f543fb22615d3410054 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 13:05:49 -0800 Subject: [PATCH 058/334] setres: Fix output in non-xiat mode. --- chroot-bin/setres | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chroot-bin/setres b/chroot-bin/setres index b049b69a2..e070e552d 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -36,7 +36,7 @@ if [ "$xmethod" != "xiat" ]; then xrandr --newmode "$mode" $data 2>/dev/null || true xrandr --addmode "$o" "$mode" xrandr --output "$o" --mode "$mode" - echo "$o" + echo "$mode" } exit 0 fi From 49e440b4d230098f423d39d590942bd5b9fb97ba Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 15:12:36 -0800 Subject: [PATCH 059/334] criat: num lock is fine. disable caps lock Reenable caps lock when we find a better solution. --- host-ext/nacl_src/criat.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 9615086e7..8e74592af 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -633,7 +633,7 @@ class CriatInstance : public pp::Instance { case 17: return 0xffe3; // control (caught earlier!) case 18: return 0xffe9; // alt (caught earlier!) case 19: return 0xff13; // pause - case 20: return 0xffe5; // caps lock + case 20: return 0; // caps lock (FIXME: reenable, 0xffe5) case 27: return 0xff1b; // esc case 32: return 0x20; // space case 33: return 0xff55; // page up @@ -653,7 +653,7 @@ class CriatInstance : public pp::Instance { case 109: return 0xffad; // num minus case 110: return 0xffae; // num dot case 111: return 0xffaf; // num divide - case 144: return 0xff7f; // num lock (maybe better not to pass through???) + case 144: return 0xff7f; // num lock case 145: return 0xff14; // scroll lock case 151: return 0x1008ff95; // WLAN case 166: return 0x1008ff26; // back From f52a6d31556072b501b6bfda66ecbb307809f6eb Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 15:16:53 -0800 Subject: [PATCH 060/334] criat: Fix scope of str. --- host-ext/nacl_src/criat.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index 8e74592af..b2aa9e283 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -336,6 +336,7 @@ class CriatInstance : public pp::Instance { /* Convert binary/text to char* */ const char* data; int datalen; + std::string str; if (receive_var_.is_array_buffer()) { pp::VarArrayBuffer array_buffer(receive_var_); data = static_cast(array_buffer.Map()); @@ -344,8 +345,8 @@ class CriatInstance : public pp::Instance { status << "receive (binary): " << data[0]; LogMessage(data[0] == 'S' ? 3 : 2, status.str()); } else { - LogMessage(3, "receive (text): " + receive_var_.AsString()); - std::string str = receive_var_.AsString(); + str = receive_var_.AsString(); + LogMessage(3, "receive (text): " + str); data = str.c_str(); datalen = str.length(); } From 3e8b55c6338ac9d386ca1b32f874ccc940e2a4e6 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 15:32:53 -0800 Subject: [PATCH 061/334] fbserver: Define variables where they are used. --- src/fbserver.c | 93 +++++++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 55 deletions(-) diff --git a/src/fbserver.c b/src/fbserver.c index c1f8e8e65..214e4ebd9 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -114,8 +114,6 @@ static void register_damage(Display *dpy, Window win) { } static int init_display(char* name) { - int event, error, major, minor; - dpy = XOpenDisplay(name); if (!dpy) { @@ -124,6 +122,7 @@ static int init_display(char* name) { } /* We need XTest, XDamage and XFixes */ + int event, error, major, minor; if (!XTestQueryExtension(dpy, &event, &error, &major, &minor)) { error("XTest not available!"); return -1; @@ -139,15 +138,14 @@ static int init_display(char* name) { return -1; } - Window root = DefaultRootWindow(dpy); - Window rootp, parent; - Window *children; - unsigned int nchildren, i; - /* Get notified when new windows are created. */ + Window root = DefaultRootWindow(dpy); XSelectInput(dpy, root, SubstructureNotifyMask); /* Register damage events for existing windows */ + Window rootp, parent; + Window *children; + unsigned int i, nchildren; XQueryTree(dpy, root, &rootp, &parent, &children, &nchildren); /* FIXME: We never reset the handler, is that a good thing? */ @@ -168,17 +166,17 @@ static int init_display(char* name) { * Reply must be a resolution in "canonical" form: x[_] */ /* FIXME: Maybe errors here should not be fatal... */ void change_resolution(const struct resolution* rin) { - char* cmd = "setres"; - char arg1[32], arg2[32], buffer[256]; - int c; - char* args[] = {cmd, arg1, arg2, NULL}; - char* endptr; - /* Setup parameters and run command */ + char arg1[32], arg2[32]; + int c; c = snprintf(arg1, sizeof(arg1), "%d", rin->width); trueorabort(c > 0, "snprintf"); c = snprintf(arg2, sizeof(arg2), "%d", rin->height); trueorabort(c > 0, "snprintf"); + + char* cmd = "setres"; + char* args[] = {cmd, arg1, arg2, NULL}; + char buffer[256]; log(2, "Running %s %s %s", cmd, arg1, arg2); c = popen2(cmd, args, NULL, 0, buffer, sizeof(buffer)); trueorabort(c > 0, "popen2"); @@ -192,6 +190,7 @@ void change_resolution(const struct resolution* rin) { trueorabort(cut, "Invalid answer: %s", buffer); *cut = 0; + char* endptr; long nwidth = strtol(buffer, &endptr, 10); trueorabort(buffer != endptr && *endptr == '\0', "Invalid width: '%s'", buffer); @@ -222,7 +221,6 @@ void close_mmap(struct cache_entry* entry) { * Reply must be in the form PID:file */ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { struct cache_entry* entry = NULL; - int try; /* Find entry in cache */ if (cache[0].paddr == paddr) @@ -236,6 +234,7 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { close_mmap(entry); } + int try; for (try = 0; try < 2; try++) { /* Check signature */ if (entry->map) { @@ -247,21 +246,22 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { } /* Setup parameters and run command */ - char* cmd = "croutonfindnacl"; - char arg1[32], arg2[32], buffer[256]; - char* args[] = {cmd, arg1, arg2, NULL}; - int c, i, p = 0; - char* endptr; + char arg1[32], arg2[32]; + int c; c = snprintf(arg1, sizeof(arg1), "%08lx", (long)paddr & 0xffffffff); trueorabort(c > 0, "snprintf"); - p = 0; + int i, p = 0; for (i = 0; i < 8; i++) { c = snprintf(arg2+p, sizeof(arg2)-p, "%02x", ((uint8_t*)&sig)[i]); trueorabort(c > 0, "snprintf"); p += c; } + + char* cmd = "croutonfindnacl"; + char* args[] = {cmd, arg1, arg2, NULL}; + char buffer[256]; log(2, "Running %s %s %s", cmd, arg1, arg2); c = popen2(cmd, args, NULL, 0, buffer, sizeof(buffer)); if (c <= 0) { @@ -279,6 +279,7 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { } *cut = 0; + char* endptr; long pid = strtol(buffer, &endptr, 10); if(buffer == endptr || *endptr != '\0') { error("Invalid pid: %s", buffer); @@ -320,10 +321,7 @@ int write_image(const struct screen* screen) { char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct screen_reply)]; struct screen_reply* reply = (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); - Window root = DefaultRootWindow(dpy); int refresh = 0; - XEvent ev; - int ret; memset(reply_raw, 0, sizeof(*reply_raw)); @@ -350,7 +348,7 @@ int write_image(const struct screen* screen) { shminfo.shmaddr = img->data = shmat(shminfo.shmid, 0, 0); trueorabort(shminfo.shmaddr != (void*)-1, "shmat"); shminfo.readOnly = False; - ret = XShmAttach(dpy, &shminfo); + int ret = XShmAttach(dpy, &shminfo); trueorabort(ret, "XShmAttach"); /* Force refresh */ refresh = 1; @@ -362,6 +360,7 @@ int write_image(const struct screen* screen) { refresh = 1; } + XEvent ev; /* Register damage on new windows */ while (XCheckTypedEvent(dpy, MapNotify, &ev)) { register_damage(dpy, ev.xcreatewindow.window); @@ -394,7 +393,7 @@ int write_image(const struct screen* screen) { } /* Get new image from framebuffer */ - XShmGetImage(dpy, root, img, 0, 0, AllPlanes); + XShmGetImage(dpy, DefaultRootWindow(dpy), img, 0, 0, AllPlanes); int size = img->bytes_per_line * img->height; @@ -451,7 +450,6 @@ int write_cursor() { char reply_raw[FRAMEMAXHEADERSIZE+replylength]; struct cursor_reply* reply = (struct cursor_reply*)(reply_raw+FRAMEMAXHEADERSIZE); - int i = 0; memset(reply_raw, 0, sizeof(*reply_raw)); @@ -462,20 +460,10 @@ int write_cursor() { reply->yhot = img->yhot; reply->cursor_serial = img->cursor_serial; /* This casts long[] to uint32_t[] */ + int i; for (i = 0; i < size; i++) reply->pixels[i] = img->pixels[i]; -#if 0 - /* Debug: dump cursor image */ - int x, y; - for (y = 0; y < img->height; y++) { - for (x = 0; x < img->width; x++) { - printf("%01x", (reply->pixels[y*img->width+x] >> 28) & 0xf); - } - printf("\n"); - } -#endif - socket_client_write_frame(reply_raw, replylength, WS_OPCODE_BINARY, 1); XFree(img); @@ -499,10 +487,6 @@ void usage(char* argv0) { int main(int argc, char** argv) { int c; - char* display; - char* endptr; - int displaynum; - while ((c = getopt(argc, argv, "v:")) != -1) { switch (c) { case 'v': @@ -516,11 +500,12 @@ int main(int argc, char** argv) { if (optind != argc-1) usage(argv[0]); - display = argv[optind]; + char* display = argv[optind]; trueorabort(display[0] == ':', "Invalid display: '%s'", display); - displaynum = (int)strtol(display+1, &endptr, 10); + char* endptr; + int displaynum = (int)strtol(display+1, &endptr, 10); trueorabort(display+1 != endptr && (*endptr == '\0' || *endptr == '.'), "Invalid display number: '%s'", display); @@ -531,11 +516,6 @@ int main(int argc, char** argv) { int length; while (1) { - struct key* k; - struct mouseclick* mc; - struct mousemove* mm; - KeyCode kc; - socket_server_accept(VERSION); while (1) { length = socket_client_read_frame((char*)buffer, sizeof(buffer)); @@ -567,11 +547,11 @@ int main(int argc, char** argv) { break; change_resolution((struct resolution*)buffer); break; - case 'K': /* Key */ + case 'K': { /* Key */ if (!check_size(length, sizeof(struct key), "key")) break; - k = (struct key*)buffer; - kc = XKeysymToKeycode(dpy, k->keysym); + struct key* k = (struct key*)buffer; + KeyCode kc = XKeysymToKeycode(dpy, k->keysym); log(2, "Key: ks=%04x kc=%04x\n", k->keysym, kc); if (kc != 0) { XTestFakeKeyEvent(dpy, kc, k->down, CurrentTime); @@ -581,21 +561,24 @@ int main(int argc, char** argv) { error("Invalid keysym %04x.", k->keysym); } break; - case 'C': /* Click */ + } + case 'C': { /* Click */ if (!check_size(length, sizeof(struct mouseclick), "mouseclick")) break; - mc = (struct mouseclick*)buffer; + struct mouseclick* mc = (struct mouseclick*)buffer; XTestFakeButtonEvent(dpy, mc->button, mc->down, CurrentTime); if (mc->down) kb_add(MOUSE, mc->button); else kb_remove(MOUSE, mc->button); break; - case 'M': /* Mouse move */ + } + case 'M': { /* Mouse move */ if (!check_size(length, sizeof(struct mousemove), "mousemove")) break; - mm = (struct mousemove*)buffer; + struct mousemove* mm = (struct mousemove*)buffer; XTestFakeMotionEvent(dpy, 0, mm->x, mm->y, CurrentTime); break; + } case 'Q': /* "Quit": release all keys */ kb_release_all(); break; From 27a2f2f74a73089453543e4b3937d7587cdec1c7 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 15:42:31 -0800 Subject: [PATCH 062/334] fbserver.c: Style fixes. --- src/fbserver.c | 67 ++++++++++++++++++++++++++++--------------------- src/websocket.c | 1 - src/websocket.h | 1 + 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/fbserver.c b/src/fbserver.c index 214e4ebd9..accfd1ec2 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -8,7 +8,6 @@ * */ -#define _GNU_SOURCE /* for ppoll */ #include "websocket.h" #include "fbserver-proto.h" #include @@ -23,10 +22,10 @@ #include #include +/* X11-related variables */ static Display *dpy; - -int damageEvent; -int fixesEvent; +static int damageEvent; +static int fixesEvent; /* shm entry cache */ struct cache_entry { @@ -36,8 +35,8 @@ struct cache_entry { size_t length; /* mmap length */ }; -struct cache_entry cache[2]; -int next_entry; +static struct cache_entry cache[2]; +static int next_entry; /* Remember which keys/buttons are currently pressed */ typedef enum { INVALID=0, MOUSE=1, KEYBOARD=2 } keybuttontype; @@ -48,9 +47,10 @@ struct keybutton { /* Store currently pressed keys/buttons in an array. * No valid entry on or after curmax. */ -struct keybutton pressed[256]; -int curmax = 0; +static struct keybutton pressed[256]; +static int curmax = 0; +/* Adds a key/button to array of pressed keys */ void kb_add(keybuttontype type, uint32_t code) { trueorabort(curmax < sizeof(pressed)/sizeof(struct keybutton), "Too many keys pressed"); @@ -70,6 +70,7 @@ void kb_add(keybuttontype type, uint32_t code) { curmax++; } +/* Removes a key/button to array of pressed keys */ void kb_remove(keybuttontype type, uint32_t code) { int lastvalid = -1; int i; @@ -83,6 +84,7 @@ void kb_remove(keybuttontype type, uint32_t code) { curmax = lastvalid+1; } +/* Releases all pressed key/buttons, and empties array */ void kb_release_all() { int i; log(2, "Releasing all keys..."); @@ -104,7 +106,7 @@ static int xerror_handler(Display *dpy, XErrorEvent *e) { return 0; } -/* Register XDamage events for a given Window. */ +/* Registers XDamage events for a given Window. */ static void register_damage(Display *dpy, Window win) { XWindowAttributes attrib; if (XGetWindowAttributes(dpy, win, &attrib) && @@ -113,6 +115,7 @@ static void register_damage(Display *dpy, Window win) { } } +/* Connects to the X11 display, initializes extensions, register for events */ static int init_display(char* name) { dpy = XOpenDisplay(name); @@ -162,7 +165,7 @@ static int init_display(char* name) { return 0; } -/* Change resolution using external handler. +/* Changes resolution using external handler. * Reply must be a resolution in "canonical" form: x[_] */ /* FIXME: Maybe errors here should not be fatal... */ void change_resolution(const struct resolution* rin) { @@ -193,10 +196,10 @@ void change_resolution(const struct resolution* rin) { char* endptr; long nwidth = strtol(buffer, &endptr, 10); trueorabort(buffer != endptr && *endptr == '\0', - "Invalid width: '%s'", buffer); + "Invalid width: '%s'", buffer); long nheight = strtol(cut+1, &endptr, 10); trueorabort(cut+1 != endptr && (*endptr == '\0' || *endptr == '\n'), - "Invalid height: '%s'", cut+1); + "Invalid height: '%s'", cut+1); log(1, "New resolution %ld x %ld", nwidth, nheight); char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct resolution)]; @@ -207,6 +210,7 @@ void change_resolution(const struct resolution* rin) { socket_client_write_frame(reply_raw, sizeof(*r), WS_OPCODE_BINARY, 1); } +/* Closes the mmap/fd in the entry. */ void close_mmap(struct cache_entry* entry) { if (!entry->map) return; @@ -217,17 +221,17 @@ void close_mmap(struct cache_entry* entry) { entry->map = NULL; } -/* Find NaCl/Chromium shm memory using external handler. +/* Finds NaCl/Chromium shm memory using external handler. * Reply must be in the form PID:file */ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { struct cache_entry* entry = NULL; /* Find entry in cache */ - if (cache[0].paddr == paddr) + if (cache[0].paddr == paddr) { entry = &cache[0]; - else if (cache[1].paddr == paddr) + } else if (cache[1].paddr == paddr) { entry = &cache[1]; - else { + } else { /* Not found: erase an existing entry. */ entry = &cache[next_entry]; next_entry = (next_entry + 1) % 2; @@ -238,9 +242,9 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { for (try = 0; try < 2; try++) { /* Check signature */ if (entry->map) { - if (*((uint64_t*)entry->map) == sig) { + if (*((uint64_t*)entry->map) == sig) return entry; - } + error("Invalid signature, fetching new shm!"); close_mmap(entry); } @@ -316,11 +320,11 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { XImage* img = NULL; XShmSegmentInfo shminfo; -/* Write framebuffer image to websocket/shm */ +/* Writes framebuffer image to websocket/shm */ int write_image(const struct screen* screen) { char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct screen_reply)]; struct screen_reply* reply = - (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); int refresh = 0; memset(reply_raw, 0, sizeof(*reply_raw)); @@ -398,7 +402,7 @@ int write_image(const struct screen* screen) { int size = img->bytes_per_line * img->height; trueorabort(size == screen->width*screen->height*4, - "Invalid screen byte count"); + "Invalid screen byte count"); if (screen->shm) { struct cache_entry* entry = find_shm(screen->paddr, screen->sig, size); @@ -442,14 +446,14 @@ int write_image(const struct screen* screen) { return 0; } -/* Write cursor image to websocket */ +/* Writes cursor image to websocket */ int write_cursor() { XFixesCursorImage *img = XFixesGetCursorImage(dpy); int size = img->width*img->height; const int replylength = sizeof(struct cursor_reply)+sizeof(uint32_t)*size; char reply_raw[FRAMEMAXHEADERSIZE+replylength]; struct cursor_reply* reply = - (struct cursor_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + (struct cursor_reply*)(reply_raw+FRAMEMAXHEADERSIZE); memset(reply_raw, 0, sizeof(*reply_raw)); @@ -470,7 +474,7 @@ int write_cursor() { return 0; } -/* Check if a packet size is correct */ +/* Checks if a packet size is correct */ int check_size(int length, int target, char* error) { if (length != target) { error("Invalid %s packet (%d != %d)", error, length, target); @@ -480,6 +484,7 @@ int check_size(int length, int target, char* error) { return 1; } +/* Prints usage */ void usage(char* argv0) { fprintf(stderr, "%s [-v 0-3] display\n", argv0); exit(1); @@ -555,8 +560,11 @@ int main(int argc, char** argv) { log(2, "Key: ks=%04x kc=%04x\n", k->keysym, kc); if (kc != 0) { XTestFakeKeyEvent(dpy, kc, k->down, CurrentTime); - if (k->down) kb_add(KEYBOARD, kc); - else kb_remove(KEYBOARD, kc); + if (k->down) { + kb_add(KEYBOARD, kc); + } else { + kb_remove(KEYBOARD, kc); + } } else { error("Invalid keysym %04x.", k->keysym); } @@ -568,8 +576,11 @@ int main(int argc, char** argv) { break; struct mouseclick* mc = (struct mouseclick*)buffer; XTestFakeButtonEvent(dpy, mc->button, mc->down, CurrentTime); - if (mc->down) kb_add(MOUSE, mc->button); - else kb_remove(MOUSE, mc->button); + if (mc->down) { + kb_add(MOUSE, mc->button); + } else { + kb_remove(MOUSE, mc->button); + } break; } case 'M': { /* Mouse move */ diff --git a/src/websocket.c b/src/websocket.c index 9ebdac696..94720b33c 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -7,7 +7,6 @@ * */ -#define _GNU_SOURCE /* for ppoll */ #include "websocket.h" #include #include diff --git a/src/websocket.h b/src/websocket.h index fb1b78c14..f324580e6 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -12,6 +12,7 @@ * - Ping packets */ +#define _GNU_SOURCE /* for ppoll */ #include #include #include From f2b6ac652c7849abd56fd85124e40361ce655705 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 15:50:59 -0800 Subject: [PATCH 063/334] websocket.h: Define bitmask constants. --- src/websocket.h | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/websocket.h b/src/websocket.h index f324580e6..809959fb3 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -44,6 +44,13 @@ const int WS_OPCODE_CLOSE = 0x8; const int WS_OPCODE_PING = 0x9; const int WS_OPCODE_PONG = 0xA; +/* WebSocket bitmasks */ +const char WS_HEADER0_FIN = 0x80; /* fin */ +const char WS_HEADER0_RSV = 0x70; /* reserved */ +const char WS_HEADER0_OPCODE_MASK = 0x0F; /* reserved */ +const char WS_HEADER1_MASK = 0x80; /* mask */ +const char WS_HEADER1_LEN_MASK = 0x7F; /* payload length */ + /* 0 - Quiet * 1 - General messages (init, new connections) * 2 - 1 + Information on each transfer @@ -56,6 +63,7 @@ static int verbose = 0; #define error(str, ...) printf("%s: " str "\n", __func__, ##__VA_ARGS__) +/* Aborts if expr is false */ #define trueorabort(expr, str, ...) do { \ if (!(expr)) { \ printf("%s: ASSERTION " #expr " FAILED (" str ")\n", \ @@ -331,8 +339,8 @@ static int socket_client_write_frame(char* buffer, unsigned int size, } } - pbuffer[0] = opcode & 0x0f; - if (fin) pbuffer[0] |= 0x80; + pbuffer[0] = opcode & WS_HEADER0_OPCODE_MASK; + if (fin) pbuffer[0] |= WS_HEADER0_FIN; pbuffer[1] = payloadlen; /* No mask (0x80) in server->client direction */ int wlen = 2+extlensize+size; @@ -373,15 +381,15 @@ static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, int opcode, mask; uint64_t length; - *fin = (header[0] & 0x80) != 0; - if (header[0] & 0x70) { /* Reserved bits are on */ + *fin = (header[0] & WS_HEADER0_FIN) != 0; + if (header[0] & WS_HEADER0_RSV) { /* Reserved bits are on */ error("Reserved bits are on."); socket_client_close(1); return -1; } - opcode = header[0] & 0x0F; - mask = (header[1] & 0x80) != 0; - length = header[1] & 0x7F; + opcode = header[0] & WS_HEADER0_OPCODE_MASK; + mask = (header[1] & WS_HEADER1_MASK) != 0; + length = header[1] & WS_HEADER1_LEN_MASK; log(2, "fin=%d; opcode=%d; mask=%d; length=%llu", *fin, opcode, mask, (long long unsigned int)length); From 9b038c52a07b60563f44b1c8591a8a3d2399d1f9 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 16:18:09 -0800 Subject: [PATCH 064/334] fbserver: Better tracking of pressed key/buttons. Adding O(1), removing O(n). --- src/fbserver.c | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/fbserver.c b/src/fbserver.c index accfd1ec2..b9635bbc9 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -39,7 +39,7 @@ static struct cache_entry cache[2]; static int next_entry; /* Remember which keys/buttons are currently pressed */ -typedef enum { INVALID=0, MOUSE=1, KEYBOARD=2 } keybuttontype; +typedef enum { MOUSE=1, KEYBOARD=2 } keybuttontype; struct keybutton { keybuttontype type; uint32_t code; /* KeyCode or mouse button */ @@ -48,47 +48,37 @@ struct keybutton { /* Store currently pressed keys/buttons in an array. * No valid entry on or after curmax. */ static struct keybutton pressed[256]; -static int curmax = 0; +static int pressed_len = 0; /* Adds a key/button to array of pressed keys */ void kb_add(keybuttontype type, uint32_t code) { - trueorabort(curmax < sizeof(pressed)/sizeof(struct keybutton), + trueorabort(pressed_len < sizeof(pressed)/sizeof(struct keybutton), "Too many keys pressed"); - int firstfree = curmax; - int i; - for (i = 0; i < curmax; i++) { - if (pressed[i].type == type && pressed[i].code == code) { - /* Key already recorded */ - return; - } else if (pressed[i].type == INVALID && firstfree == curmax) { - firstfree = i; - } - } - pressed[firstfree].type = type; - pressed[firstfree].code = code; - if (firstfree == curmax) - curmax++; + pressed[pressed_len].type = type; + pressed[pressed_len].code = code; + pressed_len++; } -/* Removes a key/button to array of pressed keys */ +/* Removes a key/button from array of pressed keys */ void kb_remove(keybuttontype type, uint32_t code) { - int lastvalid = -1; int i; - for (i = 0; i < curmax; i++) { + for (i = 0; i < pressed_len; i++) { if (pressed[i].type == type && pressed[i].code == code) { - pressed[i].type = INVALID; - } else if (pressed[i].type != INVALID) { - lastvalid = i; + if (i < pressed_len-1) { + pressed[i].type = pressed[pressed_len-1].type; + pressed[i].code = pressed[pressed_len-1].code; + } + pressed_len--; + return; } } - curmax = lastvalid+1; } /* Releases all pressed key/buttons, and empties array */ void kb_release_all() { int i; log(2, "Releasing all keys..."); - for (i = 0; i < curmax; i++) { + for (i = 0; i < pressed_len; i++) { if (pressed[i].type == MOUSE) { log(2, "Mouse %d", pressed[i].code); XTestFakeButtonEvent(dpy, pressed[i].code, 0, CurrentTime); @@ -97,7 +87,7 @@ void kb_release_all() { XTestFakeKeyEvent(dpy, pressed[i].code, 0, CurrentTime); } } - curmax = 0; + pressed_len = 0; } /* X11-related functions */ From 40eeb5b5b14248227d387bd24c6492d4cd265838 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 16:42:03 -0800 Subject: [PATCH 065/334] fbserver: Fix memory leak, initialisation problem. From valgrind output. --- src/fbserver.c | 11 ++++++----- src/websocket.h | 2 ++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/fbserver.c b/src/fbserver.c index b9635bbc9..33f416954 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -64,10 +64,9 @@ void kb_remove(keybuttontype type, uint32_t code) { int i; for (i = 0; i < pressed_len; i++) { if (pressed[i].type == type && pressed[i].code == code) { - if (i < pressed_len-1) { - pressed[i].type = pressed[pressed_len-1].type; - pressed[i].code = pressed[pressed_len-1].code; - } + if (i < pressed_len-1) + pressed[i] = pressed[pressed_len-1]; + pressed_len--; return; } @@ -149,6 +148,8 @@ static int init_display(char* name) { register_damage(dpy, children[i]); } + XFree(children); + /* Register for cursor events */ XFixesSelectCursorInput(dpy, root, XFixesDisplayCursorNotifyMask); @@ -317,7 +318,7 @@ int write_image(const struct screen* screen) { (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); int refresh = 0; - memset(reply_raw, 0, sizeof(*reply_raw)); + memset(reply_raw, 0, sizeof(reply_raw)); reply->type = 'S'; reply->width = screen->width; diff --git a/src/websocket.h b/src/websocket.h index 809959fb3..7e894a350 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -831,6 +831,8 @@ static int socket_server_accept(char* version) { exit(1); } + /* Make sure sscanf does not read too much data */ + buffer[2*SHA1_LEN+1] = '\0'; for (i = 0; i < SHA1_LEN; i++) { unsigned int value; if (sscanf(&buffer[i*2], "%02x", &value) != 1) { From 4be4313d759b627107b626e2643ec4839812f68e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 16:53:48 -0800 Subject: [PATCH 066/334] xiat: Add 4K resolution. --- targets/xiat | 1 + 1 file changed, 1 insertion(+) diff --git a/targets/xiat b/targets/xiat index 93f5368da..0c3597977 100644 --- a/targets/xiat +++ b/targets/xiat @@ -34,6 +34,7 @@ echo ' 1920 1080 l # FHD 1920 1200 l # Standard 20" 2560 1600 l # Standard 30" +3840 2160 h # 4K ' | while read -r w h flags _; do if [ -z "$w" ]; then continue From 464c42638d2b9bc355cb0dd019d1f599f62b3381 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 17:24:18 -0800 Subject: [PATCH 067/334] Update instructions. --- host-ext/nacl_src/criat.cc | 4 ++-- targets/xiat | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index b2aa9e283..c28f834b9 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -451,7 +451,7 @@ class CriatInstance : public pp::Instance { if (!down) ControlMessage("state", "fullscreen"); return PP_TRUE; - } else if (keycode == 182) { /* Expos'e => minimize window */ + } else if (keycode == 182) { /* Page flipper => minimize window */ if (!down) ControlMessage("state", "hide"); return PP_TRUE; @@ -660,7 +660,7 @@ class CriatInstance : public pp::Instance { case 166: return 0x1008ff26; // back case 167: return 0x1008ff27; // forward case 168: return 0x1008ff73; // refresh - case 182: return 0x1008ff51; // "expos'e" ("F5") + case 182: return 0x1008ff51; // page flipper ("F5") case 183: return 0x1008ff59; // fullscreen/display case 186: return 0x3b; // ; case 187: return 0x3d; // = diff --git a/targets/xiat b/targets/xiat index 0c3597977..a0af908f5 100644 --- a/targets/xiat +++ b/targets/xiat @@ -67,9 +67,10 @@ mv -f "$tmp" /etc/X11/xorg-dummy.conf chmod 0644 /etc/X11/xorg-dummy.conf undotrap -TIPS="$TIPS -You can flip through your running chroot desktops and Chromium OS by hitting -Ctrl+Alt+Shift+Back and Ctrl+Alt+Shift+Forward. -" +TIPS="$TIPS"' +You can open your running chroot desktops by clicking on the extension icon. +Once in a crouton window, press fullscreen or the "switch window" key to switch +back to Chromium OS. +' ### append x11-common From bbf814af57559541da6d9e6ded5e78c68e7444c1 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 6 Nov 2014 09:48:36 -0800 Subject: [PATCH 068/334] Indentation and style fixes. --- host-ext/nacl_src/criat.cc | 152 ++++++++++++++++++------------------- src/fbserver-proto.h | 56 ++++++++------ src/fbserver.c | 40 +++++----- src/websocket.h | 52 ++++++------- 4 files changed, 153 insertions(+), 147 deletions(-) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/criat.cc index c28f834b9..df975edc5 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/criat.cc @@ -131,7 +131,7 @@ class CriatInstance : public pp::Instance { } std::ostringstream url; - url << "ws://localhost:" << (PORT_BASE+display_) << "/"; + url << "ws://localhost:" << (PORT_BASE + display_) << "/"; websocket_.Connect(pp::Var(url.str()), NULL, 0, callback_factory_.NewCallback( &CriatInstance::OnSocketConnectCompletion)); @@ -202,7 +202,7 @@ class CriatInstance : public pp::Instance { SocketSend(pp::Var("VOK"), false); ControlMessage("connected", "Version received"); ChangeResolution(size_.width(), size_.height()); - // Start requesting frames + /* Start requesting frames */ OnFlush(); return true; } @@ -287,7 +287,7 @@ class CriatInstance : public pp::Instance { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { /* Nearest neighbour is least ugly */ - imgdata[y*w+x] = cursor->pixels[scale*y*scale*w+scale*x]; + imgdata[y*w + x] = cursor->pixels[scale*y*scale*w + scale*x]; } } pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); @@ -351,7 +351,7 @@ class CriatInstance : public pp::Instance { datalen = str.length(); } - if (data[0] == 'V') { /* Version */ + if (data[0] == 'V') { /* Version */ if (!SocketParseVersion(data, datalen)) SocketClose("Incorrect version."); @@ -359,14 +359,14 @@ class CriatInstance : public pp::Instance { } if (connected_) { - switch(data[0]) { - case 'S': /* Screen */ + switch (data[0]) { + case 'S': /* Screen */ if (SocketParseScreen(data, datalen)) return; break; - case 'P': /* New cursor data is received */ + case 'P': /* New cursor data is received */ if (SocketParseCursor(data, datalen)) return; break; - case 'R': /* Resolution request reply */ + case 'R': /* Resolution request reply */ if (SocketParseResolution(data, datalen)) return; break; default: { @@ -447,11 +447,11 @@ class CriatInstance : public pp::Instance { LogMessage(1, status.str()); - if (keycode == 183) { /* Fullscreen => toggle fullscreen */ + if (keycode == 183) { /* Fullscreen => toggle fullscreen */ if (!down) ControlMessage("state", "fullscreen"); return PP_TRUE; - } else if (keycode == 182) { /* Page flipper => minimize window */ + } else if (keycode == 182) { /* Page flipper => minimize window */ if (!down) ControlMessage("state", "hide"); return PP_TRUE; @@ -496,7 +496,7 @@ class CriatInstance : public pp::Instance { * before sending the click event. * Also, Javascript button numbers are 0-based (left=0), while * X11 numbers are 1-based (left=1). */ - SendClick(mouse_event.GetButton()+1, down ? 1 : 0); + SendClick(mouse_event.GetButton() + 1, down ? 1 : 0); } LogMessage(3, status.str()); @@ -551,7 +551,7 @@ class CriatInstance : public pp::Instance { } LogMessage(0, status.str()); - } /* FIXME: Handle IMEInputEvents too */ + } /* FIXME: Handle IMEInputEvents too */ return PP_TRUE; } @@ -599,7 +599,7 @@ class CriatInstance : public pp::Instance { r->height = height; array_buffer.Unmap(); SocketSend(array_buffer, false); - } else { /* Just assume we can take up the space */ + } else { /* Just assume we can take up the space */ std::ostringstream status; status << width/scale_ << "/" << height/scale_; ControlMessage("resize", status.str()); @@ -616,64 +616,64 @@ class CriatInstance : public pp::Instance { if (code == "ShiftLeft") return 0xffe1; if (code == "ShiftRight") return 0xffe2; - if (keycode >= 65 && keycode <= 90) /* A to Z */ - return keycode+32; - if (keycode >= 48 && keycode <= 57) /* 0 to 9 */ + if (keycode >= 65 && keycode <= 90) /* A to Z */ + return keycode + 32; + if (keycode >= 48 && keycode <= 57) /* 0 to 9 */ return keycode; - if (keycode >= 96 && keycode <= 105) /* KP 0 to 9 */ - return keycode-96+0xffb0; - if (keycode >= 112 && keycode <= 123) /* F1-F12 */ - return keycode-112+0xffbe; - - switch(keycode) { - case 8: return 0xff08; // backspace - case 9: return 0xff09; // tab - case 12: return 0xff9d; // num 5 - case 13: return 0xff0d; // enter - case 16: return 0xffe1; // shift (caught earlier!) - case 17: return 0xffe3; // control (caught earlier!) - case 18: return 0xffe9; // alt (caught earlier!) - case 19: return 0xff13; // pause - case 20: return 0; // caps lock (FIXME: reenable, 0xffe5) - case 27: return 0xff1b; // esc - case 32: return 0x20; // space - case 33: return 0xff55; // page up - case 34: return 0xff56; // page down - case 35: return 0xff57; // end - case 36: return 0xff50; // home - case 37: return 0xff51; // left - case 38: return 0xff52; // top - case 39: return 0xff53; // right - case 40: return 0xff54; // bottom - case 42: return 0xff61; // print screen - case 45: return 0xff63; // insert - case 46: return 0xffff; // delete - case 91: return kSUPER_L; // super - case 106: return 0xffaa; // num multiply - case 107: return 0xffab; // num plus - case 109: return 0xffad; // num minus - case 110: return 0xffae; // num dot - case 111: return 0xffaf; // num divide - case 144: return 0xff7f; // num lock - case 145: return 0xff14; // scroll lock - case 151: return 0x1008ff95; // WLAN - case 166: return 0x1008ff26; // back - case 167: return 0x1008ff27; // forward - case 168: return 0x1008ff73; // refresh - case 182: return 0x1008ff51; // page flipper ("F5") - case 183: return 0x1008ff59; // fullscreen/display - case 186: return 0x3b; // ; - case 187: return 0x3d; // = - case 188: return 0x2c; // , - case 189: return 0x2d; // - - case 190: return 0x2e; // . - case 191: return 0x2f; // / - case 192: return 0x60; // ` - case 219: return 0x5b; // [ - case 220: return 0x5c; // '\' - case 221: return 0x5d; // ] - case 222: return 0x27; // ' - case 229: return 0; // dead key (', `, ~): no way of knowing which... + if (keycode >= 96 && keycode <= 105) /* KP 0 to 9 */ + return keycode - 96 + 0xffb0; + if (keycode >= 112 && keycode <= 123) /* F1-F12 */ + return keycode - 112 + 0xffbe; + + switch (keycode) { + case 8: return 0xff08; // backspace + case 9: return 0xff09; // tab + case 12: return 0xff9d; // num 5 + case 13: return 0xff0d; // enter + case 16: return 0xffe1; // shift (caught earlier!) + case 17: return 0xffe3; // control (caught earlier!) + case 18: return 0xffe9; // alt (caught earlier!) + case 19: return 0xff13; // pause + case 20: return 0; // caps lock. FIXME: reenable (0xffe5) + case 27: return 0xff1b; // esc + case 32: return 0x20; // space + case 33: return 0xff55; // page up + case 34: return 0xff56; // page down + case 35: return 0xff57; // end + case 36: return 0xff50; // home + case 37: return 0xff51; // left + case 38: return 0xff52; // top + case 39: return 0xff53; // right + case 40: return 0xff54; // bottom + case 42: return 0xff61; // print screen + case 45: return 0xff63; // insert + case 46: return 0xffff; // delete + case 91: return kSUPER_L; // super + case 106: return 0xffaa; // num multiply + case 107: return 0xffab; // num plus + case 109: return 0xffad; // num minus + case 110: return 0xffae; // num dot + case 111: return 0xffaf; // num divide + case 144: return 0xff7f; // num lock + case 145: return 0xff14; // scroll lock + case 151: return 0x1008ff95; // WLAN + case 166: return 0x1008ff26; // back + case 167: return 0x1008ff27; // forward + case 168: return 0x1008ff73; // refresh + case 182: return 0x1008ff51; // page flipper ("F5") + case 183: return 0x1008ff59; // fullscreen/display + case 186: return 0x3b; // ; + case 187: return 0x3d; // = + case 188: return 0x2c; // , + case 189: return 0x2d; // - + case 190: return 0x2e; // . + case 191: return 0x2f; // / + case 192: return 0x60; // ` + case 219: return 0x5b; // [ + case 220: return 0x5c; // '\' + case 221: return 0x5d; // ] + case 222: return 0x27; // ' + case 229: return 0; // dead key ('`~). FIXME: no way of knowing which } return 0x00; @@ -681,8 +681,8 @@ class CriatInstance : public pp::Instance { /* Changes the target FPS: avoid unecessary refreshes to save CPU */ void SetTargetFPS(int new_target_fps) { - /* When increasing the fps, immediately ask for a frame, and force refresh - * the display (we probably just gained focus). */ + /* When increasing the fps, immediately ask for a frame, and force + * refresh the display (we probably just gained focus). */ if (new_target_fps > target_fps_) { force_refresh_ = true; RequestScreen(request_token_); @@ -835,7 +835,7 @@ class CriatInstance : public pp::Instance { if (debug_ == 0) data[i] = 0xFF000000; else - data[i] = 0xFF800000+i; + data[i] = 0xFF800000 + i; } } @@ -856,9 +856,9 @@ class CriatInstance : public pp::Instance { /* SuperL keycode (search key) */ const uint32_t kSUPER_L = 0xffeb; - const int kFullFPS = 30; /* Maximum fps */ - const int kBlurFPS = 5; /* fps when window is possibly hidden */ - const int kHiddenFPS = 0; /* fps when window is hidden */ + const int kFullFPS = 30; /* Maximum fps */ + const int kBlurFPS = 5; /* fps when window is possibly hidden */ + const int kHiddenFPS = 0; /* fps when window is hidden */ /* Class members */ pp::CompletionCallbackFactory callback_factory_{this}; diff --git a/src/fbserver-proto.h b/src/fbserver-proto.h index 26b01db95..4f060c1e3 100644 --- a/src/fbserver-proto.h +++ b/src/fbserver-proto.h @@ -15,63 +15,69 @@ #define VERSION "VF1" #define PORT_BASE 30010 +/* Request for a frame */ struct __attribute__((__packed__)) screen { - char type; /* 'S' */ - uint8_t shm:1; /* Transfer data through shm */ - uint8_t refresh:1; /* Force a refresh, even if no damage is observed */ + char type; /* 'S' */ + uint8_t shm:1; /* Transfer data through shm */ + uint8_t refresh:1; /* Force a refresh, even if no damage is observed */ uint16_t width; uint16_t height; - uint64_t paddr; /* shm: client buffer address */ - uint64_t sig; /* shm: signature at the beginning of buffer */ + uint64_t paddr; /* shm: client buffer address */ + uint64_t sig; /* shm: signature at the beginning of buffer */ }; +/* Reply to request for a frame */ struct __attribute__((__packed__)) screen_reply { - char type; /* 'S' */ - uint8_t shm:1; /* Data was transfered through shm */ - uint8_t shmfailed:1; /* Set to 1 if shm trick has failed */ - uint8_t updated:1; /* Set to 1 if data has been updated */ - uint8_t cursor_updated:1; /* Set to 1 if cursor has been updated */ + char type; /* 'S' */ + uint8_t shm:1; /* Data was transfered through shm */ + uint8_t shmfailed:1; /* shm trick has failed */ + uint8_t updated:1; /* data has been updated (Xdamage) */ + uint8_t cursor_updated:1; /* cursor has been updated */ uint16_t width; uint16_t height; - uint32_t cursor_serial; /* Unique cursor serial number */ + uint32_t cursor_serial; /* Cursor to display */ }; -/* Ask for cursor image (if serial is unknown) */ +/* Request for cursor image (if cursor_serial is unknown) */ struct __attribute__((__packed__)) cursor { - char type; /* 'P' */ + char type; /* 'P' */ }; +/* Reply to requets for a cursor image (variable length) */ struct __attribute__((__packed__)) cursor_reply { - char type; /* 'P' */ + char type; /* 'P' */ uint16_t width, height; - uint16_t xhot, yhot; - uint32_t cursor_serial; - uint32_t pixels[0]; + uint16_t xhot, yhot; /* "Hot" coordinates */ + uint32_t cursor_serial; /* X11 unique serial number */ + uint32_t pixels[0]; /* Payload, 32-bit per pixel */ }; /* Change resolution (query + reply) */ struct __attribute__((__packed__)) resolution { - char type; /* 'R' */ + char type; /* 'R' */ uint16_t width; uint16_t height; }; +/* Press a key */ struct __attribute__((__packed__)) key { - char type; /* 'K' */ - uint8_t down:1; /* 1: down, 0: up */ - uint32_t keysym; + char type; /* 'K' */ + uint8_t down:1; /* 1: down, 0: up */ + uint32_t keysym; /* X11 KeySym */ }; +/* Move the mouse */ struct __attribute__((__packed__)) mousemove { - char type; /* 'M' */ + char type; /* 'M' */ uint16_t x; uint16_t y; }; +/* Click the mouse */ struct __attribute__((__packed__)) mouseclick { - char type; /* 'C' */ + char type; /* 'C' */ uint8_t down:1; - uint8_t button; + uint8_t button; /* X11 button number (e.g. 1 is left) */ }; -#endif /* FB_SERVER_PROTO_H_ */ +#endif /* FB_SERVER_PROTO_H_ */ diff --git a/src/fbserver.c b/src/fbserver.c index 33f416954..40fb06578 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -42,7 +42,7 @@ static int next_entry; typedef enum { MOUSE=1, KEYBOARD=2 } keybuttontype; struct keybutton { keybuttontype type; - uint32_t code; /* KeyCode or mouse button */ + uint32_t code; /* KeyCode or mouse button */ }; /* Store currently pressed keys/buttons in an array. @@ -193,8 +193,8 @@ void change_resolution(const struct resolution* rin) { "Invalid height: '%s'", cut+1); log(1, "New resolution %ld x %ld", nwidth, nheight); - char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct resolution)]; - struct resolution* r = (struct resolution*)(reply_raw+FRAMEMAXHEADERSIZE); + char reply_raw[FRAMEMAXHEADERSIZE + sizeof(struct resolution)]; + struct resolution* r = (struct resolution*)(reply_raw + FRAMEMAXHEADERSIZE); r->type = 'R'; r->width = nwidth; r->height = nheight; @@ -248,7 +248,7 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { trueorabort(c > 0, "snprintf"); int i, p = 0; for (i = 0; i < 8; i++) { - c = snprintf(arg2+p, sizeof(arg2)-p, "%02x", + c = snprintf(arg2 + p, sizeof(arg2) - p, "%02x", ((uint8_t*)&sig)[i]); trueorabort(c > 0, "snprintf"); p += c; @@ -313,9 +313,9 @@ XShmSegmentInfo shminfo; /* Writes framebuffer image to websocket/shm */ int write_image(const struct screen* screen) { - char reply_raw[FRAMEMAXHEADERSIZE+sizeof(struct screen_reply)]; + char reply_raw[FRAMEMAXHEADERSIZE + sizeof(struct screen_reply)]; struct screen_reply* reply = - (struct screen_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + (struct screen_reply*)(reply_raw + FRAMEMAXHEADERSIZE); int refresh = 0; memset(reply_raw, 0, sizeof(reply_raw)); @@ -363,13 +363,13 @@ int write_image(const struct screen* screen) { } /* Check for damage */ - while (XCheckTypedEvent(dpy, damageEvent+XDamageNotify, &ev)) { + while (XCheckTypedEvent(dpy, damageEvent + XDamageNotify, &ev)) { refresh = 1; } /* Check for cursor events */ reply->cursor_updated = 0; - while (XCheckTypedEvent(dpy, fixesEvent+XFixesCursorNotify, &ev)) { + while (XCheckTypedEvent(dpy, fixesEvent + XFixesCursorNotify, &ev)) { XFixesCursorNotifyEvent* curev = (XFixesCursorNotifyEvent*)&ev; char* name = XGetAtomName(dpy, curev->cursor_name); log(2, "cursor! %ld %s", curev->cursor_serial, name); @@ -441,10 +441,10 @@ int write_image(const struct screen* screen) { int write_cursor() { XFixesCursorImage *img = XFixesGetCursorImage(dpy); int size = img->width*img->height; - const int replylength = sizeof(struct cursor_reply)+sizeof(uint32_t)*size; - char reply_raw[FRAMEMAXHEADERSIZE+replylength]; + const int replylength = sizeof(struct cursor_reply) + size*sizeof(uint32_t); + char reply_raw[FRAMEMAXHEADERSIZE + replylength]; struct cursor_reply* reply = - (struct cursor_reply*)(reply_raw+FRAMEMAXHEADERSIZE); + (struct cursor_reply*)(reply_raw + FRAMEMAXHEADERSIZE); memset(reply_raw, 0, sizeof(*reply_raw)); @@ -503,10 +503,10 @@ int main(int argc, char** argv) { char* endptr; int displaynum = (int)strtol(display+1, &endptr, 10); trueorabort(display+1 != endptr && (*endptr == '\0' || *endptr == '.'), - "Invalid display number: '%s'", display); + "Invalid display number: '%s'", display); init_display(display); - socket_server_init(PORT_BASE+displaynum); + socket_server_init(PORT_BASE + displaynum); unsigned char buffer[BUFFERSIZE]; int length; @@ -527,23 +527,23 @@ int main(int argc, char** argv) { } switch (buffer[0]) { - case 'S': /* Screen */ + case 'S': /* Screen */ if (!check_size(length, sizeof(struct screen), "screen")) break; write_image((struct screen*)buffer); break; - case 'P': /* Cursor */ + case 'P': /* Cursor */ if (!check_size(length, sizeof(struct cursor), "cursor")) break; write_cursor(); break; - case 'R': /* Resolution */ + case 'R': /* Resolution */ if (!check_size(length, sizeof(struct resolution), "resolution")) break; change_resolution((struct resolution*)buffer); break; - case 'K': { /* Key */ + case 'K': { /* Key */ if (!check_size(length, sizeof(struct key), "key")) break; struct key* k = (struct key*)buffer; @@ -561,7 +561,7 @@ int main(int argc, char** argv) { } break; } - case 'C': { /* Click */ + case 'C': { /* Click */ if (!check_size(length, sizeof(struct mouseclick), "mouseclick")) break; @@ -574,14 +574,14 @@ int main(int argc, char** argv) { } break; } - case 'M': { /* Mouse move */ + case 'M': { /* Mouse move */ if (!check_size(length, sizeof(struct mousemove), "mousemove")) break; struct mousemove* mm = (struct mousemove*)buffer; XTestFakeMotionEvent(dpy, 0, mm->x, mm->y, CurrentTime); break; } - case 'Q': /* "Quit": release all keys */ + case 'Q': /* "Quit": release all keys */ kb_release_all(); break; default: diff --git a/src/websocket.h b/src/websocket.h index 7e894a350..f908bbcd9 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -45,11 +45,11 @@ const int WS_OPCODE_PING = 0x9; const int WS_OPCODE_PONG = 0xA; /* WebSocket bitmasks */ -const char WS_HEADER0_FIN = 0x80; /* fin */ -const char WS_HEADER0_RSV = 0x70; /* reserved */ -const char WS_HEADER0_OPCODE_MASK = 0x0F; /* reserved */ -const char WS_HEADER1_MASK = 0x80; /* mask */ -const char WS_HEADER1_LEN_MASK = 0x7F; /* payload length */ +const char WS_HEADER0_FIN = 0x80; /* fin */ +const char WS_HEADER0_RSV = 0x70; /* reserved */ +const char WS_HEADER0_OPCODE_MASK = 0x0F; /* opcode */ +const char WS_HEADER1_MASK = 0x80; /* mask */ +const char WS_HEADER1_LEN_MASK = 0x7F; /* payload length */ /* 0 - Quiet * 1 - General messages (init, new connections) @@ -103,12 +103,12 @@ static int block_read(int fd, char* buffer, size_t size) { int tot = 0; while (tot < size) { - n = read(fd, buffer+tot, size-tot); + n = read(fd, buffer + tot, size - tot); log(3, "n=%d+%d/%zd", n, tot, size); if (n < 0) return n; if (n == 0) - return -1; /* EOF */ + return -1; /* EOF */ tot += n; } @@ -122,7 +122,7 @@ static int block_write(int fd, char* buffer, size_t size) { int tot = 0; while (tot < size) { - n = write(fd, buffer+tot, size-tot); + n = write(fd, buffer + tot, size - tot); log(3, "n=%d+%d/%zd", n, tot, size); if (n < 0) return n; @@ -203,7 +203,7 @@ static int popen2(char* cmd, char *const argv[], /* We can write something to stdin */ if (fds[1].revents & POLLOUT) { if (inlen > writelen) { - int n = write(stdin_fd[1], input+writelen, inlen-writelen); + int n = write(stdin_fd[1], input + writelen, inlen - writelen); if (n < 0) { error("write error."); goto error; @@ -223,7 +223,7 @@ static int popen2(char* cmd, char *const argv[], /* We can read something from stdout */ if (fds[0].revents & POLLIN) { - int n = read(stdout_fd[0], output+readlen, outlen-readlen); + int n = read(stdout_fd[0], output + readlen, outlen - readlen); if (n < 0) { error("read error."); goto error; @@ -315,7 +315,7 @@ static void socket_client_close(int sendclose) { static int socket_client_write_frame(char* buffer, unsigned int size, unsigned int opcode, int fin) { /* Start of frame, with header: at least 2 bytes before the actual data */ - char* pbuffer = buffer+FRAMEMAXHEADERSIZE-2; + char* pbuffer = buffer + FRAMEMAXHEADERSIZE - 2; int payloadlen = size; int extlensize = 0; @@ -341,9 +341,9 @@ static int socket_client_write_frame(char* buffer, unsigned int size, pbuffer[0] = opcode & WS_HEADER0_OPCODE_MASK; if (fin) pbuffer[0] |= WS_HEADER0_FIN; - pbuffer[1] = payloadlen; /* No mask (0x80) in server->client direction */ + pbuffer[1] = payloadlen; /* No mask (0x80) in server->client direction */ - int wlen = 2+extlensize+size; + int wlen = 2 + extlensize + size; if (block_write(client_fd, pbuffer, wlen) != wlen) { syserror("Write error."); socket_client_close(0); @@ -382,7 +382,7 @@ static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, int opcode, mask; uint64_t length; *fin = (header[0] & WS_HEADER0_FIN) != 0; - if (header[0] & WS_HEADER0_RSV) { /* Reserved bits are on */ + if (header[0] & WS_HEADER0_RSV) { error("Reserved bits are on."); socket_client_close(1); return -1; @@ -467,16 +467,16 @@ static int socket_client_read_frame_header(int* fin, uint32_t* maskkey, return -1; } - if (opcode == WS_OPCODE_CLOSE) { /* Connection close. */ + if (opcode == WS_OPCODE_CLOSE) { /* Connection close. */ error("Connection close from WebSocket client."); socket_client_close(1); free(buffer); return -1; - } else if (opcode == WS_OPCODE_PING) { /* Ping */ + } else if (opcode == WS_OPCODE_PING) { /* Ping */ socket_client_write_frame(buffer, length, WS_OPCODE_PONG, 1); - } else if (opcode == WS_OPCODE_PONG) { /* Pong */ + } else if (opcode == WS_OPCODE_PONG) { /* Pong */ /* Do nothing */ - } else { /* Unknown opcode */ + } else { /* Unknown opcode */ error("Unknown packet (%x).", opcode); socket_client_close(1); free(buffer); @@ -549,7 +549,7 @@ static int socket_client_read_frame(char* buffer, int size) { return -1; } - if (socket_client_read_frame_data(buffer+buflen, len, maskkey) < 0) { + if (socket_client_read_frame_data(buffer + buflen, len, maskkey) < 0) { socket_client_close(0); return -1; } @@ -562,8 +562,8 @@ static int socket_client_read_frame(char* buffer, int size) { /* Send a version packet to the extension, and read VOK reply. */ static int socket_client_sendversion(char* version) { int versionlen = strlen(version); - char* outbuf = malloc(FRAMEMAXHEADERSIZE+versionlen); - memcpy(outbuf+FRAMEMAXHEADERSIZE, version, versionlen); + char* outbuf = malloc(FRAMEMAXHEADERSIZE + versionlen); + memcpy(outbuf + FRAMEMAXHEADERSIZE, version, versionlen); log(2, "Sending version packet (%s).", version); @@ -716,7 +716,7 @@ static int socket_server_read_header(int newclient_fd, char* websocket_key) { if (strlen(key) == 0 && !value) break; - if (first) { /* Normally GET / HTTP/1.1 */ + if (first) { /* Normally GET / HTTP/1.1 */ first = 0; char* tok = strtok(key, " "); @@ -803,7 +803,7 @@ static int socket_server_accept(char* version) { } /* key from client + GUID */ - int websocket_keylen = SECKEY_LEN+strlen(GUID); + int websocket_keylen = SECKEY_LEN + strlen(GUID); char websocket_key[websocket_keylen]; /* Read and parse HTTP header */ @@ -818,11 +818,11 @@ static int socket_server_accept(char* version) { char sha1[SHA1_LEN]; /* Some margin so we can read the full output of base64 */ - int b64_len = SHA1_BASE64_LEN+4; + int b64_len = SHA1_BASE64_LEN + 4; char b64[b64_len]; int i; - memcpy(websocket_key+SECKEY_LEN, GUID, strlen(GUID)); + memcpy(websocket_key + SECKEY_LEN, GUID, strlen(GUID)); /* SHA-1 is 20 bytes long (40 characters in hex form) */ if (popen2("sha1sum", NULL, websocket_key, websocket_keylen, @@ -832,7 +832,7 @@ static int socket_server_accept(char* version) { } /* Make sure sscanf does not read too much data */ - buffer[2*SHA1_LEN+1] = '\0'; + buffer[2*SHA1_LEN + 1] = '\0'; for (i = 0; i < SHA1_LEN; i++) { unsigned int value; if (sscanf(&buffer[i*2], "%02x", &value) != 1) { From 7189753ae0cf59f3f482dedbac0854ccbb4a5008 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 6 Nov 2014 10:17:58 -0800 Subject: [PATCH 069/334] xiat/criat: Rename to xiwi/kiwi. --- chroot-bin/croutoncycle | 26 ++++---- chroot-bin/croutonxinitrc-wrapper | 6 +- chroot-bin/setres | 4 +- chroot-etc/{xserverrc-xiat => xserverrc-xiwi} | 2 +- host-ext/.gitignore | 2 +- host-ext/crouton/background.js | 62 +++++++++--------- host-ext/crouton/{criat.nmf => kiwi.nmf} | 2 +- host-ext/crouton/window.html | 6 +- host-ext/crouton/window.js | 64 +++++++++---------- host-ext/gencrx.sh | 2 +- host-ext/nacl_src/Makefile | 8 +-- host-ext/nacl_src/{criat.cc => kiwi.cc} | 34 +++++----- installer/functions | 2 +- targets/{xiat => xiwi} | 4 +- 14 files changed, 112 insertions(+), 112 deletions(-) rename chroot-etc/{xserverrc-xiat => xserverrc-xiwi} (97%) rename host-ext/crouton/{criat.nmf => kiwi.nmf} (74%) rename host-ext/nacl_src/{criat.cc => kiwi.cc} (97%) rename targets/{xiat => xiwi} (96%) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index e2c53cd16..e2cd292ec 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -22,7 +22,7 @@ Cycles through running graphical chroots. # - 1-9: chroot displays # s: informs of a Chromium OS window change (called from extension): # - 0: any Chromium OS window -# - n: criat window: X11 display number +# - n: kiwi window: X11 display number # force [command]: Force switching display, even if it does not appear # to be necessary. @@ -73,8 +73,8 @@ fi # Ensure environment sanity export XAUTHORITY='' -# Set to y if there is any xiat instance running -xiatactive='' +# Set to y if there is any xiwi instance running +xiwiactive='' # Set to y if aura is running without a X server noaurax='' @@ -84,16 +84,16 @@ displist='' for disp in /tmp/.X*-lock; do disp="${disp#*X}" disp=":${disp%-lock}" - # Only add VT-based and xiat-based chroots here (that excludes Xephyr) + # Only add VT-based and xiwi-based chroots here (that excludes Xephyr) if [ "$disp" = ':0' ]; then continue elif DISPLAY="$disp" xprop -root 'XFree86_VT' 2>/dev/null \ | grep -q 'INTEGER'; then displist="$displist $disp" elif DISPLAY="$disp" xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ - | grep -q '= "xiat"$'; then + | grep -q '= "xiwi"$'; then displist="$displist $disp" - xiatactive='y' + xiwiactive='y' fi done @@ -118,16 +118,16 @@ fulllist="$winlist$displist" fulllist="${fulllist% }" if [ "$tty" = 'tty1' ]; then - # Either in Chromium OS, xephyr/xiat chroot, or window. + # Either in Chromium OS, xephyr/xiwi chroot, or window. # Active window is starred. for disp in $winlist; do if [ "${disp%"*"}" != "$disp" ]; then curdisp="$disp" - if [ -n "$xiatactive" -a "${disp%"*"}" = "$aurawin" -a \ + if [ -n "$xiwiactive" -a "${disp%"*"}" = "$aurawin" -a \ -s "$CRIATDISPLAY" ]; then - criatdisp="`cat $CRIATDISPLAY`" - if [ "$criatdisp" -ge 0 ]; then - curdisp=":$criatdisp" + kiwidisp="`cat $CRIATDISPLAY`" + if [ "$kiwidisp" -ge 0 ]; then + curdisp=":$kiwidisp" fi fi break @@ -277,7 +277,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then croutonwmtools raise "${destdisp%"*"}" fi - if [ -n "$xiatactive" -a "${destdisp%"*"}" = "$aurawin" ]; then + if [ -n "$xiwiactive" -a "${destdisp%"*"}" = "$aurawin" ]; then STATUS="`echo "X-1" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" @@ -285,7 +285,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then fi else export DISPLAY="$destdisp" - if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiat"$'; then + if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiwi"$'; then if [ -z "$noaurax" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 3d2b0cc7a..dcd573233 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -71,8 +71,8 @@ if [ "$xmethod" = 'xephyr' ]; then croutonwheel $CROUTON_WHEEL_PARAMS & fi -# xbindkeys and other input-related apps are not needed for criat -if [ "$xmethod" != "xiat" ]; then +# xbindkeys and other input-related apps are not needed for kiwi +if [ "$xmethod" != "xiwi" ]; then # Apply the Chromebook keyboard map if installed. if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then setxkbmap -model chromebook @@ -121,7 +121,7 @@ if [ "$xmethod" != "xiat" ]; then fi # Crouton-in-a-tab: Start fbserver and launch display -if [ "$xmethod" = 'xiat' ]; then +if [ "$xmethod" = 'xiwi' ]; then # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. setres 1024 768 > /dev/null diff --git a/chroot-bin/setres b/chroot-bin/setres index e070e552d..4f605aadc 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -4,7 +4,7 @@ # found in the LICENSE file. # Changes the resolution of the current display. -# If XMETHOD is criat, tries to create a new exact resolution, and change mode +# If XMETHOD is kiwi, tries to create a new exact resolution, and change mode # to that. If that fails (e.g. non-patched xorg-dummy), take the closest, # smaller, available resolution in xrandr, and if no smaller resolution is # available, pick the closest one. @@ -27,7 +27,7 @@ fi xmethod="`xprop -root 'CROUTON_XMETHOD' | sed -n 's/^.*\"\(.*\)\"/\1/p'`" -if [ "$xmethod" != "xiat" ]; then +if [ "$xmethod" != "xiwi" ]; then cvt "$x" "$y" "$r" | { read -r _ read -r _ mode data diff --git a/chroot-etc/xserverrc-xiat b/chroot-etc/xserverrc-xiwi similarity index 97% rename from chroot-etc/xserverrc-xiat rename to chroot-etc/xserverrc-xiwi index 84ca7012b..5c9ebd975 100644 --- a/chroot-etc/xserverrc-xiat +++ b/chroot-etc/xserverrc-xiwi @@ -11,7 +11,7 @@ for arg in "$@"; do fi done -XMETHOD='xiat' +XMETHOD='xiwi' XARGS="-nolisten tcp -config xorg-dummy.conf -logfile $logfile" if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/host-ext/.gitignore b/host-ext/.gitignore index 952926fed..ba608417e 100644 --- a/host-ext/.gitignore +++ b/host-ext/.gitignore @@ -1,4 +1,4 @@ crouton.crx crouton.zip crouton.pem -crouton/criat.pexe +crouton/kiwi.pexe diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 1c7056c98..d309b3c84 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -25,7 +25,7 @@ var websocket_ = null; /* Active connection */ /* State variables */ var debug_ = false; -var hidpi_ = false; /* true if criat windows should be opened in HiDPI mode */ +var hidpi_ = false; /* true if kiwi windows should be opened in HiDPI mode */ var enabled_ = true; /* true if we are trying to connect */ var active_ = false; /* true if we are connected to a server */ var error_ = false; /* true if there was an error during the last connection */ @@ -40,9 +40,9 @@ var sversion_ = 0; /* Version of the websocket server */ var logger_ = []; /* Array of status messages: [LogLevel, time, message] */ var windows_ = []; /* Array of windows. (.display, .name) */ -var criat_win_ = {}; /* Map of criat windows. Key is display, value is object +var kiwi_win_ = {}; /* Map of kiwi windows. Key is display, value is object (.id, .window: window element) */ -var focus_win_ = -1; /* Focused criat window. -1 if no criat window focused. */ +var focus_win_ = -1; /* Focused kiwi window. -1 if no kiwi window focused. */ /* Set the current status string. * active is a boolean, true if the WebSocket connection is established. */ @@ -104,10 +104,10 @@ function updateWindowList(force) { } } -/* Called from criat (window.js), so we can directly access each window */ -function registerCriat(display, window) { - if (criat_win_[display] && criat_win_[display].id >= 0) { - criat_win_[display].window = window; +/* Called from kiwi (window.js), so we can directly access each window */ +function registerKiwi(display, window) { + if (kiwi_win_[display] && kiwi_win_[display].id >= 0) { + kiwi_win_[display].window = window; } } @@ -175,10 +175,10 @@ function refreshUI() { debugcheck.onclick = function() { debug_ = debugcheck.checked; refreshUI(); - var disps = Object.keys(criat_win_); + var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].window) - criat_win_[disps[i]].window.setDebug(debug_?1:0); + if (kiwi_win_[disps[i]].window) + kiwi_win_[disps[i]].window.setDebug(debug_?1:0); } } debugcheck.checked = debug_; @@ -189,10 +189,10 @@ function refreshUI() { hidpicheck.onclick = function() { hidpi_ = hidpicheck.checked; refreshUI(); - var disps = Object.keys(criat_win_); + var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].window) - criat_win_[disps[i]].window.setHiDPI(hidpi_?1:0); + if (kiwi_win_[disps[i]].window) + kiwi_win_[disps[i]].window.setHiDPI(hidpi_?1:0); } } hidpicheck.disabled = false; @@ -416,8 +416,8 @@ function websocketMessage(evt) { ) windows_.forEach(function(k) { - if (criat_win_[k.display] && criat_win_[k.display].window) { - criat_win_[k.display].window.setTitle(k.name); + if (kiwi_win_[k.display] && kiwi_win_[k.display].window) { + kiwi_win_[k.display].window.setTitle(k.name); } }) } @@ -427,20 +427,20 @@ function websocketMessage(evt) { display = parseInt(payload); if (display <= 0) { /* Get out of full screen, for the current window */ - var disps = Object.keys(criat_win_); + var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - var winid = criat_win_[disps[i]].id; + var winid = kiwi_win_[disps[i]].id; chrome.windows.update(winid, {focused: false}); chrome.windows.get(winid, function(win) { chrome.windows.update(winid, {'state': 'minimized'}, function(win) {}); }) } - } else if (criat_win_[display] && criat_win_[display].id >= 0 && - (!criat_win_[display].window || - !criat_win_[display].window.closing)) { + } else if (kiwi_win_[display] && kiwi_win_[display].id >= 0 && + (!kiwi_win_[display].window || + !kiwi_win_[display].window.closing)) { /* focus/full screen an existing window */ - var winid = criat_win_[display].id; + var winid = kiwi_win_[display].id; chrome.windows.update(winid, {focused: true}); chrome.windows.get(winid, function(win) { if (win.state == "maximized") @@ -449,9 +449,9 @@ function websocketMessage(evt) { }) } else { /* Open a new window */ - criat_win_[display] = new Object(); - criat_win_[display].id = -1; - criat_win_[display].window = null; + kiwi_win_[display] = new Object(); + kiwi_win_[display].id = -1; + kiwi_win_[display].window = null; win = windows_.filter(function(x){ return x.display == display })[0] name = win ? win.name : "crouton in a tab"; @@ -462,7 +462,7 @@ function websocketMessage(evt) { "&title=" + encodeURIComponent(name), 'type': "popup" }, function(newwin) { - criat_win_[display].id = newwin.id; + kiwi_win_[display].id = newwin.id; focus_win_ = display; if (active_ && sversion_ >= 2) websocket_.send("Cs" + focus_win_); @@ -513,10 +513,10 @@ function websocketClose() { /* Called when window in focus changes: feeback to the extension so the * clipboard can be transfered. */ function windowFocusChanged(windowid) { - var disps = Object.keys(criat_win_); + var disps = Object.keys(kiwi_win_); nextfocus_win_ = -1; for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].id == windowid) { + if (kiwi_win_[disps[i]].id == windowid) { nextfocus_win_ = disps[i]; break; } @@ -531,11 +531,11 @@ function windowFocusChanged(windowid) { /* Called when a window is removed, so we can delete its reference. */ function windowRemoved(windowid) { - var disps = Object.keys(criat_win_); + var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (criat_win_[disps[i]].id == windowid) { - criat_win_[disps[i]].id = -1; - criat_win_[disps[i]].window = null; + if (kiwi_win_[disps[i]].id == windowid) { + kiwi_win_[disps[i]].id = -1; + kiwi_win_[disps[i]].window = null; printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); } } diff --git a/host-ext/crouton/criat.nmf b/host-ext/crouton/kiwi.nmf similarity index 74% rename from host-ext/crouton/criat.nmf rename to host-ext/crouton/kiwi.nmf index 37051eb42..eb12b933e 100644 --- a/host-ext/crouton/criat.nmf +++ b/host-ext/crouton/kiwi.nmf @@ -2,7 +2,7 @@ "program": { "portable": { "pnacl-translate": { - "url": "criat.pexe" + "url": "kiwi.pexe" } } } diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 963e5b61c..3174702ed 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -46,7 +46,7 @@ width: 100%; height: 100%; } - #criat { + #kiwi { margin-left: 0px; margin-top: 0px; } @@ -59,9 +59,9 @@
Initializing...
-
diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index d507e43b4..d92e15415 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -6,7 +6,7 @@ var CLOSE_TIMEOUT = 2; /* Close window x seconds after disconnect */ var DEBUG_LEVEL = 2; /* If debug is enabled, use this level in NaCl */ var RESIZE_RATE_LIMIT = 300; /* No more than 1 resize query every x ms */ -var CriatModule_ = null; /* NaCl module */ +var KiwiModule_ = null; /* NaCl module */ var listener_ = null; /* listener div element */ var debug_ = 0; /* Debuging level, passed to NaCl module */ var hidpi_ = 0; /* HiDPI mode */ @@ -21,18 +21,18 @@ var resizeLimited_ = false; function registerWindow(register) { chrome.extension.getBackgroundPage(). - registerCriat(display_, register ? window : null); + registerKiwi(display_, register ? window : null); } /* NaCl module loaded */ function moduleDidLoad() { - CriatModule_ = document.getElementById('criat'); + KiwiModule_ = document.getElementById('kiwi'); updateStatus('Starting...'); - criatResize(); - CriatModule_.postMessage('display:' + display_); - CriatModule_.postMessage('debug:' + debug_); - CriatModule_.postMessage('hidpi:' + hidpi_); - CriatModule_.focus(); + kiwiResize(); + KiwiModule_.postMessage('display:' + display_); + KiwiModule_.postMessage('debug:' + debug_); + KiwiModule_.postMessage('hidpi:' + hidpi_); + KiwiModule_.focus(); } /* NaCl is loading... */ @@ -46,17 +46,17 @@ function handleProgress(event) { function handleError(event) { // We can't use common.naclModule yet because the module has not been // loaded. - CriatModule_ = document.getElementById('criat'); - updateStatus('ERROR: ' + CriatModule_.lastError); + KiwiModule_ = document.getElementById('kiwi'); + updateStatus('ERROR: ' + KiwiModule_.lastError); registerWindow(false); } /* NaCl module crashed */ function handleCrash(event) { - if (CriatModule_.exitStatus == -1) { + if (KiwiModule_.exitStatus == -1) { updateStatus('NaCl module crashed.'); } else { - updateStatus('NaCl module exited: ' + CriatModule_.exitStatus); + updateStatus('NaCl module exited: ' + KiwiModule_.exitStatus); } registerWindow(false); } @@ -71,18 +71,18 @@ function setDebug(debug) { document.getElementById('content').style.paddingTop = "0px"; document.getElementById('header').style.display = 'none'; } - if (CriatModule_) { - CriatModule_.postMessage('debug:' + debug_); - criatResize(); + if (KiwiModule_) { + KiwiModule_.postMessage('debug:' + debug_); + kiwiResize(); } } /* Change HiDPI mode */ function setHiDPI(hidpi) { hidpi_ = hidpi; - if (CriatModule_) { - CriatModule_.postMessage('hidpi:' + hidpi_); - criatResize(); + if (KiwiModule_) { + KiwiModule_.postMessage('hidpi:' + hidpi_); + kiwiResize(); } } @@ -165,28 +165,28 @@ function handleMessage(message) { var lheight = listener_.clientHeight; var marginleft = (lwidth-width)/2; var margintop = (lheight-height)/2; - CriatModule_.style.marginLeft = Math.max(marginleft, 0) + "px"; - CriatModule_.style.marginTop = Math.max(margintop, 0) + "px"; - CriatModule_.width = width; - CriatModule_.height = height; + KiwiModule_.style.marginLeft = Math.max(marginleft, 0) + "px"; + KiwiModule_.style.marginTop = Math.max(margintop, 0) + "px"; + KiwiModule_.width = width; + KiwiModule_.height = height; } } /* Tell the module that the window was resized (this triggers a change of * resolution, followed by a resize message. */ -function criatResize() { +function kiwiResize() { console.log("resize! " + listener_.clientWidth + "/" + listener_.clientHeight); - if (CriatModule_) - CriatModule_.postMessage('resize:' + listener_.clientWidth + "/" + listener_.clientHeight); + if (KiwiModule_) + KiwiModule_.postMessage('resize:' + listener_.clientWidth + "/" + listener_.clientHeight); } /* Window was resize, limit to one event per second */ function handleResize() { if (!resizeLimited_) { - criatResize(); + kiwiResize(); setTimeout(function() { if (resizePending_) - criatResize(); + kiwiResize(); resizeLimited_ = resizePending_ = false; }, RESIZE_RATE_LIMIT); resizeLimited_ = true; @@ -202,22 +202,22 @@ function handleFocusBlur(evt) { * See http://crbug.com/403061 */ console.log("focus/blur: " + evt.type + ", focus=" + document.hasFocus() + ", hidden=" + document.hidden + "/" + document.visibilityState); - if (!CriatModule_) + if (!KiwiModule_) return; if (document.hasFocus()) { - CriatModule_.postMessage("focus:"); + KiwiModule_.postMessage("focus:"); } else { if (closing_) window.close(); if (!document.hidden) - CriatModule_.postMessage("blur:"); + KiwiModule_.postMessage("blur:"); else - CriatModule_.postMessage("hide:"); + KiwiModule_.postMessage("hide:"); } console.log("active: " + document.activeElement); - CriatModule_.focus(); + KiwiModule_.focus(); } /* Start in full screen */ diff --git a/host-ext/gencrx.sh b/host-ext/gencrx.sh index 44a7e0e26..1d442deb7 100755 --- a/host-ext/gencrx.sh +++ b/host-ext/gencrx.sh @@ -16,7 +16,7 @@ set -e EXTNAME="crouton" -CRIAT_PEXE="$EXTNAME/criat.pexe" +CRIAT_PEXE="$EXTNAME/kiwi.pexe" cd "`dirname "$0"`" diff --git a/host-ext/nacl_src/Makefile b/host-ext/nacl_src/Makefile index 87364f5b3..1aea49c33 100644 --- a/host-ext/nacl_src/Makefile +++ b/host-ext/nacl_src/Makefile @@ -9,17 +9,17 @@ VALID_TOOLCHAINS := pnacl NACL_SDK_ROOT ?= $(HOME)/nacl_sdk/pepper_37 -../crouton/criat.pexe: pnacl/Release/criat.pexe - cp pnacl/Release/criat.pexe ../crouton/criat.pexe +../crouton/kiwi.pexe: pnacl/Release/kiwi.pexe + cp pnacl/Release/kiwi.pexe ../crouton/kiwi.pexe include $(NACL_SDK_ROOT)/tools/common.mk -TARGET = criat +TARGET = kiwi LIBS = ppapi_cpp ppapi CFLAGS = -Wall -std=gnu++11 -SOURCES = criat.cc +SOURCES = kiwi.cc # Build rules generated by macros from common.mk: $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) diff --git a/host-ext/nacl_src/criat.cc b/host-ext/nacl_src/kiwi.cc similarity index 97% rename from host-ext/nacl_src/criat.cc rename to host-ext/nacl_src/kiwi.cc index df975edc5..3bfcec1ab 100644 --- a/host-ext/nacl_src/criat.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -29,11 +29,11 @@ /* Protocol data structures */ #include "../../src/fbserver-proto.h" -class CriatInstance : public pp::Instance { +class KiwiInstance : public pp::Instance { public: - explicit CriatInstance(PP_Instance instance): pp::Instance(instance) {} + explicit KiwiInstance(PP_Instance instance): pp::Instance(instance) {} - virtual ~CriatInstance() {} + virtual ~KiwiInstance() {} /* Registers events */ virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { @@ -134,7 +134,7 @@ class CriatInstance : public pp::Instance { url << "ws://localhost:" << (PORT_BASE + display_) << "/"; websocket_.Connect(pp::Var(url.str()), NULL, 0, callback_factory_.NewCallback( - &CriatInstance::OnSocketConnectCompletion)); + &KiwiInstance::OnSocketConnectCompletion)); StatusMessage("Connecting..."); } @@ -145,7 +145,7 @@ class CriatInstance : public pp::Instance { status << "Connection failed (" << result << "), retrying..."; StatusMessage(status.str()); pp::Module::Get()->core()->CallOnMainThread(1000, - callback_factory_.NewCallback(&CriatInstance::SocketConnect)); + callback_factory_.NewCallback(&KiwiInstance::SocketConnect)); return; } @@ -159,7 +159,7 @@ class CriatInstance : public pp::Instance { /* Closes the WebSocket connection. */ void SocketClose(std::string reason) { websocket_.Close(0, pp::Var(reason), - callback_factory_.NewCallback(&CriatInstance::OnSocketClosed)); + callback_factory_.NewCallback(&KiwiInstance::OnSocketClosed)); } /* Called when WebSocket is closed */ @@ -228,7 +228,7 @@ class CriatInstance : public pp::Instance { pp::Module::Get()->core()->CallOnMainThread( 1000/target_fps_, callback_factory_.NewCallback( - &CriatInstance::RequestScreen), + &KiwiInstance::RequestScreen), request_token_); } } @@ -331,7 +331,7 @@ class CriatInstance : public pp::Instance { /* Get ready to receive next frame */ pp::Module::Get()->core()->CallOnMainThread(0, - callback_factory_.NewCallback(&CriatInstance::SocketReceive)); + callback_factory_.NewCallback(&KiwiInstance::SocketReceive)); /* Convert binary/text to char* */ const char* data; @@ -387,7 +387,7 @@ class CriatInstance : public pp::Instance { * Parameter is ignored: used for callbacks */ void SocketReceive(int32_t /*result*/ = 0) { websocket_.ReceiveMessage(&receive_var_, callback_factory_.NewCallback( - &CriatInstance::OnSocketReceiveCompletion)); + &KiwiInstance::OnSocketReceiveCompletion)); } /* Sends a WebSocket request, possibly flushing current mouse position @@ -811,7 +811,7 @@ class CriatInstance : public pp::Instance { } else if (delay >= 0) { pp::Module::Get()->core()->CallOnMainThread( delay*1000, - callback_factory_.NewCallback(&CriatInstance::RequestScreen), + callback_factory_.NewCallback(&KiwiInstance::RequestScreen), request_token_); } else { RequestScreen(request_token_); @@ -848,7 +848,7 @@ class CriatInstance : public pp::Instance { * completes. */ flush_context_ = context_; context_.Flush( - callback_factory_.NewCallback(&CriatInstance::OnFlush)); + callback_factory_.NewCallback(&KiwiInstance::OnFlush)); } private: @@ -861,7 +861,7 @@ class CriatInstance : public pp::Instance { const int kHiddenFPS = 0; /* fps when window is hidden */ /* Class members */ - pp::CompletionCallbackFactory callback_factory_{this}; + pp::CompletionCallbackFactory callback_factory_{this}; pp::Graphics2D context_; pp::Graphics2D flush_context_; pp::Rect view_rect_; @@ -906,20 +906,20 @@ class CriatInstance : public pp::Instance { bool hidpi_ = false; }; -class CriatModule : public pp::Module { +class KiwiModule : public pp::Module { public: - CriatModule() : pp::Module() {} - virtual ~CriatModule() {} + KiwiModule() : pp::Module() {} + virtual ~KiwiModule() {} virtual pp::Instance* CreateInstance(PP_Instance instance) { - return new CriatInstance(instance); + return new KiwiInstance(instance); } }; namespace pp { Module* CreateModule() { - return new CriatModule(); + return new KiwiModule(); } } /* namespace pp */ diff --git a/installer/functions b/installer/functions index cde840d55..4a885f40a 100644 --- a/installer/functions +++ b/installer/functions @@ -155,7 +155,7 @@ validate_name() { # Websocket interface PIPEDIR='/tmp/crouton-ext' -CRIATDISPLAY="$PIPEDIR/criat-display" +CRIATDISPLAY="$PIPEDIR/kiwi-display" # Write a command to croutonwebsocket, and read back response websocketcommand() { diff --git a/targets/xiat b/targets/xiwi similarity index 96% rename from targets/xiat rename to targets/xiwi index a0af908f5..4e7d7005f 100644 --- a/targets/xiat +++ b/targets/xiwi @@ -6,11 +6,11 @@ REQUIRES='core audio extension' PROVIDES='x11' DESCRIPTION='Crouton in a tab X11 backend. Improves compatibility but lacks GPU accel.' CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper setres xinit' -CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiat xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -XMETHOD="${XMETHOD:-xiat}" +XMETHOD="${XMETHOD:-xiwi}" install xorg xserver-xorg-video-dummy From 8b993cda31a2a829a2fb703c77e6d93fb93e68d8 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 17:17:24 -0800 Subject: [PATCH 070/334] kiwi: Basic touch handling. --- host-ext/nacl_src/kiwi.cc | 75 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 3bfcec1ab..6665b5241 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -533,25 +533,86 @@ class KiwiInstance : public pp::Instance { mouse_wheel_y -= 16; } } else if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART || + event.GetType() == PP_INPUTEVENT_TYPE_TOUCHMOVE || event.GetType() == PP_INPUTEVENT_TYPE_TOUCHEND) { - /* FIXME: To be implemented */ + /* FIXME: This is a very primitive implementation: + * we only handle single touch */ pp::TouchInputEvent touch_event(event); + std::ostringstream status; int count = touch_event.GetTouchCount( PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); - std::ostringstream status; - status << "TOUCH " << count; + status << "TOUCH " << count << " "; + + /* We only care about the first touch (when count goes from 0 + * to 1), and record the id in touch_id_. */ + switch (event.GetType()) { + case PP_INPUTEVENT_TYPE_TOUCHSTART: + if (touch_count_ == 0 && count == 1) { + touch_id_ = touch_event.GetTouchByIndex( + PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, 0).id(); + } + touch_count_ += count; + status << "START"; + break; + case PP_INPUTEVENT_TYPE_TOUCHMOVE: + status << "MOVE"; + break; + case PP_INPUTEVENT_TYPE_TOUCHEND: + touch_count_ -= count; + status << "END"; + break; + default: + break; + } + + /* FIXME: Is there a better way to figure out if a touch id + * is present? (GetTouchById is unhelpful and returns a TouchPoint + * full of zeros, which may well be valid...) */ + bool has_tpid = false; for (int i = 0; i < count; i++) { pp::TouchPoint tp = touch_event.GetTouchByIndex( PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); status << std::endl << tp.id() << "//" << tp.position().x() << "/" << tp.position().y() << "@" << tp.pressure(); + if (tp.id() == touch_id_) + has_tpid = true; } + LogMessage(2, status.str()); - LogMessage(0, status.str()); - } /* FIXME: Handle IMEInputEvents too */ + if (has_tpid) { + std::ostringstream status; + /* Emulate a click: only care about touch at id touch_id_ */ + pp::TouchPoint tp = touch_event.GetTouchById( + PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, touch_id_); + + pp::Point touch_event_pos( + tp.position().x() * scale_, + tp.position().y() * scale_); + bool down = event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART; + + if (mouse_pos_.x() != touch_event_pos.x() || + mouse_pos_.y() != touch_event_pos.y()) { + pending_mouse_move_ = true; + mouse_pos_ = touch_event_pos; + } + + status << "Emulated mouse "; + + if (event.GetType() != PP_INPUTEVENT_TYPE_TOUCHMOVE) { + status << (down ? "DOWN" : "UP"); + SendClick(1, down ? 1 : 0); + } else { + status << "MOVE"; + } + + status << " " << touch_event_pos.x() << "/" + << touch_event_pos.y(); + LogMessage(2, status.str()); + } + } /* FIXME: Handle IMEInputEvents too */ return PP_TRUE; } @@ -888,6 +949,10 @@ class KiwiInstance : public pp::Instance { /* Super_L press has been delayed */ bool pending_super_l_ = false; + /* Touch */ + int touch_count_; /* Number of points currently pressed */ + int touch_id_; /* First touch id */ + /* Performance metrics */ PP_Time lasttime_; double avgfps_ = 0.0; From a4840a073d6fd88886c62b5a58d8c67254d4a2f4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 17:34:27 -0800 Subject: [PATCH 071/334] kiwi: Register IME events. Implementation still missing. --- host-ext/nacl_src/kiwi.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 6665b5241..236f89d3c 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -39,7 +39,8 @@ class KiwiInstance : public pp::Instance { virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL | - PP_INPUTEVENT_CLASS_TOUCH); + PP_INPUTEVENT_CLASS_TOUCH | + PP_INPUTEVENT_CLASS_IME); RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); srand(pp::Module::Get()->core()->GetTime()); @@ -612,7 +613,17 @@ class KiwiInstance : public pp::Instance { << touch_event_pos.y(); LogMessage(2, status.str()); } - } /* FIXME: Handle IMEInputEvents too */ + } else if (event.GetType() == PP_INPUTEVENT_TYPE_IME_TEXT) { + /* FIXME: There are other IME event types... */ + pp::IMEInputEvent ime_event(event); + + /* FIXME: Do something with these events. We probably need to "type" + * the letters one by one... */ + + std::ostringstream status; + status << "IME TEXT: " << ime_event.GetText().AsString(); + LogMessage(0, status.str()); + } return PP_TRUE; } From 90b656b694444c3a61f8b677c043e8b46c3bd6d3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 6 Nov 2014 14:44:22 -0800 Subject: [PATCH 072/334] xiwi: Fix description, comment in setres. --- chroot-bin/setres | 2 +- targets/xiwi | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chroot-bin/setres b/chroot-bin/setres index 4f605aadc..2ac0b04ba 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -4,7 +4,7 @@ # found in the LICENSE file. # Changes the resolution of the current display. -# If XMETHOD is kiwi, tries to create a new exact resolution, and change mode +# If XMETHOD is xiwi, tries to create a new exact resolution, and change mode # to that. If that fails (e.g. non-patched xorg-dummy), take the closest, # smaller, available resolution in xrandr, and if no smaller resolution is # available, pick the closest one. diff --git a/targets/xiwi b/targets/xiwi index 4e7d7005f..9bfec2a70 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -4,7 +4,7 @@ # found in the LICENSE file. REQUIRES='core audio extension' PROVIDES='x11' -DESCRIPTION='Crouton in a tab X11 backend. Improves compatibility but lacks GPU accel.' +DESCRIPTION='X11 backend running unaccelerated in a Chromium OS window' CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" From 3691fba7204bc8989517b79829b233e332b625b4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 5 Nov 2014 19:02:53 -0800 Subject: [PATCH 073/334] kiwi: Fancy C++ logger class Log message appears as stringstreams, and are automatically flushed when the Message goes out of scope. --- host-ext/nacl_src/kiwi.cc | 246 +++++++++++++++++++------------------- 1 file changed, 124 insertions(+), 122 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 236f89d3c..765dd2500 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -58,7 +58,7 @@ class KiwiInstance : public pp::Instance { std::string message = var_message.AsString(); - LogMessage(2, "message=" + message); + LogMessage(2) << "message=" << message; size_t pos = message.find(':'); if (pos != std::string::npos) { @@ -97,28 +97,66 @@ class KiwiInstance : public pp::Instance { } private: + /* Message class that allows C++-style logging/status messages. + * The message is flushed when the object gets out of scope. */ + class Message { + public: + Message(KiwiInstance& ki, std::string type, bool dummy): ki_(ki) { + if (!dummy) { + out_.reset(new std::ostringstream()); + *out_ << type << ":"; + } + } + + virtual ~Message() { + if (out_) ki_.PostMessage(out_->str()); + } + + template Message& operator<<(T&& val) { + if (out_) *out_ << val; + return *this; + } + + Message(Message&& other) = default; /* Steals the unique_ptr */ + + /* The next 2 functions cannot be implemented correctly, make sure we + * cannot call them */ + Message(const Message& other) = delete; + Message& operator =(const Message&) = delete; + + private: + std::unique_ptr out_; + KiwiInstance& ki_; + }; + /* Sends a status message to Javascript */ - void StatusMessage(std::string str) { - ControlMessage("status", str); + Message StatusMessage() { + return Message(*this, "status", false); } /* Sends a logging message to Javascript */ - void LogMessage(int level, std::string str) { + Message LogMessage(int level) { if (level <= debug_) { std::ostringstream status; double delta = 1000 * (pp::Module::Get()->core()->GetTime() - lasttime_); - status << "(" << level << ") " << (int)delta << " " << str; - ControlMessage("log", status.str()); + Message m(*this, "debug", false); + m << "(" << level << ") " << (int)delta << " "; + return m; + } else { + return Message(*this, "debug", true); } } + /* Sends a resize message to Javascript */ + void ResizeMessage(int width, int height) { + Message(*this, "resize", false) << width << "/" << height; + } + /* Sends a control message to Javascript * Format: : */ void ControlMessage(std::string type, std::string str) { - std::ostringstream status; - status << type << ":" << str; - PostMessage(status.str()); + Message(*this, type, false) << str; } /** WebSocket interface **/ @@ -127,7 +165,7 @@ class KiwiInstance : public pp::Instance { * Parameter is ignored: used for callbacks */ void SocketConnect(int32_t /*result*/ = 0) { if (display_ < 0) { - LogMessage(-1, "SocketConnect: No display defined yet."); + LogMessage(-1) << "SocketConnect: No display defined yet."; return; } @@ -136,15 +174,14 @@ class KiwiInstance : public pp::Instance { websocket_.Connect(pp::Var(url.str()), NULL, 0, callback_factory_.NewCallback( &KiwiInstance::OnSocketConnectCompletion)); - StatusMessage("Connecting..."); + StatusMessage() << "Connecting..."; } /* Called when WebSocket is connected (or failed to connect) */ void OnSocketConnectCompletion(int32_t result) { if (result != PP_OK) { - std::ostringstream status; - status << "Connection failed (" << result << "), retrying..."; - StatusMessage(status.str()); + StatusMessage() << "Connection failed (" + << result << "), retrying..."; pp::Module::Get()->core()->CallOnMainThread(1000, callback_factory_.NewCallback(&KiwiInstance::SocketConnect)); return; @@ -154,7 +191,7 @@ class KiwiInstance : public pp::Instance { SocketReceive(); - StatusMessage("Connected."); + StatusMessage() << "Connected."; } /* Closes the WebSocket connection. */ @@ -165,7 +202,7 @@ class KiwiInstance : public pp::Instance { /* Called when WebSocket is closed */ void OnSocketClosed(int32_t result) { - StatusMessage("Disconnected..."); + StatusMessage() << "Disconnected..."; ControlMessage("disconnected", "Socket closed"); connected_ = false; screen_flying_ = false; @@ -181,22 +218,19 @@ class KiwiInstance : public pp::Instance { if (length == target) return true; - std::stringstream status; - status << "Invalid " << type << " request (" << length - << " != " << target << ")."; - LogMessage(-1, status.str()); + LogMessage(-1) << "Invalid " << type << " request (" << length + << " != " << target << ")."; return false; } /* Receives and handles a version request */ bool SocketParseVersion(const char* data, int datalen) { if (connected_) { - LogMessage(-1, "Got a version while connected?!?"); + LogMessage(-1) << "Got a version while connected?!?"; return false; } if (strcmp(data, VERSION)) { - LogMessage(-1, "Invalid version received (" + - std::string(data) + ")."); + LogMessage(-1) << "Invalid version received (" << data << ")."; return false; } connected_ = true; @@ -242,9 +276,8 @@ class KiwiInstance : public pp::Instance { /* No cache entry, ask for data. */ SocketSend(pp::Var("P"), false); } else { - std::ostringstream status; - status << "Cursor use cache for " << (reply->cursor_serial); - LogMessage(2, status.str()); + LogMessage(2) << "Cursor use cache for " + << reply->cursor_serial; pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, it->second.img, it->second.hot); } @@ -255,10 +288,8 @@ class KiwiInstance : public pp::Instance { /* Receives and handles a cursor_reply request */ bool SocketParseCursor(const char* data, int datalen) { if (datalen < sizeof(struct cursor_reply)) { - std::stringstream status; - status << "Invalid cursor_reply packet (" << datalen - << " < " << sizeof(struct cursor_reply) << ")."; - LogMessage(-1, status.str()); + LogMessage(-1) << "Invalid cursor_reply packet (" << datalen + << " < " << sizeof(struct cursor_reply) << ")."; return false; } @@ -269,11 +300,10 @@ class KiwiInstance : public pp::Instance { "cursor_reply")) return false; - std::ostringstream status; - status << "Cursor " << (cursor->width) << "/" << (cursor->height); - status << " " << (cursor->xhot) << "/" << (cursor->yhot); - status << " " << (cursor->cursor_serial); - LogMessage(0, status.str()); + LogMessage(0) << "Cursor " + << (cursor->width) << "/" << (cursor->height) + << " " << (cursor->xhot) << "/" << (cursor->yhot) + << " " << (cursor->cursor_serial); /* Scale down if needed */ int scale = 1; @@ -305,27 +335,23 @@ class KiwiInstance : public pp::Instance { if (!CheckSize(datalen, sizeof(struct resolution), "resolution")) return false; struct resolution* r = (struct resolution*)data; - std::ostringstream newres; - newres << (r->width/scale_) << "/" << (r->height/scale_); /* Tell Javascript so that it can center us on the page */ - ControlMessage("resize", newres.str()); + ResizeMessage(r->width/scale_, r->height/scale_); force_refresh_ = true; return true; } /* Called when a frame is received from WebSocket server */ void OnSocketReceiveCompletion(int32_t result) { - std::stringstream status; - status << "ReadCompletion: " << result << "."; - LogMessage(5, status.str()); + LogMessage(5) << "ReadCompletion: " << result << "."; if (result == PP_ERROR_INPROGRESS) { - LogMessage(0, "Receive error INPROGRESS (should not happen)."); + LogMessage(0) << "Receive error INPROGRESS (should not happen)."; /* We called SocketReceive too many times. */ /* Not fatal: just wait for next call */ return; } else if (result != PP_OK) { - LogMessage(-1, "Receive error."); + LogMessage(-1) << "Receive error."; SocketClose("Receive error."); return; } @@ -342,12 +368,11 @@ class KiwiInstance : public pp::Instance { pp::VarArrayBuffer array_buffer(receive_var_); data = static_cast(array_buffer.Map()); datalen = array_buffer.ByteLength(); - std::stringstream status; - status << "receive (binary): " << data[0]; - LogMessage(data[0] == 'S' ? 3 : 2, status.str()); + LogMessage(data[0] == 'S' ? 3 : 2) << "receive (binary): " + << data[0]; } else { str = receive_var_.AsString(); - LogMessage(3, "receive (text): " + str); + LogMessage(3) << "receive (text): " << str; data = str.c_str(); datalen = str.length(); } @@ -370,15 +395,13 @@ class KiwiInstance : public pp::Instance { case 'R': /* Resolution request reply */ if (SocketParseResolution(data, datalen)) return; break; - default: { - std::stringstream status; - status << "Invalid request. First char: " << (int)data[0]; - LogMessage(-1, status.str()); + default: + LogMessage(-1) << "Invalid request. First char: " + << (int)data[0]; /* Fall-through: disconnect. */ } - } } else { - LogMessage(-1, "Got some packet before version..."); + LogMessage(-1) << "Got some packet before version..."; } SocketClose("Invalid payload."); @@ -395,7 +418,7 @@ class KiwiInstance : public pp::Instance { * first */ void SocketSend(const pp::Var& var, bool flushmouse) { if (!connected_) { - LogMessage(-1, "SocketSend: not connected!"); + LogMessage(-1) << "SocketSend: not connected!"; return; } @@ -434,20 +457,17 @@ class KiwiInstance : public pp::Instance { uint32_t keysym = KeyCodeToKeySym(keycode, keystr); bool down = event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN; - std::ostringstream status; - status << "Key " << (down ? "DOWN" : "UP"); - status << ": C:" << keystr; - status << "/KC:" << std::hex << keycode; - status << "/KS:" << std::hex << keysym; + LogMessage(keysym == 0 ? 0 : 1) + << "Key " << (down ? "DOWN" : "UP") + << ": C:" << keystr + << "/KC:" << std::hex << keycode + << "/KS:" << std::hex << keysym + << (keysym == 0 ? " (KEY UNKNOWN!)" : ""); if (keysym == 0) { - status << " (KEY UNKNOWN!)"; - LogMessage(0, status.str()); return PP_TRUE; } - LogMessage(1, status.str()); - if (keycode == 183) { /* Fullscreen => toggle fullscreen */ if (!down) ControlMessage("state", "fullscreen"); @@ -485,13 +505,13 @@ class KiwiInstance : public pp::Instance { mouse_pos_ = mouse_event_pos; } - std::ostringstream status; - status << "Mouse " << mouse_event_pos.x() << "x" - << mouse_event_pos.y(); + Message m = LogMessage(3); + m << "Mouse " << mouse_event_pos.x() << "x" + << mouse_event_pos.y(); if (event.GetType() != PP_INPUTEVENT_TYPE_MOUSEMOVE) { - status << " " << (down ? "DOWN" : "UP"); - status << " " << (mouse_event.GetButton()); + m << " " << (down ? "DOWN" : "UP") + << " " << (mouse_event.GetButton()); /* SendClick calls SocketSend, which flushes the mouse position * before sending the click event. @@ -499,22 +519,18 @@ class KiwiInstance : public pp::Instance { * X11 numbers are 1-based (left=1). */ SendClick(mouse_event.GetButton() + 1, down ? 1 : 0); } - - LogMessage(3, status.str()); } else if (event.GetType() == PP_INPUTEVENT_TYPE_WHEEL) { pp::WheelInputEvent wheel_event(event); mouse_wheel_x += wheel_event.GetDelta().x(); mouse_wheel_y += wheel_event.GetDelta().y(); - std::ostringstream status; - status << "MWd " << wheel_event.GetDelta().x() << "x" - << wheel_event.GetDelta().y(); - status << "MWt " << wheel_event.GetTicks().x() << "x" - << wheel_event.GetTicks().y(); - status << "acc " << mouse_wheel_x << "x" - << mouse_wheel_y; - LogMessage(2, status.str()); + LogMessage(2) << "MWd " << wheel_event.GetDelta().x() << "x" + << wheel_event.GetDelta().y() + << "MWt " << wheel_event.GetTicks().x() << "x" + << wheel_event.GetTicks().y() + << "acc " << mouse_wheel_x << "x" + << mouse_wheel_y; while (mouse_wheel_x <= -16) { SendClick(6, 1); SendClick(6, 0); @@ -541,10 +557,11 @@ class KiwiInstance : public pp::Instance { pp::TouchInputEvent touch_event(event); - std::ostringstream status; int count = touch_event.GetTouchCount( PP_TOUCHLIST_TYPE_CHANGEDTOUCHES); - status << "TOUCH " << count << " "; + + Message m = LogMessage(2); + m << "TOUCH " << count << " "; /* We only care about the first touch (when count goes from 0 * to 1), and record the id in touch_id_. */ @@ -555,14 +572,14 @@ class KiwiInstance : public pp::Instance { PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, 0).id(); } touch_count_ += count; - status << "START"; + m << "START"; break; case PP_INPUTEVENT_TYPE_TOUCHMOVE: - status << "MOVE"; + m << "MOVE"; break; case PP_INPUTEVENT_TYPE_TOUCHEND: touch_count_ -= count; - status << "END"; + m << "END"; break; default: break; @@ -575,16 +592,14 @@ class KiwiInstance : public pp::Instance { for (int i = 0; i < count; i++) { pp::TouchPoint tp = touch_event.GetTouchByIndex( PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, i); - status << std::endl << tp.id() << "//" - << tp.position().x() << "/" << tp.position().y() - << "@" << tp.pressure(); + m << "\n " << tp.id() << "//" + << tp.position().x() << "/" << tp.position().y() + << "@" << tp.pressure(); if (tp.id() == touch_id_) has_tpid = true; } - LogMessage(2, status.str()); if (has_tpid) { - std::ostringstream status; /* Emulate a click: only care about touch at id touch_id_ */ pp::TouchPoint tp = touch_event.GetTouchById( PP_TOUCHLIST_TYPE_CHANGEDTOUCHES, touch_id_); @@ -600,18 +615,16 @@ class KiwiInstance : public pp::Instance { mouse_pos_ = touch_event_pos; } - status << "Emulated mouse "; + m << "\nEmulated mouse "; if (event.GetType() != PP_INPUTEVENT_TYPE_TOUCHMOVE) { - status << (down ? "DOWN" : "UP"); + m << (down ? "DOWN" : "UP"); SendClick(1, down ? 1 : 0); } else { - status << "MOVE"; + m << "MOVE"; } - status << " " << touch_event_pos.x() << "/" - << touch_event_pos.y(); - LogMessage(2, status.str()); + m << " " << touch_event_pos.x() << "/" << touch_event_pos.y(); } } else if (event.GetType() == PP_INPUTEVENT_TYPE_IME_TEXT) { /* FIXME: There are other IME event types... */ @@ -620,9 +633,7 @@ class KiwiInstance : public pp::Instance { /* FIXME: Do something with these events. We probably need to "type" * the letters one by one... */ - std::ostringstream status; - status << "IME TEXT: " << ime_event.GetText().AsString(); - LogMessage(0, status.str()); + LogMessage(0) << "IME TEXT: " << ime_event.GetText().AsString(); } return PP_TRUE; @@ -638,16 +649,15 @@ class KiwiInstance : public pp::Instance { pp::Size new_size = pp::Size(view_rect_.width() * scale_, view_rect_.height() * scale_); - std::ostringstream status; - status << "InitContext " << new_size.width() << "x" << new_size.height() - << "s" << scale_; - LogMessage(0, status.str()); + LogMessage(0) << "InitContext " + << new_size.width() << "x" << new_size.height() + << "s" << scale_; const bool kIsAlwaysOpaque = true; context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque); context_.SetScale(1.0f / scale_); if (!BindGraphics(context_)) { - LogMessage(0, "Unable to bind 2d context!"); + LogMessage(0) << "Unable to bind 2d context!"; context_ = pp::Graphics2D(); return; } @@ -658,9 +668,7 @@ class KiwiInstance : public pp::Instance { /* Requests the server for a resolution change. */ void ChangeResolution(int width, int height) { - std::ostringstream status; - status << "Asked for resolution " << width << "x" << height; - LogMessage(1, status.str()); + LogMessage(1) << "Asked for resolution " << width << "x" << height; if (connected_) { struct resolution* r; @@ -672,9 +680,7 @@ class KiwiInstance : public pp::Instance { array_buffer.Unmap(); SocketSend(array_buffer, false); } else { /* Just assume we can take up the space */ - std::ostringstream status; - status << width/scale_ << "/" << height/scale_; - ControlMessage("resize", status.str()); + ResizeMessage(width/scale_, height/scale_); } } @@ -805,19 +811,17 @@ class KiwiInstance : public pp::Instance { * (e.g. when changing frame rate), since we have no way of cancelling * scheduled callbacks. */ void RequestScreen(int32_t token) { - std::stringstream status; - status << "OnWaitEnd " << token << "/" << request_token_; - LogMessage(3, status.str()); + LogMessage(3) << "OnWaitEnd " << token << "/" << request_token_; if (!connected_) { - LogMessage(-1, "!connected"); + LogMessage(-1) << "!connected"; return; } /* Check that this request is up to date, and that no other * request is flying */ if (token != request_token_ || screen_flying_) { - LogMessage(2, "Old token, or screen flying..."); + LogMessage(2) << "Old token, or screen flying..."; return; } screen_flying_ = true; @@ -858,17 +862,15 @@ class KiwiInstance : public pp::Instance { avgfps_ = 0.9*avgfps_ + 0.1*cfps; if ((k_ % ((int)avgfps_+1)) == 0 || debug_ >= 1) { - std::stringstream ss; - ss << "fps: " << (int)(cfps+0.5) - << " (" << (int)(avgfps_+0.5) << ")" - << " delay: " << (int)(delay*1000) - << " deltat: " << (int)(deltat*1000) - << " target fps: " << (int)(target_fps_) - << " " << size_.width() << "x" << size_.height(); - LogMessage(0, ss.str()); + LogMessage(0) << "fps: " << (int)(cfps+0.5) + << " (" << (int)(avgfps_+0.5) << ")" + << " delay: " << (int)(delay*1000) + << " deltat: " << (int)(deltat*1000) + << " target fps: " << (int)(target_fps_) + << " " << size_.width() << "x" << size_.height(); } - LogMessage(5, "OnFlush"); + LogMessage(5) << "OnFlush"; screen_flying_ = false; From 9c5e1cc57e9aabb985534d52c7dedebf15bb5151 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Nov 2014 16:22:29 -0800 Subject: [PATCH 074/334] kiwi: Smarter Search key logic. Press Super_L when needed, release before non-letter keys. Also send event when Search is pressed on its own. --- host-ext/nacl_src/kiwi.cc | 52 +++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 765dd2500..b52e6ce36 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -482,14 +482,36 @@ class KiwiInstance : public pp::Instance { * letter keys (a-z). This way, Home (Search+Left) appears without * modifiers (instead of Super_L+Home) */ if (keystr == "OSLeft") { - pending_super_l_ = down; - return PP_TRUE; + if (down) { + search_state_ = kSearchUpFirst; + } else { + if (search_state_ == kSearchUpFirst) { + /* No other key was pressed: press+release */ + SendKey(kSUPER_L, 1); + SendKey(kSUPER_L, 0); + } else if (search_state_ == kSearchDown) { + SendKey(kSUPER_L, 0); + } + search_state_ = kSearchInactive; + } + return PP_TRUE; /* Ignore key */ } - bool letter = (keycode >= 65 && keycode <= 90); - if (letter && pending_super_l_ && down) SendKey(kSUPER_L, 1); + if (keycode >= 65 && keycode <= 90) { /* letter */ + /* Search is active, send Super_L if needed */ + if (down && (search_state_ == kSearchUpFirst || + search_state_ == kSearchUp)) { + SendKey(kSUPER_L, 1); + search_state_ = kSearchDown; + } + } else { /* non-letter */ + /* Release Super_L if needed */ + if (search_state_ == kSearchDown) + SendKey(kSUPER_L, 0); + /* Transition from UpFirst to Up */ + search_state_ = kSearchUp; + } SendKey(keysym, down ? 1 : 0); - if (letter && pending_super_l_ && !down) SendKey(kSUPER_L, 0); } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { @@ -774,7 +796,11 @@ class KiwiInstance : public pp::Instance { void SendClick(int button, int down) { struct mouseclick* mc; - if (pending_super_l_ && down) SendKey(kSUPER_L, 1); + if (down && (search_state_ == kSearchUpFirst || + search_state_ == kSearchUp)) { + SendKey(kSUPER_L, 1); + search_state_ = kSearchDown; + } pp::VarArrayBuffer array_buffer(sizeof(*mc)); mc = static_cast(array_buffer.Map()); @@ -784,8 +810,6 @@ class KiwiInstance : public pp::Instance { array_buffer.Unmap(); SocketSend(array_buffer, true); - if (pending_super_l_ && !down) SendKey(kSUPER_L, 0); - /* That means we have focus */ SetTargetFPS(kFullFPS); } @@ -959,8 +983,16 @@ class KiwiInstance : public pp::Instance { /* Mouse wheel accumulators */ int mouse_wheel_x = 0; int mouse_wheel_y = 0; - /* Super_L press has been delayed */ - bool pending_super_l_ = false; + + /* Search key state: + * - active/inactive: Key is pushed on Chromium OS side + * - down/up: Key is pushed on xiwi side */ + enum { + kSearchInactive, /* Inactive (up) */ + kSearchUpFirst, /* Active, up, no other key (yet) */ + kSearchUp, /* Active, up */ + kSearchDown /* Active, down */ + } search_state_ = kSearchInactive; /* Touch */ int touch_count_; /* Number of points currently pressed */ From 2f6f212b37329a6b010406124e590224e4daa114 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 7 Nov 2014 16:26:06 -0800 Subject: [PATCH 075/334] fbserver, kb_add: Deduplicate keys Sometimes trying to be too smart hurts... Long presses on a single key filled up the array, and crashed the server... With this, we check if the key is already recorded before adding it. --- src/fbserver.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fbserver.c b/src/fbserver.c index 40fb06578..9f59c8de5 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -54,6 +54,13 @@ static int pressed_len = 0; void kb_add(keybuttontype type, uint32_t code) { trueorabort(pressed_len < sizeof(pressed)/sizeof(struct keybutton), "Too many keys pressed"); + + int i; + for (i = 0; i < pressed_len; i++) { + if (pressed[i].type == type && pressed[i].code == code) + return; + } + pressed[pressed_len].type = type; pressed[pressed_len].code = code; pressed_len++; From dab87c2da17fd9031ad134684efbbeb7efa8754e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 10 Nov 2014 11:03:44 -0800 Subject: [PATCH 076/334] kiwi: Fix croutoncycle switch, so that window comes back maximized Apply same trick as when page flipper is pressed: If in fullscreen, first maximize, then minimize. --- host-ext/crouton/background.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index d309b3c84..587e387ba 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -426,14 +426,25 @@ function websocketMessage(evt) { case 'X': /* Ask to open a crouton-in-a-tab window */ display = parseInt(payload); if (display <= 0) { - /* Get out of full screen, for the current window */ + /* Minimize all kiwi windows */ var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { var winid = kiwi_win_[disps[i]].id; chrome.windows.update(winid, {focused: false}); + + minimize = function(win) { + chrome.windows.update(winid, + {'state': 'minimized'}, function(win) {})} + chrome.windows.get(winid, function(win) { - chrome.windows.update(winid, {'state': 'minimized'}, - function(win) {}); + /* To make restore nicer, first exit full screen, + * then minimize */ + if (win.state == "fullscreen") { + chrome.windows.update(winid, + {'state': 'maximized'}, minimize) + } else { + minimize() + } }) } } else if (kiwi_win_[display] && kiwi_win_[display].id >= 0 && From a1dad25ab1cc578c90fe5a4aac08c9a203ec3ff8 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 10 Nov 2014 19:26:45 -0800 Subject: [PATCH 077/334] kiwi: Fix SuperL logic. --- host-ext/nacl_src/kiwi.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index b52e6ce36..d94537018 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -462,7 +462,8 @@ class KiwiInstance : public pp::Instance { << ": C:" << keystr << "/KC:" << std::hex << keycode << "/KS:" << std::hex << keysym - << (keysym == 0 ? " (KEY UNKNOWN!)" : ""); + << (keysym == 0 ? " (KEY UNKNOWN!)" : "") + << " searchstate:" << search_state_; if (keysym == 0) { return PP_TRUE; @@ -506,10 +507,13 @@ class KiwiInstance : public pp::Instance { } } else { /* non-letter */ /* Release Super_L if needed */ - if (search_state_ == kSearchDown) + if (search_state_ == kSearchDown) { SendKey(kSUPER_L, 0); - /* Transition from UpFirst to Up */ - search_state_ = kSearchUp; + search_state_ = kSearchUp; + } else if (search_state_ == kSearchUpFirst) { + /* Switch from UpFirst to Up */ + search_state_ = kSearchUp; + } } SendKey(keysym, down ? 1 : 0); } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN || From bdb20def0362c7b802f5f3e30e69af0ff2eb16a3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 10 Nov 2014 21:25:39 -0800 Subject: [PATCH 078/334] kiwi/crouton*: Standardize websocket interface - Always use display numbers (`X` commands, croutoncycle output) - Use `cros` to identify Chromium OS display (not `aura`) - Extension: Do not display X11 windows - Extension: Switch using display numbers/cros instead of indices --- chroot-bin/croutonclip | 19 ++++++---------- chroot-bin/croutoncycle | 40 ++++++++++++++++++---------------- chroot-bin/croutonwheel | 4 ++-- host-ext/crouton/background.js | 30 +++++++++++++++---------- installer/functions | 1 + 5 files changed, 50 insertions(+), 44 deletions(-) diff --git a/chroot-bin/croutonclip b/chroot-bin/croutonclip index 630dd2fb0..310419ab1 100755 --- a/chroot-bin/croutonclip +++ b/chroot-bin/croutonclip @@ -10,8 +10,6 @@ VERBOSE='' . "`dirname "$0"`/../installer/functions" -CROUTONCLIPPIDFILE='/tmp/crouton-lock/clip' - # rundisplay :X cmd ... # Run a command on the specified display rundisplay() { @@ -44,7 +42,7 @@ copyclip() { # Copy clipboard content from the current display { - if [ "$current" = 'aura' ]; then + if [ "$current" = 'cros' ]; then echo -n 'R' | websocketcommand else # Check if display is still running @@ -65,7 +63,7 @@ copyclip() { fi # Paste clipboard content to the next display - if [ "$next" = 'aura' ]; then + if [ "$next" = 'cros' ]; then STATUS="`(echo -n 'W'; cat) | websocketcommand`" if [ "$STATUS" != 'WOK' ]; then # Write failed, skip Chromium OS (do not update $current) @@ -122,19 +120,16 @@ waitwebsocket() { # Assume current display is Chromium OS: avoid race as we may not be able to # detect the first VT/window change. -current='aura' - -# Only let one instance *really* run at a time -PIDFILE='/tmp/crouton-lock/clip' +current='cros' -mkdir -m 775 -p /tmp/crouton-lock -exec 3>>"$PIDFILE" +mkdir -m 775 -p "$CROUTONLOCKDIR" +exec 3>>"$CROUTONLOCKDIR/clip" if ! flock -n 3; then echo "Another instance of croutonclip running, waiting..." flock 3 fi -addtrap "echo -n > '$PIDFILE' 2>/dev/null" +addtrap "echo -n > '$CROUTONLOCKDIR/clip' 2>/dev/null" ( # This subshell handles USR1 signals from croutoncycle. @@ -156,7 +151,7 @@ addtrap "echo -n > '$PIDFILE' 2>/dev/null" trap "echo 'USR1'" USR1 # Set the PID of this subshell after the trap is in place - sh -c 'echo -n "$PPID"' > "$PIDFILE" + sh -c 'echo -n "$PPID"' > "$CROUTONLOCKDIR/clip" # Force an update when started. echo "Force" diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index e2cd292ec..94d872f46 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -17,12 +17,12 @@ Cycles through running graphical chroots. # Undocumented: # display: return display associated with current window # (used from croutonclip): -# - aura: Chromium OS -# - 0: Chromium OS X11 display (non-aura window) -# - 1-9: chroot displays +# - cros: Chromium OS +# - :0: Chromium OS X11 display (non-aura window) +# - :1-9: chroot displays # s: informs of a Chromium OS window change (called from extension): -# - 0: any Chromium OS window -# - n: kiwi window: X11 display number +# - cros: any Chromium OS window +# - :1-9: kiwi window: X11 display number # force [command]: Force switching display, even if it does not appear # to be necessary. @@ -57,15 +57,15 @@ getname() { } # Only let one instance run at a time to avoid nasty race conditions -mkdir -m 775 -p /tmp/crouton-lock -exec 3>/tmp/crouton-lock/cycle +mkdir -m 775 -p "$CROUTONLOCKDIR" +exec 3>"$CROUTONLOCKDIR/cycle" flock 3 # set display command from extension if [ "$cmd" = 's' ]; then echo "$disp" > "$CRIATDISPLAY" - if [ -s "$CROUTONCLIPPIDFILE" ]; then - kill -USR1 "`cat "$CROUTONCLIPPIDFILE"`" || true + if [ -s "$CROUTONLOCKDIR/clip" ]; then + kill -USR1 "`cat "$CROUTONLOCKDIR/clip"`" || true fi exit 0 fi @@ -126,8 +126,8 @@ if [ "$tty" = 'tty1' ]; then if [ -n "$xiwiactive" -a "${disp%"*"}" = "$aurawin" -a \ -s "$CRIATDISPLAY" ]; then kiwidisp="`cat $CRIATDISPLAY`" - if [ "$kiwidisp" -ge 0 ]; then - curdisp=":$kiwidisp" + if [ "${kiwidisp#:[0-9]}" != "$kiwidisp" ]; then + curdisp="$kiwidisp" fi fi break @@ -169,6 +169,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then if [ "${number#[0-9]}" = "$number" ]; then number='0' else + disp=":$number" line="`getname "$number"`" fi fi @@ -176,7 +177,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then active='*' if [ "$cmd" = 'd' ]; then if [ "$line" = 'aura_root_0' ]; then - echo 'aura' + echo 'cros' else echo ":$number" fi @@ -186,13 +187,14 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then fi if [ "$line" = 'aura_root_0' ]; then line="$chromiumos" + disp="cros" + window='' fi if [ "$cmd" = 'l' ]; then - echo "$number$active $line" + echo "${disp%"*"}$active $line" fi done for disp in $displist; do - number="${disp#:}" active=' ' if [ "$disp" = "$curdisp" ]; then active='*' @@ -203,7 +205,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then fi if [ "$cmd" = 'l' ]; then - echo -n "$number$active " + echo -n "$disp$active " getname "$disp" fi done @@ -278,7 +280,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then fi if [ -n "$xiwiactive" -a "${destdisp%"*"}" = "$aurawin" ]; then - STATUS="`echo "X-1" | websocketcommand`" + STATUS="`echo -n "Xcros" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi @@ -290,7 +292,7 @@ else sudo -n chvt 1 sleep .1 fi - STATUS="`echo "X${destdisp#:}" | websocketcommand`" + STATUS="`echo -n "X${destdisp}" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi @@ -304,8 +306,8 @@ else fi fi -if [ -s "$CROUTONCLIPPIDFILE" ]; then - kill -USR1 "`cat "$CROUTONCLIPPIDFILE"`" || true +if [ -s "$CROUTONLOCKDIR/clip" ]; then + kill -USR1 "`cat "$CROUTONLOCKDIR/clip"`" || true fi # Wait a flip and then refresh the display for good measure diff --git a/chroot-bin/croutonwheel b/chroot-bin/croutonwheel index 2513fb523..dc492baa3 100755 --- a/chroot-bin/croutonwheel +++ b/chroot-bin/croutonwheel @@ -50,8 +50,8 @@ fi eval "`host-x11`" # Only let one instance *really* run at a time -mkdir -m 775 -p /tmp/crouton-lock -exec 3>/tmp/crouton-lock/wheel +mkdir -m 775 -p "$CROUTONLOCKDIR" +exec 3>"$CROUTONLOCKDIR/wheel" flock 3 # Monitor xinput2 events, reacting only to scroll events (axes 2 and 3). diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 587e387ba..5defa6310 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -222,7 +222,7 @@ function refreshUI() { cell2.innerHTML = windows_[i].name; cell2.onclick = (function(i) { return function() { if (active_) { - websocket_.send("C" + i); + websocket_.send("C" + windows_[i].display); closePopup(); } } })(i); @@ -404,16 +404,22 @@ function websocketMessage(evt) { case 'C': /* Returned data from a croutoncycle command */ /* Non-zero length has a window list; otherwise it's a cycle signal */ if (payload.length > 0) { - windows_ = payload.split('\n').filter( - function(x) { return x.length > 3 } - ).map( + windows_ = payload.split('\n').map( function(x) { + m = x.match(/^([^ *]*)\*? +(.*)$/) + if (!m) + return null; + + /* Only display cros and X11 servers (no window) */ + if (m[1] != "cros" && !m[1].match(/^:([0-9]+)$/)) + return null; + k = new Object() - k.display = x[0]; - k.name = x.substring(3); + k.display = m[1]; + k.name = m[2]; return k; } - ) + ).filter( function(x) { return !!x; } ) windows_.forEach(function(k) { if (kiwi_win_[k.display] && kiwi_win_[k.display].window) { @@ -424,8 +430,10 @@ function websocketMessage(evt) { refreshUI(); break; case 'X': /* Ask to open a crouton-in-a-tab window */ - display = parseInt(payload); - if (display <= 0) { + display = payload + match = display.match(/^:([0-9]+)$/) + displaynum = match ? match[1] : null + if (!displaynum) { /* Minimize all kiwi windows */ var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { @@ -467,7 +475,7 @@ function websocketMessage(evt) { win = windows_.filter(function(x){ return x.display == display })[0] name = win ? win.name : "crouton in a tab"; - chrome.windows.create({ 'url': "window.html?display=" + display + + chrome.windows.create({ 'url': "window.html?display=" + displaynum + "&debug=" + (debug_ ? 1 : 0) + "&hidpi=" + (hidpi_ ? 1 : 0) + "&title=" + encodeURIComponent(name), @@ -525,7 +533,7 @@ function websocketClose() { * clipboard can be transfered. */ function windowFocusChanged(windowid) { var disps = Object.keys(kiwi_win_); - nextfocus_win_ = -1; + nextfocus_win_ = "cros"; for (var i = 0; i < disps.length; i++) { if (kiwi_win_[disps[i]].id == windowid) { nextfocus_win_ = disps[i]; diff --git a/installer/functions b/installer/functions index 4a885f40a..4cc0f02fa 100644 --- a/installer/functions +++ b/installer/functions @@ -156,6 +156,7 @@ validate_name() { # Websocket interface PIPEDIR='/tmp/crouton-ext' CRIATDISPLAY="$PIPEDIR/kiwi-display" +CROUTONLOCKDIR='/tmp/crouton-lock' # Write a command to croutonwebsocket, and read back response websocketcommand() { From b1caf25b7c43f526747711f2cc9347f2874a3ad2 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 10 Nov 2014 21:37:36 -0800 Subject: [PATCH 079/334] kiwi: Rename `log` messages to `debug` --- host-ext/crouton/background.js | 3 ++- host-ext/crouton/window.html | 4 ++-- host-ext/crouton/window.js | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 5defa6310..68f2f17b4 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -105,7 +105,8 @@ function updateWindowList(force) { } /* Called from kiwi (window.js), so we can directly access each window */ -function registerKiwi(display, window) { +function registerKiwi(displaynum, window) { + display = ":" + displaynum if (kiwi_win_[display] && kiwi_win_[display].id >= 0) { kiwi_win_[display].window = window; } diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 3174702ed..643fe66bd 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -24,7 +24,7 @@ bottom: 0; left: 0; } - #status, #log { + #status, #debug { color: rgba(255, 255, 255, 1); } #header { @@ -55,7 +55,7 @@
Initializing...
diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index d92e15415..115e350d8 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -107,16 +107,16 @@ function handleMessage(message) { type = str.substr(0, i); payload = str.substr(i+1); } else { - type = "log"; + type = "debug"; payload = str; } console.log(message.data); - if (type == "log") { - var logEl = document.getElementById('log'); - if (logEl) - logEl.textContent = message.data; + if (type == "debug") { + var debugEl = document.getElementById('debug'); + if (debugEl) + debugEl.textContent = message.data; } else if (type == "status") { updateStatus(payload); } else if (type == "connected") { From 23b57ea9d423ede1278dd59f4b9e2d6850e5f817 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 10 Nov 2014 21:42:10 -0800 Subject: [PATCH 080/334] extension: Keep track of Show Log state. --- host-ext/crouton/background.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 68f2f17b4..ac024a153 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -25,6 +25,7 @@ var websocket_ = null; /* Active connection */ /* State variables */ var debug_ = false; +var showlog_ = false; /* true if extension log should be shown */ var hidpi_ = false; /* true if kiwi windows should be opened in HiDPI mode */ var enabled_ = true; /* true if we are trying to connect */ var active_ = false; /* true if we are connected to a server */ @@ -147,7 +148,6 @@ function refreshUI() { helplink = view.document.getElementById("help"); helplink.onclick = showHelp; /* Update enable/disable link. */ - /* FIXME: Sometimes, there is a little box coming around the link */ enablelink = view.document.getElementById("enable"); if (enabled_) { enablelink.textContent = "Disable"; @@ -240,9 +240,11 @@ function refreshUI() { /* Only update if "show log" is enabled */ logcheck = view.document.getElementById("logcheck"); logcheck.onclick = function() { + showlog_ = logcheck.checked; refreshUI(); } - if (logcheck.checked) { + logcheck.checked = showlog_; + if (showlog_) { for (var i = 0; i < logger_.length; i++) { value = logger_[i]; From 7e42ba12e750cfc1565193101d469fd68c3e4f4f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Dec 2013 18:51:28 -0800 Subject: [PATCH 081/334] Add vtlock command for tests to ensure graphics exclusivity --- test/run.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/run.sh b/test/run.sh index 06118c5ab..a494dc87b 100755 --- a/test/run.sh +++ b/test/run.sh @@ -378,6 +378,18 @@ SyntaxError: invalid syntax" 1>&2 return 0 } +# Ensures only one test can play with graphics at one time +vtlock() { + local vtlockfile='/var/lock/croutonvt' + { + if ! flock -n 3; then + log 'Waiting for VT lock...' + flock 3 + fi + "$@" || return $? + } 3>>"$vtlockfile" +} + # Default responses to questions export CROUTON_USERNAME='test' export CROUTON_PASSPHRASE='hunter2' From cf5207a6fe58dc6e328db389286f0f606334bab4 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 29 Dec 2013 18:59:00 -0500 Subject: [PATCH 082/334] Don't explicitly include core. --- targets/xephyr | 2 +- targets/xorg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/xephyr b/targets/xephyr index 16f775f02..973278ec7 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -REQUIRES='core audio' +REQUIRES='audio' PROVIDES='x11' DESCRIPTION='Nested X11 backend. Improves compatibility but lacks GPU accel.' CHROOTBIN='brightness croutoncycle croutonpowerd croutonwheel croutonxinitrc-wrapper xinit' diff --git a/targets/xorg b/targets/xorg index 03aae7aaa..d7d2db2a5 100644 --- a/targets/xorg +++ b/targets/xorg @@ -6,7 +6,7 @@ if [ "$TARGETS" != 'help' -a "$TARGETS" != 'check' \ -a "${ARCH#arm}" != "$ARCH" ]; then error 1 'xorg target does not work on ARM.' fi -REQUIRES='core audio' +REQUIRES='audio' PROVIDES='x11' DESCRIPTION='X.Org X11 backend. Enables GPU acceleration on supported platforms.' CHROOTBIN='brightness croutoncycle croutonpowerd croutonxinitrc-wrapper setres xinit' From e88d1b2a35a600c250091f02795f6cffe93e72b0 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 29 Dec 2013 18:59:19 -0500 Subject: [PATCH 083/334] Don't override the log fd --- test/run.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/run.sh b/test/run.sh index a494dc87b..21c0cf683 100755 --- a/test/run.sh +++ b/test/run.sh @@ -8,7 +8,7 @@ # Tests numbering is categorical in 10's by the following guide: # 0*: meta-tests, e.g. tester # 1*: core tests, e.g. basic, background, upgrade -# 2*: small-target/tech tests, e.g. cli-extra, audio +# 3*: small-target/tech tests, e.g. cli-extra, audio # 5*: DE tests, e.g. xfce, xbmc # 9*: misc application tests, e.g. chrome # Numbering within a category is arbitrary and can have overlaps. @@ -210,12 +210,12 @@ bootstrap() { echo "$file" # Use flock so that bootstrap can be called in parallel - if ! flock -n 3; then + if ! flock -n 4; then log "Waiting for bootstrap for $1 to complete..." - flock 3 + flock 4 elif [ ! -s "$file" ]; then crouton -r "$1" -f "$file" -d 1>&2 - fi 3>>"$file" + fi 4>>"$file" if [ ! -s "$file" ]; then log "FAIL due to incomplete bootstrap for $1" @@ -235,14 +235,14 @@ snapshot() { local name="${3:-"$1"}" # Use flock so that snapshot can be called in parallel - if ! flock -n 3; then + if ! flock -n 4; then log "Waiting for snapshot for $1-$targets to complete..." - flock 3 + flock 4 elif [ ! -s "$file" ]; then crouton -f "`bootstrap "$1"`" -t "$targets" -n "$name" 1>&2 host edit-chroot -y -b -f "$file" "$name" return 0 - fi 3>>"$file" + fi 4>>"$file" # Restore the snapshot into place crouton -f "$file" -n "$name" @@ -382,12 +382,12 @@ SyntaxError: invalid syntax" 1>&2 vtlock() { local vtlockfile='/var/lock/croutonvt' { - if ! flock -n 3; then + if ! flock -n 4; then log 'Waiting for VT lock...' - flock 3 + flock 4 fi "$@" || return $? - } 3>>"$vtlockfile" + } 4>>"$vtlockfile" } # Default responses to questions From 3e3d10cdb7b648cb556f9aa015d40b993e0d47c6 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 29 Dec 2013 18:59:37 -0500 Subject: [PATCH 084/334] Add xephyr and xorg tests. --- test/tests/30-audio | 4 +--- test/tests/35-xorg | 19 +++++++++++++++++++ test/tests/36-xephyr | 13 +++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/tests/35-xorg create mode 100644 test/tests/36-xephyr diff --git a/test/tests/30-audio b/test/tests/30-audio index 704bc7e21..d1c1bb408 100644 --- a/test/tests/30-audio +++ b/test/tests/30-audio @@ -4,14 +4,12 @@ # found in the LICENSE file. # All supported releases should be able to create an audio chroot and play sound - if [ -z "$release" ]; then echo "all" exit 0 fi -snapshot "$release" core -crouton -u -n "$release" -t audio +snapshot "$release" audio # We pass -fdat to aplay/arecord, which means 48kHz, 16-bit, stereo. # dd writes/reads 8 blocks of 48000 bytes: 2 seconds worth of sound. diff --git a/test/tests/35-xorg b/test/tests/35-xorg new file mode 100644 index 000000000..0088c3178 --- /dev/null +++ b/test/tests/35-xorg @@ -0,0 +1,19 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# xorg won't work on arm +if uname -m | grep -q 'arm'; then + log "xorg is not available on ARM. Skipping test." + exit 0 +fi + +# All supported releases should be able to launch an xorg server +for release in $SUPPORTED_RELEASES; do + snapshot "$release" audio + crouton -U -n "$release" -t xorg + exitswithin 0 20 vtlock host \ + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 + host delete-chroot -y "$release" +done diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr new file mode 100644 index 000000000..d0b76e344 --- /dev/null +++ b/test/tests/36-xephyr @@ -0,0 +1,13 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# All supported releases should be able to launch an xephyr server +for release in $SUPPORTED_RELEASES; do + snapshot "$release" audio + crouton -U -n "$release" -t xephyr + exitswithin 0 20 vtlock host \ + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 + host delete-chroot -y "$release" +done From 35e7cfe0ee4c2abaf42d4ed1da27a326c48f484e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 30 Dec 2013 16:48:28 -0500 Subject: [PATCH 085/334] Tweak test timing; unmount-chroot is still an issue. --- test/tests/35-xorg | 2 +- test/tests/36-xephyr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests/35-xorg b/test/tests/35-xorg index 0088c3178..ebf803c80 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -13,7 +13,7 @@ fi for release in $SUPPORTED_RELEASES; do snapshot "$release" audio crouton -U -n "$release" -t xorg - exitswithin 0 20 vtlock host \ + vtlock exitswithin 0 60 host \ enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 host delete-chroot -y "$release" done diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr index d0b76e344..59828f299 100644 --- a/test/tests/36-xephyr +++ b/test/tests/36-xephyr @@ -7,7 +7,7 @@ for release in $SUPPORTED_RELEASES; do snapshot "$release" audio crouton -U -n "$release" -t xephyr - exitswithin 0 20 vtlock host \ + vtlock exitswithin 0 60 host \ enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 host delete-chroot -y "$release" done From 1f3e802e87c55bad3a6b372084f3a6fb3a9b2db2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 3 Jan 2014 11:14:01 -0800 Subject: [PATCH 086/334] Fix xorg/xephyr test command --- test/tests/35-xorg | 2 +- test/tests/36-xephyr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests/35-xorg b/test/tests/35-xorg index ebf803c80..dc8093794 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -14,6 +14,6 @@ for release in $SUPPORTED_RELEASES; do snapshot "$release" audio crouton -U -n "$release" -t xorg vtlock exitswithin 0 60 host \ - enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true host delete-chroot -y "$release" done diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr index 59828f299..710d94220 100644 --- a/test/tests/36-xephyr +++ b/test/tests/36-xephyr @@ -8,6 +8,6 @@ for release in $SUPPORTED_RELEASES; do snapshot "$release" audio crouton -U -n "$release" -t xephyr vtlock exitswithin 0 60 host \ - enter-chroot -n "$release" exec xinit /usr/bin/xterm -e exit 0 + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true host delete-chroot -y "$release" done From 122fda44e9636454feaa4066dbf47c1871a60e0e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 13 Jun 2014 09:29:22 +0800 Subject: [PATCH 087/334] test/35-xorg: Dump OpenGL+dri information --- test/tests/35-xorg | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/tests/35-xorg b/test/tests/35-xorg index dc8093794..2261fdb7c 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -13,7 +13,35 @@ fi for release in $SUPPORTED_RELEASES; do snapshot "$release" audio crouton -U -n "$release" -t xorg + + echo ' + install mesa-utils + mkdir -p /home/tmp + chmod 777 /home/tmp + cat > /home/tmp/xinitrc <&1 | tee /home/tmp/glxinfo.out +xdriinfo 2>&1 | tee /home/tmp/xdriinfo.out +exec /usr/bin/xterm -e true +END + ' | crouton -T -U -n "$release" + vtlock exitswithin 0 60 host \ - enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true + enter-chroot -n "$release" exec xinit "/home/tmp/xinitrc" + host enter-chroot -n "$release" sh -c ' + echo -n "release: " + croutonversion -r + echo -n "uname: " + uname -a + echo -n "vendor: " + cat /sys/class/graphics/fb0/device/vendor + echo -n "device: " + cat /sys/class/graphics/fb0/device/device + echo "glxinfo:" + cat /home/tmp/glxinfo.out + echo -n "xdriinfo: " + cat /home/tmp/xdriinfo.out + ' | log host delete-chroot -y "$release" done From 421cfb5b6003af775c06cf36520d62414e7030a8 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 29 Oct 2014 14:09:28 -0700 Subject: [PATCH 088/334] 35/36-xorg/xephyr: Update to new split test --- test/tests/35-xorg | 68 +++++++++++++++++++++++--------------------- test/tests/36-xephyr | 19 +++++++------ 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/test/tests/35-xorg b/test/tests/35-xorg index 2261fdb7c..1442dc6d2 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -1,47 +1,51 @@ #!/bin/sh -e -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# xorg won't work on arm -if uname -m | grep -q 'arm'; then - log "xorg is not available on ARM. Skipping test." +# All supported releases should be able to launch an xorg server +if [ -z "$release" ]; then + # xorg won't work on arm + if uname -m | grep -q 'arm'; then + log "xorg is not available on ARM. Skipping test." + else + echo "all" + fi exit 0 fi -# All supported releases should be able to launch an xorg server -for release in $SUPPORTED_RELEASES; do - snapshot "$release" audio - crouton -U -n "$release" -t xorg +snapshot "$release" audio +crouton -U -n "$release" -t xorg - echo ' - install mesa-utils - mkdir -p /home/tmp - chmod 777 /home/tmp - cat > /home/tmp/xinitrc < /home/tmp/xinitrc <&1 | tee /home/tmp/glxinfo.out xdriinfo 2>&1 | tee /home/tmp/xdriinfo.out exec /usr/bin/xterm -e true END - ' | crouton -T -U -n "$release" +' | crouton -T -U -n "$release" - vtlock exitswithin 0 60 host \ - enter-chroot -n "$release" exec xinit "/home/tmp/xinitrc" - host enter-chroot -n "$release" sh -c ' - echo -n "release: " - croutonversion -r - echo -n "uname: " - uname -a - echo -n "vendor: " - cat /sys/class/graphics/fb0/device/vendor - echo -n "device: " - cat /sys/class/graphics/fb0/device/device - echo "glxinfo:" - cat /home/tmp/glxinfo.out - echo -n "xdriinfo: " - cat /home/tmp/xdriinfo.out - ' | log - host delete-chroot -y "$release" -done +vtlock exitswithin 0 60 host \ + enter-chroot -n "$release" exec xinit "/home/tmp/xinitrc" +host enter-chroot -n "$release" sh -c ' + echo "====GLX info" + echo -n "release:" + croutonversion -r + echo -n "uname:" + uname -a + echo -n "vendor:" + cat /sys/class/graphics/fb0/device/vendor + echo -n "device:" + cat /sys/class/graphics/fb0/device/device + echo -n "xdriinfo:" + cat /home/tmp/xdriinfo.out + echo "==glxinfo" + cat /home/tmp/glxinfo.out + echo "====/GLX info" +' | log +host delete-chroot -y "$release" diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr index 710d94220..7f567c5bb 100644 --- a/test/tests/36-xephyr +++ b/test/tests/36-xephyr @@ -1,13 +1,16 @@ #!/bin/sh -e -# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # All supported releases should be able to launch an xephyr server -for release in $SUPPORTED_RELEASES; do - snapshot "$release" audio - crouton -U -n "$release" -t xephyr - vtlock exitswithin 0 60 host \ - enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true - host delete-chroot -y "$release" -done +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +snapshot "$release" audio +crouton -U -n "$release" -t xephyr +vtlock exitswithin 0 60 host \ + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true +host delete-chroot -y "$release" From 3f68ccb687407c8a07019db393a5ec7f94d05e0d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 30 Oct 2014 10:18:39 -0700 Subject: [PATCH 089/334] test/run.sh: Change vtlock function So we can run multiple commands within the lock. --- test/run.sh | 20 ++++++++++++++------ test/tests/35-xorg | 2 +- test/tests/36-xephyr | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/test/run.sh b/test/run.sh index 21c0cf683..2e792e58b 100755 --- a/test/run.sh +++ b/test/run.sh @@ -379,15 +379,23 @@ SyntaxError: invalid syntax" 1>&2 } # Ensures only one test can play with graphics at one time +# Run it without parameters, in a subshell. The lock will be released when +# the subshell exits vtlock() { local vtlockfile='/var/lock/croutonvt' - { - if ! flock -n 4; then - log 'Waiting for VT lock...' - flock 4 - fi + exec 4>>"$vtlockfile" + if ! flock -n 4; then + log 'Waiting for VT lock...' + flock 4 + fi +} + +# Runs the provided command under vtlock +vtlockrun() { + ( + vtlock "$@" || return $? - } 4>>"$vtlockfile" + ) } # Default responses to questions diff --git a/test/tests/35-xorg b/test/tests/35-xorg index 1442dc6d2..d6797d0fc 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -30,7 +30,7 @@ exec /usr/bin/xterm -e true END ' | crouton -T -U -n "$release" -vtlock exitswithin 0 60 host \ +vtlockrun exitswithin 0 60 host \ enter-chroot -n "$release" exec xinit "/home/tmp/xinitrc" host enter-chroot -n "$release" sh -c ' echo "====GLX info" diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr index 7f567c5bb..6467bf229 100644 --- a/test/tests/36-xephyr +++ b/test/tests/36-xephyr @@ -11,6 +11,6 @@ fi snapshot "$release" audio crouton -U -n "$release" -t xephyr -vtlock exitswithin 0 60 host \ +vtlockrun exitswithin 0 60 host \ enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true host delete-chroot -y "$release" From 71174c29f33dd97601e8b628408f0d7c8c8fa9b7 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 30 Oct 2014 11:58:49 -0700 Subject: [PATCH 090/334] test/run.sh: Kill subprocesses when test exits Move trap call so that it is actually executed, and kill any leftover subprocesses so the test can complete. Also, unmount chroot with -f. --- test/run.sh | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/test/run.sh b/test/run.sh index 2e792e58b..ef3771a42 100755 --- a/test/run.sh +++ b/test/run.sh @@ -111,7 +111,30 @@ logto() { local AWK='mawk -W interactive' # srand() uses system time as seed but returns previous seed. Call it twice. ((((ret=0; TRAP='' - ( release="$2" . "$1" ) &- || ret=$? + ( + PREFIX="`mktemp -d --tmpdir="$PREFIXROOT" "$tname.XXX"`" + # Remount noexec/etc to make the environment as harsh as possible + mount --bind "$PREFIX" "$PREFIX" + mount -i -o remount,nosuid,nodev,noexec "$PREFIX" + + # Get subshell pid + pid="`sh -c 'echo $PPID'`" + + # Clean up on exit + settrap " + set -x + echo Running trap... + if [ -d '$PREFIX/chroots' ]; then + sh -e '$SCRIPTDIR/host-bin/unmount-chroot' \ + -a -f -y -c '$PREFIX/chroots' + fi + # Kill any leftover subprocess + pkill -9 -P '$pid' + umount -l '$PREFIX' + rm -rf --one-file-system '$PREFIX' + " + release="$2" . "$1" + ) &- || ret=$? sleep 1 if [ "$ret" = 0 ]; then log "TEST PASSED: $retpreamble $ret" @@ -546,19 +569,6 @@ while true; do tname="${t##*/}.$rel.$try" # Run the test ( - PREFIX="`mktemp -d --tmpdir="$PREFIXROOT" "$tname.XXX"`" - # Remount PREFIX noexec/etc to make the environment as harsh as possible - mount --bind "$PREFIX" "$PREFIX" - mount -i -o remount,nosuid,nodev,noexec "$PREFIX" - # Clean up on exit - settrap " - if [ -d '$PREFIX/chroots' ]; then - sh -e '$SCRIPTDIR/host-bin/unmount-chroot' \ - -a -y -c '$PREFIX/chroots' - fi - umount -l '$PREFIX' - rm -rf --one-file-system '$PREFIX' - " if ! logto "$TESTDIR/$tname" "$t" "$rel" "$try"; then if [ "$((try+1))" -lt "$MAXTRIES" ]; then # Test failed, try again... From 0338ea9868c5c4b5e51c139f7fe6beb84ad5c0e8 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 30 Oct 2014 10:19:19 -0700 Subject: [PATCH 091/334] tests/w?-*: Test all de/wm --- test/tests/w0-common | 59 ++++++++++++++++++++++++++++++++++++++++++++ test/tests/w1-e17 | 17 +++++++++++++ test/tests/w2-gnome | 16 ++++++++++++ test/tests/w3-kde | 16 ++++++++++++ test/tests/w4-lxde | 16 ++++++++++++ test/tests/w5-unity | 16 ++++++++++++ test/tests/w6-xbmc | 16 ++++++++++++ test/tests/w7-xfce | 18 ++++++++++++++ 8 files changed, 174 insertions(+) create mode 100644 test/tests/w0-common create mode 100644 test/tests/w1-e17 create mode 100644 test/tests/w2-gnome create mode 100644 test/tests/w3-kde create mode 100644 test/tests/w4-lxde create mode 100644 test/tests/w5-unity create mode 100644 test/tests/w6-xbmc create mode 100644 test/tests/w7-xfce diff --git a/test/tests/w0-common b/test/tests/w0-common new file mode 100644 index 000000000..79db55eb5 --- /dev/null +++ b/test/tests/w0-common @@ -0,0 +1,59 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This test is sourced by other w* test, do not run it on it's own +if [ -z "$release" -o -z "$target" ]; then + exit 0 +fi + +if [ -z "$startcmd" ]; then + error 2 "startcmd not defined" +fi + +# Start with a x11 snapshot +snapshot "$release" x11 + +# Install snapshot tools + xte +echo 'install --minimal x11-apps imagemagick xautomation' | \ + crouton -T -U -n "$release" + +ret=0 +crouton -u -n "$release" -t "$target" || ret=$? +if [ "$ret" -ne 0 ]; then + if [ "$ret" -eq 99 ]; then + log "Target $target failed on $release, as expected (unsupported combination)." + exit 0 + else + log "Target $target failed on $release." + exit "$ret" + fi +fi + +( + vtlock + host "$startcmd" -b -n "$release" + # FIXME: Wait for croutoncycle to change? + sleep 60 + host enter-chroot -n "$release" sh -exc ' + # Test croutoncycle as a bonus + export DISPLAY="`croutoncycle display`" + if [ "$DISPLAY" = ":0" -o "$DISPLAY" = "aura" ]; then + echo "Invalid display ($DISPLAY)." 1>&2 + exit 1 + fi + if [ -n "'"$xte"'" ]; then + echo "'"$xte"'" | tr ";" "\n" | xte + sleep 60 + fi + # Snapshot! + xwd -root | convert -quality 75 xwd:- ~/"screenshot-'"$target"'.jpg"' + + mv "$PREFIX/chroots/$release/home/test/screenshot-$target.jpg" \ + "$file-snapshot.jpg" + + host unmount-chroot -f -y "$release" +) + +host delete-chroot -y "$release" diff --git a/test/tests/w1-e17 b/test/tests/w1-e17 new file mode 100644 index 000000000..20bcc519c --- /dev/null +++ b/test/tests/w1-e17 @@ -0,0 +1,17 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="e17" +startcmd="starte17" +# FIXME: Unfortunately, e17 cannot be configured with the keyboard only... +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w2-gnome b/test/tests/w2-gnome new file mode 100644 index 000000000..6f67045f2 --- /dev/null +++ b/test/tests/w2-gnome @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="gnome" +startcmd="startgnome" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w3-kde b/test/tests/w3-kde new file mode 100644 index 000000000..a1ebe6617 --- /dev/null +++ b/test/tests/w3-kde @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="kde" +startcmd="startkde" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w4-lxde b/test/tests/w4-lxde new file mode 100644 index 000000000..230ab5170 --- /dev/null +++ b/test/tests/w4-lxde @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="lxde" +startcmd="startlxde" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w5-unity b/test/tests/w5-unity new file mode 100644 index 000000000..43dbd8ccb --- /dev/null +++ b/test/tests/w5-unity @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="unity" +startcmd="startunity" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w6-xbmc b/test/tests/w6-xbmc new file mode 100644 index 000000000..93e45a162 --- /dev/null +++ b/test/tests/w6-xbmc @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="xbmc" +startcmd="startxbmc" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w7-xfce b/test/tests/w7-xfce new file mode 100644 index 000000000..452b2aaf2 --- /dev/null +++ b/test/tests/w7-xfce @@ -0,0 +1,18 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="xfce" +startcmd="startxfce4" +# This magic sequence presses "Use default configuration" +xte="keydown Alt_L;key Tab;key Tab;keyup Alt_L;key Left;key Left;\ +keydown Return;sleep 1;keyup Return" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" From 99c7a8a28e31d5201727b073d072d79c99f58637 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 11 Nov 2014 11:07:34 -0800 Subject: [PATCH 092/334] test/x0-alltargets: WM/DE are now tested in w* --- test/run.sh | 3 +++ test/tests/x0-alltargets | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/test/run.sh b/test/run.sh index ef3771a42..4301c1a2b 100755 --- a/test/run.sh +++ b/test/run.sh @@ -11,6 +11,9 @@ # 3*: small-target/tech tests, e.g. cli-extra, audio # 5*: DE tests, e.g. xfce, xbmc # 9*: misc application tests, e.g. chrome +# Alphabetic tests are long, and not run by default: +# w*: Start all DE/wm, and take snapshots +# x*: Install test all targets that do not have tests # Numbering within a category is arbitrary and can have overlaps. # Tests are always run in alphanumeric order unless specified by parameters. diff --git a/test/tests/x0-alltargets b/test/tests/x0-alltargets index bfe475ebe..f355ba9d5 100644 --- a/test/tests/x0-alltargets +++ b/test/tests/x0-alltargets @@ -18,8 +18,10 @@ for target in "$SCRIPTDIR/targets/"*; do continue fi - # Some other targets do not require testing in this context - for blacklist in audio core x11 xephyr xorg; do + # Some other targets do not require testing in this context, + # or have their own w* tests + for blacklist in audio core x11 xephyr xorg \ + e17 gnome kde lxde unity xbmc xfce; do if [ "$target" = "$blacklist" ]; then break fi From e67c251e23367f48819c4be134eb2a763b004e2d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 11 Nov 2014 15:16:48 -0800 Subject: [PATCH 093/334] kiwi: Workaround broken keycode in freon See http://crbug.com/425156. This leaves us no way to distinguish left and right control/shift/alt on freon. --- host-ext/nacl_src/kiwi.cc | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index d94537018..e231934c9 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -482,7 +482,10 @@ class KiwiInstance : public pp::Instance { /* We delay sending Super-L, and only "press" it on mouse clicks and * letter keys (a-z). This way, Home (Search+Left) appears without * modifiers (instead of Super_L+Home) */ - if (keystr == "OSLeft") { + /* FIXME: NumpadDecimal is a dirty hack for broken freon, + see http://crbug.com/425156 */ + if (keysym == kSUPER_L && (keystr == "OSLeft" || + keystr == "NumpadDecimal")) { if (down) { search_state_ = kSearchUpFirst; } else { @@ -713,13 +716,6 @@ class KiwiInstance : public pp::Instance { /* Converts "IE"/JavaScript keycode to X11 KeySym. * See http://unixpapa.com/js/key.html */ uint32_t KeyCodeToKeySym(uint32_t keycode, std::string code) { - if (code == "ControlLeft") return 0xffe3; - if (code == "ControlRight") return 0xffe4; - if (code == "AltLeft") return 0xffe9; - if (code == "AltRight") return 0xffea; - if (code == "ShiftLeft") return 0xffe1; - if (code == "ShiftRight") return 0xffe2; - if (keycode >= 65 && keycode <= 90) /* A to Z */ return keycode + 32; if (keycode >= 48 && keycode <= 57) /* 0 to 9 */ @@ -734,9 +730,15 @@ class KiwiInstance : public pp::Instance { case 9: return 0xff09; // tab case 12: return 0xff9d; // num 5 case 13: return 0xff0d; // enter - case 16: return 0xffe1; // shift (caught earlier!) - case 17: return 0xffe3; // control (caught earlier!) - case 18: return 0xffe9; // alt (caught earlier!) + case 16: // shift + if (code == "ShiftRight") return 0xffe2; + return 0xffe1; // (left) + case 17: // control + if (code == "ControlRight") return 0xffe4; + return 0xffe3; // (left) + case 18: // alt + if (code == "AltRight") return 0xffea; + return 0xffe9; // (left) case 19: return 0xff13; // pause case 20: return 0; // caps lock. FIXME: reenable (0xffe5) case 27: return 0xff1b; // esc From 993fc952173acb23d0efc3077c3e11e3c0f6b702 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 11 Nov 2014 17:04:10 -0800 Subject: [PATCH 094/334] kiwi: Fixes after internal review Fix C++ style check, constant references, etc. --- host-ext/nacl_src/kiwi.cc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index e231934c9..78fbd7170 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -101,7 +101,8 @@ class KiwiInstance : public pp::Instance { * The message is flushed when the object gets out of scope. */ class Message { public: - Message(KiwiInstance& ki, std::string type, bool dummy): ki_(ki) { + Message(pp::Instance* inst, const std::string& type, bool dummy): + inst_(inst) { if (!dummy) { out_.reset(new std::ostringstream()); *out_ << type << ":"; @@ -109,10 +110,10 @@ class KiwiInstance : public pp::Instance { } virtual ~Message() { - if (out_) ki_.PostMessage(out_->str()); + if (out_) inst_->PostMessage(out_->str()); } - template Message& operator<<(T&& val) { + template Message& operator<<(const T& val) { if (out_) *out_ << val; return *this; } @@ -126,37 +127,36 @@ class KiwiInstance : public pp::Instance { private: std::unique_ptr out_; - KiwiInstance& ki_; + pp::Instance* inst_; }; /* Sends a status message to Javascript */ Message StatusMessage() { - return Message(*this, "status", false); + return Message(this, "status", false); } /* Sends a logging message to Javascript */ Message LogMessage(int level) { if (level <= debug_) { - std::ostringstream status; double delta = 1000 * (pp::Module::Get()->core()->GetTime() - lasttime_); - Message m(*this, "debug", false); + Message m(this, "debug", false); m << "(" << level << ") " << (int)delta << " "; return m; } else { - return Message(*this, "debug", true); + return Message(this, "debug", true); } } /* Sends a resize message to Javascript */ void ResizeMessage(int width, int height) { - Message(*this, "resize", false) << width << "/" << height; + Message(this, "resize", false) << width << "/" << height; } /* Sends a control message to Javascript * Format: : */ - void ControlMessage(std::string type, std::string str) { - Message(*this, type, false) << str; + void ControlMessage(const std::string& type, const std::string& str) { + Message(this, type, false) << str; } /** WebSocket interface **/ @@ -195,7 +195,7 @@ class KiwiInstance : public pp::Instance { } /* Closes the WebSocket connection. */ - void SocketClose(std::string reason) { + void SocketClose(const std::string& reason) { websocket_.Close(0, pp::Var(reason), callback_factory_.NewCallback(&KiwiInstance::OnSocketClosed)); } @@ -715,7 +715,7 @@ class KiwiInstance : public pp::Instance { /* Converts "IE"/JavaScript keycode to X11 KeySym. * See http://unixpapa.com/js/key.html */ - uint32_t KeyCodeToKeySym(uint32_t keycode, std::string code) { + uint32_t KeyCodeToKeySym(uint32_t keycode, const std::string& code) { if (keycode >= 65 && keycode <= 90) /* A to Z */ return keycode + 32; if (keycode >= 48 && keycode <= 57) /* 0 to 9 */ From 26eba3adf1ae6925b3a527b0c8be013a4fd577f0 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 11 Nov 2014 18:27:03 -0800 Subject: [PATCH 095/334] kiwi: Restore from fullscreen to previous state. Unfortunately, that does not work if the window is docked on one side... (maximize/restore also does not work with docked windows, so probably a Chromium OS bug...) --- host-ext/crouton/window.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 115e350d8..2277b87cf 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -15,6 +15,8 @@ var title_ = "crouton in a tab"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ +var prevstate_ = "maximized"; /* Previous window state (before full screen) */ + /* Rate limit resize events */ var resizePending_ = false; var resizeLimited_ = false; @@ -136,8 +138,11 @@ function handleMessage(message) { } else if (type == "state" && payload == "fullscreen") { /* Toggle full screen */ chrome.windows.getCurrent(function(win) { - var newstate = (win.state == "fullscreen") ? - "maximized" : "fullscreen"; + var newstate = prevstate_; + if (win.state != "fullscreen") { + prevstate_ = win.state; + newstate = "fullscreen"; + } chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, {'state': newstate}, function(win) {}) }) From daf3163beb9777a8e5cf488c5da61e9f487d9363 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 13:36:24 -0800 Subject: [PATCH 096/334] targets/audio: Move automake conversion function to prepare file. No modification in the code. --- installer/prepare.sh | 111 +++++++++++++++++++++++++++++++++++++++++++ targets/audio | 102 ++------------------------------------- 2 files changed, 114 insertions(+), 99 deletions(-) diff --git a/installer/prepare.sh b/installer/prepare.sh index 964b67487..ac53e1221 100755 --- a/installer/prepare.sh +++ b/installer/prepare.sh @@ -193,6 +193,117 @@ compile() { } +# Convert an automake Makefile.am into a shell script, and provide useful +# functions to compile libraries and executables. +# Needs to be run in the same directory as the Makefile.am file. +# This outputs the converted Makefile.am to stdout, which is meant to be +# piped to sh -s (see audio and xiat for examples) +convert_automake() { + echo ' + top_srcdir=".." + top_builddir=".." + ' + sed -e ' + # Concatenate lines ending in \ + : start; /\\$/{N; b start} + s/ *\\\n[ \t]*/ /g + # Convert automake to shell + s/^[^ ]*:/#\0/ + s/^\t/#\0/ + s/ *= */=/ + s/\([^ ]*\) *+= */\1=${\1}\ / + s/ /\\ /g + y/()/{}/ + ' 'Makefile.am' + echo ' + # buildsources: Build all source files for target + # $1: target + # $2: additional gcc flags + # Prints a list of .o files + buildsources() { + local target="$1" + local extragccflags="$2" + + eval local sources=\"\$${target}_SOURCES\" + eval local cppflags=\"\$${target}_CPPFLAGS\" + + for dep in $sources; do + if [ "${dep%.c}" != "$dep" ]; then + ofile="${dep%.c}.o" + gcc -c "$dep" -o "$ofile" '"$archgccflags"' \ + $cppflags $extragccflags 1>&2 || return $? + echo -n "$ofile " + fi + done + } + + # fixlibadd: + # Fix list of libraries ($1): replace lib.la by -l + fixlibadd() { + for libdep in $*; do + if [ "${libdep%.la}" != "$libdep" ]; then + libdep="${libdep%.la}" + libdep="-l${libdep#lib}" + fi + echo -n "$libdep " + done + } + + # buildlib: Build a library + # $1: library name + # $2: additional linker flags + buildlib() { + local lib="$1" + local extraflags="$2" + local ofiles + # local eats the return status: separate the 2 statements + ofiles="`buildsources "${lib}_la" "-fPIC -DPIC"`" + + eval local libadd=\"\$${lib}_la_LIBADD\" + eval local ldflags=\"\$${lib}_la_LDFLAGS\" + + libadd="`fixlibadd $libadd`" + + # Detect library version (e.g. 0.0.0) + local fullver="`echo -n "$ldflags" | \ + sed -n '\''y/:/./; \ + s/.*-version-info \([0-9.]*\)$/\\1/p'\''`" + local shortver="" + # Get "short" library version (e.g. 0) + if [ -n "$fullver" ]; then + shortver=".${fullver%%.*}" + fullver=".$fullver" + fi + local fullso="$lib.so$fullver" + local shortso="$lib.so$shortver" + gcc -shared -fPIC -DPIC $ofiles $libadd -o "$fullso" \ + '"$archgccflags"' $extraflags -Wl,-soname,"$shortso" + if [ -n "$fullver" ]; then + ln -sf "$fullso" "$shortso" + # Needed at link-time only + ln -sf "$shortso" "$lib.so" + fi + } + + # buildexe: Build an executable file + # $1: executable file name + # $2: additional linker flags + buildexe() { + local exe="$1" + local extraflags="$2" + local ofiles="`buildsources "$exe" ""`" + + eval local ldadd=\"\$${exe}_LDADD\" + eval local ldflags=\"\$${exe}_LDFLAGS\" + + ldadd="`fixlibadd $ldadd`" + + gcc $ofiles $ldadd -o "$exe" '"$archgccflags"' $extraflags + } + ' +} + + # The rest is dictated first by the distro-specific prepare.sh, and then by the # selected targets. diff --git a/targets/audio b/targets/audio index a43543212..b068de0fe 100644 --- a/targets/audio +++ b/targets/audio @@ -522,111 +522,15 @@ END # Convert Makefile.am to a shell script, and run it. { echo ' - top_srcdir=".." - top_builddir=".." SBC_LIBS="'"`PKG_CONFIG_PATH="$pkgconfigpath" \ pkg-config --libs sbc`"'" SBC_CFLAGS="'"`PKG_CONFIG_PATH="$pkgconfigpath" \ pkg-config --cflags sbc`"'" ' - sed -e ' - # Concatenate lines ending in \ - : start; /\\$/{N; b start} - s/ *\\\n[ \t]*/ /g - # Convert automake to shell - s/^[^ ]*:/#\0/ - s/^\t/#\0/ - s/ *= */=/ - s/\([^ ]*\) *+= */\1=${\1}\ / - s/ /\\ /g - y/()/{}/ - ' 'Makefile.am' - echo ' - # buildsources: Build all source files for target - # $1: target - # $2: additional gcc flags - # Prints a list of .o files - buildsources() { - local target="$1" - local extragccflags="$2" - - eval local sources=\"\$${target}_SOURCES\" - eval local cppflags=\"\$${target}_CPPFLAGS\" - - for dep in $sources; do - if [ "${dep%.c}" != "$dep" ]; then - ofile="${dep%.c}.o" - gcc -c "$dep" -o "$ofile" '"$archgccflags"' \ - $cppflags $extragccflags 1>&2 || return $? - echo -n "$ofile " - fi - done - } - - # fixlibadd: - # Fix list of libraries ($1): replace lib.la by -l - fixlibadd() { - for libdep in $*; do - if [ "${libdep%.la}" != "$libdep" ]; then - libdep="${libdep%.la}" - libdep="-l${libdep#lib}" - fi - echo -n "$libdep " - done - } - - # buildlib: Build a library - # $1: library name - # $2: additional linker flags - buildlib() { - local lib="$1" - local extraflags="$2" - local ofiles - # local eats the return status: separate the 2 statements - ofiles="`buildsources "${lib}_la" "-fPIC -DPIC"`" - - eval local libadd=\"\$${lib}_la_LIBADD\" - eval local ldflags=\"\$${lib}_la_LDFLAGS\" - - libadd="`fixlibadd $libadd`" - - # Detect library version (e.g. 0.0.0) - local fullver="`echo -n "$ldflags" | \ - sed -n '\''y/:/./; \ - s/.*-version-info \([0-9.]*\)$/\\1/p'\''`" - local shortver="" - # Get "short" library version (e.g. 0) - if [ -n "$fullver" ]; then - shortver=".${fullver%%.*}" - fullver=".$fullver" - fi - local fullso="$lib.so$fullver" - local shortso="$lib.so$shortver" - gcc -shared -fPIC -DPIC $ofiles $libadd -o "$fullso" \ - '"$archgccflags"' $extraflags -Wl,-soname,"$shortso" - if [ -n "$fullver" ]; then - ln -sf "$fullso" "$shortso" - # Needed at link-time only - ln -sf "$shortso" "$lib.so" - fi - } - - # buildexe: Build an executable file - # $1: executable file name - # $2: additional linker flags - buildexe() { - local exe="$1" - local extraflags="$2" - local ofiles="`buildsources "$exe" ""`" - - eval local ldadd=\"\$${exe}_LDADD\" - eval local ldflags=\"\$${exe}_LDFLAGS\" - - ldadd="`fixlibadd $ldadd`" - - gcc $ofiles $ldadd -o "$exe" '"$archgccflags"' $extraflags - } + convert_automake + + echo ' buildlib libcras # Pass -rpath=$CRASLIBDIR to linker, so we do not need to add From f66aef8262647c50ffc8307a71a72371967991aa Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 13:44:59 -0800 Subject: [PATCH 097/334] prepare: convert_automake: Slight improvements. xorg-dummy uses some more complex syntax: implement. --- installer/prepare.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/installer/prepare.sh b/installer/prepare.sh index ac53e1221..18d1f1de3 100755 --- a/installer/prepare.sh +++ b/installer/prepare.sh @@ -210,11 +210,14 @@ convert_automake() { # Convert automake to shell s/^[^ ]*:/#\0/ s/^\t/#\0/ + s/\t/ /g s/ *= */=/ s/\([^ ]*\) *+= */\1=${\1}\ / - s/ /\\ /g - y/()/{}/ - ' 'Makefile.am' + s/ /\\ /g + y/()/{}/ + s/if\\ \(.*\)/if [ -n "${\1}" ]; then/ + s/endif/fi/ + ' 'Makefile.am' echo ' # buildsources: Build all source files for target # $1: target @@ -226,12 +229,13 @@ convert_automake() { eval local sources=\"\$${target}_SOURCES\" eval local cppflags=\"\$${target}_CPPFLAGS\" + local cflags="$cppflags ${CFLAGS} ${AM_CFLAGS}" for dep in $sources; do if [ "${dep%.c}" != "$dep" ]; then ofile="${dep%.c}.o" gcc -c "$dep" -o "$ofile" '"$archgccflags"' \ - $cppflags $extragccflags 1>&2 || return $? + $cflags $extragccflags 1>&2 || return $? echo -n "$ofile " fi done From 41c9c68febc3661fb7e1f4b581de6b19f223ebe4 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 4 Nov 2014 13:28:21 -0800 Subject: [PATCH 098/334] xiat: Arbitrary resolution through patched xorg-dummy. --- chroot-etc/xorg-dummy.conf | 5 +- targets/xiwi | 412 ++++++++++++++++++++++++++++++++----- 2 files changed, 365 insertions(+), 52 deletions(-) diff --git a/chroot-etc/xorg-dummy.conf b/chroot-etc/xorg-dummy.conf index bbf0470cd..d3199324f 100644 --- a/chroot-etc/xorg-dummy.conf +++ b/chroot-etc/xorg-dummy.conf @@ -6,10 +6,6 @@ Section "Monitor" Identifier "Monitor0" HorizSync 5.0-1000.0 VertRefresh 5.0-200.0 - # This display size corresponds to 96 dpi at 1024x768 - DisplaySize 271 203 - # The following lines are automatically generated -###MODELINES### EndSection Section "Device" @@ -26,6 +22,7 @@ Section "Screen" Monitor "Monitor0" SubSection "Display" Depth 24 + Modes "1024x768" EndSubSection EndSection diff --git a/targets/xiwi b/targets/xiwi index 9bfec2a70..11b6ea5ef 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -12,60 +12,376 @@ CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.exampl ### Append to prepare.sh: XMETHOD="${XMETHOD:-xiwi}" +# Unhold xserver-xorg-video-dummy to make sure deps are pulled in +if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then + apt-mark unhold xserver-xorg-video-dummy || true 2>/dev/null +fi + install xorg xserver-xorg-video-dummy # Compile croutonfbserver compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ libx11-dev libxfixes-dev libxdamage-dev libxext-dev libxtst-dev -tmp="`mktemp crouton.XXXXXX --tmpdir=/tmp`" -addtrap "rm -f '$tmp'" - -awk '/###MODELINES###/{exit}; 1' /etc/crouton/xorg-dummy.conf > "$tmp" - -# Generate a set of reasonable resolutions -echo ' -1280 800 l # Early models -1366 768 l # HD -768 1366 l # "P"HD -2560 1700 h # Pixel -1280 850 l # Pixel half-res -1536 864 l # Down/upscaled resolution -1920 1080 l # FHD -1920 1200 l # Standard 20" -2560 1600 l # Standard 30" -3840 2160 h # 4K -' | while read -r w h flags _; do - if [ -z "$w" ]; then - continue - fi - rate=60 # 60Hz refresh - scale2=2 # 2*DPI scale - case "$flags" in - h) scale2=4;; # 2 - *) scale2=2;; # 1 (low dpi) - esac - # Width: Allow for dock on sides + half-width window - dws="0 $((50*scale2/2)) $(((w-50*scale2/2)/2)) $((w/2))" # Measured: 47 - for dw in $dws; do - nw="$((w-dw))" - # Height: Allow for title bar/dock, or title bar only - # Measured: 28,33,36,75,80,83,85,95 - dhs="0 28 33 36 60 75 85 95 100" - for dh in $dhs; do - nh="$((h-(dh*scale2+1)/2))" - mhz="$((rate*(nw+3)*(nh+3)/1000000))" - echo " Modeline \"${nw}x${nh}\" ${mhz} \ -${nw} $((nw+1)) $((nw+2)) $((nw+3)) \ -${nh} $((nh+1)) $((nh+2)) $((nh+3))" >> "$tmp" - done - done -done - -awk 'p; /###MODELINES###/{p=1}' /etc/crouton/xorg-dummy.conf >> "$tmp" -mv -f "$tmp" /etc/X11/xorg-dummy.conf -chmod 0644 /etc/X11/xorg-dummy.conf -undotrap +ln -sf /etc/crouton/xorg-dummy.conf /etc/X11/ + +# Download the latest xf86-video-dummy package +urlbase="http://xorg.freedesktop.org/releases/individual/driver/" + +DUMMYBUILDTMP="`mktemp -d crouton-cras.XXXXXX --tmpdir=/tmp`" + +addtrap "rm -rf --one-file-system '$DUMMYBUILDTMP'" + +echo "Download Xorg dummy driver..." 1>&2 + +wget -O "$DUMMYBUILDTMP/dummy.tar.bz2" "$urlbase/xf86-video-dummy-0.3.7.tar.bz2" + +install --minimal --asdeps patch gcc libc-dev pkg-config \ + xserver-xorg-dev x11proto-xf86dga-dev + +( + cd "$DUMMYBUILDTMP" + # -m prevents "time stamp is in the future" spam + tar --strip-components=1 -xmf dummy.tar.bz2 + + echo "Patching Xorg dummy driver (xrandr 1.2 support)..." 1>&2 + patch -p1 < + #endif + ++#include "xf86Crtc.h" ++ + /* + * Driver data structures. + */ +@@ -178,6 +180,115 @@ dummySetup(pointer module, pointer opts, int *errmaj, int *errmin) + #endif /* XFree86LOADER */ + + static Bool ++size_valid(ScrnInfoPtr pScrn, int width, int height) ++{ ++ /* Guard against invalid parameters */ ++ if (width == 0 || height == 0 || ++ width > DUMMY_MAX_WIDTH || height > DUMMY_MAX_HEIGHT) ++ return FALSE; ++ ++ /* videoRam is in kb, divide first to avoid 32-bit int overflow */ ++ if ((width*height+1023)/1024*pScrn->bitsPerPixel/8 > pScrn->videoRam) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static Bool ++dummy_xf86crtc_resize(ScrnInfoPtr pScrn, int width, int height) ++{ ++ int old_width, old_height; ++ ++ old_width = pScrn->virtualX; ++ old_height = pScrn->virtualY; ++ ++ if (size_valid(pScrn, width, height)) { ++ PixmapPtr rootPixmap; ++ ScreenPtr pScreen = pScrn->pScreen; ++ ++ pScrn->virtualX = width; ++ pScrn->virtualY = height; ++ ++ rootPixmap = pScreen->GetScreenPixmap(pScreen); ++ if (!pScreen->ModifyPixmapHeader(rootPixmap, width, height, ++ -1, -1, -1, NULL)) { ++ pScrn->virtualX = old_width; ++ pScrn->virtualY = old_height; ++ return FALSE; ++ } ++ ++ pScrn->displayWidth = rootPixmap->devKind / ++ (rootPixmap->drawable.bitsPerPixel / 8); ++ ++ return TRUE; ++ } else { ++ return FALSE; ++ } ++} ++ ++static const xf86CrtcConfigFuncsRec dummy_xf86crtc_config_funcs = { ++ dummy_xf86crtc_resize ++}; ++ ++static xf86OutputStatus ++dummy_output_detect(xf86OutputPtr output) ++{ ++ return XF86OutputStatusConnected; ++} ++ ++static int ++dummy_output_mode_valid(xf86OutputPtr output, DisplayModePtr pMode) ++{ ++ if (size_valid(output->scrn, pMode->HDisplay, pMode->VDisplay)) { ++ return MODE_OK; ++ } else { ++ return MODE_MEM; ++ } ++} ++ ++static DisplayModePtr ++dummy_output_get_modes(xf86OutputPtr output) ++{ ++ return NULL; ++} ++ ++static void ++dummy_output_dpms(xf86OutputPtr output, int dpms) ++{ ++ return; ++} ++ ++static const xf86OutputFuncsRec dummy_output_funcs = { ++ .detect = dummy_output_detect, ++ .mode_valid = dummy_output_mode_valid, ++ .get_modes = dummy_output_get_modes, ++ .dpms = dummy_output_dpms, ++}; ++ ++static Bool ++dummy_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, ++ Rotation rotation, int x, int y) ++{ ++ crtc->mode = *mode; ++ crtc->x = x; ++ crtc->y = y; ++ crtc->rotation = rotation; ++ ++ return TRUE; ++} ++ ++static void ++dummy_crtc_dpms(xf86CrtcPtr output, int dpms) ++{ ++ return; ++} ++ ++static const xf86CrtcFuncsRec dummy_crtc_funcs = { ++ .set_mode_major = dummy_crtc_set_mode_major, ++ .dpms = dummy_crtc_dpms, ++}; ++ ++static Bool + DUMMYGetRec(ScrnInfoPtr pScrn) + { + /* +@@ -283,6 +394,8 @@ DUMMYPreInit(ScrnInfoPtr pScrn, int flags) + DUMMYPtr dPtr; + int maxClock = 230000; + GDevPtr device = xf86GetEntityInfo(pScrn->entityList[0])->device; ++ xf86OutputPtr output; ++ xf86CrtcPtr crtc; + + if (flags & PROBE_DETECT) + return TRUE; +@@ -346,13 +459,6 @@ DUMMYPreInit(ScrnInfoPtr pScrn, int flags) + if (!xf86SetDefaultVisual(pScrn, -1)) + return FALSE; + +- if (pScrn->depth > 1) { +- Gamma zeros = {0.0, 0.0, 0.0}; +- +- if (!xf86SetGamma(pScrn, zeros)) +- return FALSE; +- } +- + xf86CollectOptions(pScrn, device->options); + /* Process the options */ + if (!(dPtr->Options = malloc(sizeof(DUMMYOptions)))) +@@ -382,64 +488,45 @@ DUMMYPreInit(ScrnInfoPtr pScrn, int flags) + maxClock); + } + +- pScrn->progClock = TRUE; +- /* +- * Setup the ClockRanges, which describe what clock ranges are available, +- * and what sort of modes they can be used for. +- */ +- clockRanges = (ClockRangePtr)xnfcalloc(sizeof(ClockRange), 1); +- clockRanges->next = NULL; +- clockRanges->ClockMulFactor = 1; +- clockRanges->minClock = 11000; /* guessed §§§ */ +- clockRanges->maxClock = 300000; +- clockRanges->clockIndex = -1; /* programmable */ +- clockRanges->interlaceAllowed = TRUE; +- clockRanges->doubleScanAllowed = TRUE; +- +- /* Subtract memory for HW cursor */ +- +- +- { +- int apertureSize = (pScrn->videoRam * 1024); +- i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, +- pScrn->display->modes, clockRanges, +- NULL, 256, DUMMY_MAX_WIDTH, +- (8 * pScrn->bitsPerPixel), +- 128, DUMMY_MAX_HEIGHT, pScrn->display->virtualX, +- pScrn->display->virtualY, apertureSize, +- LOOKUP_BEST_REFRESH); +- +- if (i == -1) +- RETURN; +- } ++ xf86CrtcConfigInit(pScrn, &dummy_xf86crtc_config_funcs); ++ ++ xf86CrtcSetSizeRange(pScrn, 256, 256, DUMMY_MAX_WIDTH, DUMMY_MAX_HEIGHT); ++ ++ crtc = xf86CrtcCreate(pScrn, &dummy_crtc_funcs); ++ ++ output = xf86OutputCreate (pScrn, &dummy_output_funcs, "default"); ++ ++ output->possible_crtcs = 0x7f; + +- /* Prune the modes marked as invalid */ +- xf86PruneDriverModes(pScrn); ++ xf86InitialConfiguration(pScrn, TRUE); ++ ++ if (pScrn->depth > 1) { ++ Gamma zeros = {0.0, 0.0, 0.0}; ++ ++ if (!xf86SetGamma(pScrn, zeros)) ++ return FALSE; ++ } + +- if (i == 0 || pScrn->modes == NULL) { ++ if (pScrn->modes == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); + RETURN; + } + +- /* +- * Set the CRTC parameters for all of the modes based on the type +- * of mode, and the chipset's interlace requirements. +- * +- * Calling this is required if the mode->Crtc* values are used by the +- * driver and if the driver doesn't provide code to set them. They +- * are not pre-initialised at all. +- */ +- xf86SetCrtcForModes(pScrn, 0); +- + /* Set the current mode to the first in the list */ + pScrn->currentMode = pScrn->modes; + +- /* Print the list of modes being used */ +- xf86PrintModes(pScrn); ++ /* Set default mode in CRTC */ ++ crtc->funcs->set_mode_major(crtc, pScrn->currentMode, RR_Rotate_0, 0, 0); + + /* If monitor resolution is set on the command line, use it */ + xf86SetDpi(pScrn, 0, 0); + ++ /* Set monitor size based on DPI */ ++ output->mm_width = pScrn->xDpi > 0 ? ++ (pScrn->virtualX * 254 / (10*pScrn->xDpi)) : 0; ++ output->mm_height = pScrn->yDpi > 0 ? ++ (pScrn->virtualY * 254 / (10*pScrn->yDpi)) : 0; ++ + if (xf86LoadSubModule(pScrn, "fb") == NULL) { + RETURN; + } +@@ -559,6 +646,8 @@ DUMMYScreenInit(SCREEN_INIT_ARGS_DECL) + + if (!miSetPixmapDepths ()) return FALSE; + ++ pScrn->displayWidth = pScrn->virtualX; ++ + /* + * Call the framebuffer layer's ScreenInit function, and fill in other + * pScreen fields. +@@ -597,23 +686,6 @@ DUMMYScreenInit(SCREEN_INIT_ARGS_DECL) + if (dPtr->swCursor) + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Software Cursor.\n"); + +- { +- +- +- BoxRec AvailFBArea; +- int lines = pScrn->videoRam * 1024 / +- (pScrn->displayWidth * (pScrn->bitsPerPixel >> 3)); +- AvailFBArea.x1 = 0; +- AvailFBArea.y1 = 0; +- AvailFBArea.x2 = pScrn->displayWidth; +- AvailFBArea.y2 = lines; +- xf86InitFBManager(pScreen, &AvailFBArea); +- +- xf86DrvMsg(pScrn->scrnIndex, X_INFO, +- "Using %i scanlines of offscreen memory \n" +- , lines - pScrn->virtualY); +- } +- + xf86SetBackingStore(pScreen); + xf86SetSilkenMouse(pScreen); + +@@ -640,6 +712,9 @@ DUMMYScreenInit(SCREEN_INIT_ARGS_DECL) + | CMAP_RELOAD_ON_MODE_SWITCH)) + return FALSE; + ++ if (!xf86CrtcScreenInit(pScreen)) ++ return FALSE; ++ + /* DUMMYInitVideo(pScreen); */ + + pScreen->SaveScreen = DUMMYSaveScreen; +EOF + + # Fake version 0.3.8 + package="xf86-video-dummy" + major='0' + minor='3' + patch='8' + version="$major.$minor.$patch" + + sed -e ' + s/#undef \(HAVE_.*\)$/#define \1 1/ + s/#undef \(USE_.*\)$/#define \1 1/ + s/#undef \(STDC_HEADERS\)$/#define \1 1/ + s/#undef \(.*VERSION\)$/#define \1 "'$version'"/ + s/#undef \(.*VERSION_MAJOR\)$/#define \1 '$major'/ + s/#undef \(.*VERSION_MINOR\)$/#define \1 '$minor'/ + s/#undef \(.*VERSION_PATCHLEVEL\)$/#define \1 '$patch'/ + s/#undef \(.*PACKAGE_STRING\)$/#define \1 "'"$package $version"'"/ + s/#undef \(.*PACKAGE_*\)$/#define \1 "'$package'"/ + ' config.h.in > config.h + + echo "Compiling Xorg dummy driver..." 1>&2 + + cd src + # Convert Makefile.am to a shell script, and run it. + { + echo ' + DGA=1 + CFLAGS="-std=gnu99 -O2 -g -DHAVE_CONFIG_H -I.. -I." + XORG_CFLAGS="'"`pkg-config --cflags xorg-server`"'" + ' + + convert_automake + + echo ' + buildlib dummy_drv + ' + } | sh -s -e $SETOPTIONS + + echo "Installing Xorg dummy driver..." 1>&2 + + DRIVERDIR="/usr/lib/xorg/modules/drivers" + mkdir -p "$DRIVERDIR/" + /usr/bin/install -s dummy_drv.so "$DRIVERDIR/" +) # End compilation subshell + +if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then + # Hold xserver-xorg-video-dummy to make sure the driver does not get erased + apt-mark hold xserver-xorg-video-dummy +fi TIPS="$TIPS"' You can open your running chroot desktops by clicking on the extension icon. From 648b52a260321cb1e969c024a7fec6596b1ddabf Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 13 Nov 2014 10:59:59 -0800 Subject: [PATCH 099/334] tests/37-xiwi: Very basic xiwi test This test is incomplete, as it is not able to connect to the extension, so xinit parameter is never run. However, this tests target installation, and verifies that there is no critical X failures (e.g. X server cannot start). --- test/tests/37-xiwi | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/tests/37-xiwi diff --git a/test/tests/37-xiwi b/test/tests/37-xiwi new file mode 100644 index 000000000..212b7efd3 --- /dev/null +++ b/test/tests/37-xiwi @@ -0,0 +1,22 @@ +#!/bin/sh -e +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# All supported releases should be able to launch an xiwi server +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +# FIXME: This test is incomplete, as it is not able to connect to the +# extension, so xinit parameter is never run. However, this tests target +# installation, and verifies that there is no critical X failures (e.g. +# X server cannot start). + +snapshot "$release" audio +crouton -U -n "$release" -t xiwi +# FIXME: When x11test is merged, this should be run under vtlock +exitswithin 0 60 host \ + enter-chroot -n "$release" exec xinit /usr/bin/xterm -e true +host delete-chroot -y "$release" From 7902469822201d629bcd1cedd457d0d4ba9b636b Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 13 Nov 2014 12:53:36 -0800 Subject: [PATCH 100/334] xiwi: Fetch .gz archives instead of bz2 Debian does not install bzip2 by default. --- targets/xiwi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/xiwi b/targets/xiwi index 11b6ea5ef..99cc1ca64 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -34,7 +34,7 @@ addtrap "rm -rf --one-file-system '$DUMMYBUILDTMP'" echo "Download Xorg dummy driver..." 1>&2 -wget -O "$DUMMYBUILDTMP/dummy.tar.bz2" "$urlbase/xf86-video-dummy-0.3.7.tar.bz2" +wget -O "$DUMMYBUILDTMP/dummy.tar.gz" "$urlbase/xf86-video-dummy-0.3.7.tar.gz" install --minimal --asdeps patch gcc libc-dev pkg-config \ xserver-xorg-dev x11proto-xf86dga-dev @@ -42,7 +42,7 @@ install --minimal --asdeps patch gcc libc-dev pkg-config \ ( cd "$DUMMYBUILDTMP" # -m prevents "time stamp is in the future" spam - tar --strip-components=1 -xmf dummy.tar.bz2 + tar --strip-components=1 -xmf dummy.tar.gz echo "Patching Xorg dummy driver (xrandr 1.2 support)..." 1>&2 patch -p1 < Date: Tue, 18 Nov 2014 09:39:42 -0800 Subject: [PATCH 101/334] daemon: Do not generate test tarball; use fixed version Version with checksum 811e9713f6357ee3996cb7cce80a3e2b has not changed in a month, fetch that until R39 goes stable, then we'll be able to remove it. --- test/daemon.sh | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/test/daemon.sh b/test/daemon.sh index eb2d67db2..a113b0344 100755 --- a/test/daemon.sh +++ b/test/daemon.sh @@ -38,8 +38,9 @@ MIRRORENV="" # Maximum test run time (minutes): 24 hours MAXTESTRUNTIME="$((24*60))" GSAUTOTEST="gs://chromeos-autotest-results" -# FIXME: Remove this when test is merged -GSCROUTONTEST="gs://drinkcat-crouton/crouton-test/packages" +# FIXME: Remove this when test is merged (>=R39): a temporary hack fetches +# the crouton test from gs://drinkcat-crouton/crouton-test/packages +TESTCSUM="811e9713f6357ee3996cb7cce80a3e2b" USAGE="$APPLICATION [options] -q QUEUEURL @@ -250,15 +251,6 @@ DUTSSHOPTIONS="-o ConnectTimeout=30 -o IdentityFile=$SSHKEY \ -o ControlPath=$TMPROOT/ssh/%h \ -o ControlMaster=auto -o ControlPersist=10m" -# FIXME: Remove this when test is merged -echo "Building latest test-plaform_Crouton tarball..." 1>&2 -tar cvfj "$TMPROOT/test-platform_Crouton.tar.bz2" \ - -C "$AUTOTESTROOT/client/site_tests/platform_Crouton" . --exclude='*~' -( cd "$TMPROOT"; md5sum test-platform_Crouton.tar.bz2 > packages.checksum ) -TESTCSUM="`cat "$TMPROOT/packages.checksum" | cut -d' ' -f 1`" -gsutil cp "$TMPROOT/test-platform_Crouton.tar.bz2" \ - "$TMPROOT/packages.checksum" "$GSCROUTONTEST-$TESTCSUM/" - syncstatus while sleep "$POLLINTERVAL"; do From 0708c122fc574554e2115041e1b5d862532f634e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 18 Nov 2014 11:36:02 -0800 Subject: [PATCH 102/334] Fix syntax error in Unity targets --- targets/unity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/unity b/targets/unity index b2e5ec8a6..301ea9261 100644 --- a/targets/unity +++ b/targets/unity @@ -5,7 +5,7 @@ if [ -z "$TARGETNOINSTALL" ]; then if [ "$DISTRO" != 'ubuntu' ]; then error 99 "unity target is only supported on Ubuntu." - elif [ -a ! "${ARCH#arm}" = "$ARCH" ] && ! release -eq precise; then + elif [ "${ARCH#arm}" != "$ARCH" ] && ! release -eq precise; then error 99 "unity target does not work in xephyr in $RELEASE due to missing egl support." fi fi From f11567b0e9cacd5090f43adcf48e85c215d703ad Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 18 Nov 2014 14:33:52 -0800 Subject: [PATCH 103/334] kiwi: Make sure Kiwi modules takes the entire space on startup This removes a potential race on startup, where the module was still using its original size (800x600), and does not have time to tell the extension to resize it to maximum size. Example trace: ``` resize! 1280/834 => JS tells Kiwi to resize debug:(0) -2147483648 InitContext 800x600s1 => Initialized with default size resize:1280/834 => Kiwi tells JS to resize element status:Connecting... debug:(2) -2147483648 message=hidpi:0 status:Connected. connected:Version received debug:(1) -2147483648 Asked for resolution 800x600 => Kiwi asks for wrong resolution debug:(0) 0 fps: 0 (0) delay: -2147483648 deltat: -2147483648 target fps: 30 800x600 debug:(0) 75 InitContext 1280x834s1 => Kiwi gets notified of element resize only here debug:(2) 100 receive (binary): R => croutonfbserver tells Kiwi 800x600 is ok resize:800/600 => Kiwi tells JS to resize down to 800x600 debug:(0) 131 InitContext 800x600s1 ``` --- host-ext/crouton/window.html | 2 +- host-ext/crouton/window.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 643fe66bd..14e507d04 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -60,7 +60,7 @@
Initializing...
diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 2277b87cf..b1efef69b 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -30,10 +30,10 @@ function registerWindow(register) { function moduleDidLoad() { KiwiModule_ = document.getElementById('kiwi'); updateStatus('Starting...'); - kiwiResize(); - KiwiModule_.postMessage('display:' + display_); KiwiModule_.postMessage('debug:' + debug_); KiwiModule_.postMessage('hidpi:' + hidpi_); + /* Sending the display command triggers a connection: send it last. */ + KiwiModule_.postMessage('display:' + display_); KiwiModule_.focus(); } From 48d0fc7b689df97d289be31f9ba8622dc948098f Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 19 Nov 2014 09:42:19 -0800 Subject: [PATCH 104/334] setres: Use named modes, instead of a temporary mode hack This should be more reliable altogether, and avoids unecessary mode switches. --- chroot-bin/setres | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/chroot-bin/setres b/chroot-bin/setres index 2ac0b04ba..9ec934c69 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -57,22 +57,32 @@ replacemode() { # Try to change to arbitrary resolution mhz="$((r*x*y/1000000))" -name="Crouton in a tab" +name="kiwi_${x}x${y}_${r}" -# Tweak the temp mode so xrandr knows the 2 modes are different, and -# switches away from the temp mode. -replacemode "$o" "$name (tmp)" $mhz $x $x $x $((x+1)) $y $y $y $y +# Try to switch mode, if it already exists. +if xrandr --output "$o" --mode "$name" 2>/dev/null; then + echo "${x}x${y}_${r}" + exit 0 +fi + +# Add the new mode +xrandr --newmode "$name" $mhz $x $x $x $x $y $y $y $y +xrandr --addmode "$o" "$name" -# This fails on non-patched xorg-dummy -if xrandr --output "$o" --mode "$name (tmp)"; then - replacemode "$o" "$name" $mhz $x $x $x $x $y $y $y $y - xrandr --output "$o" --mode "$name" - replacemode "$o" "$name (tmp)" +# The next line fails on non-patched xorg-dummy +if xrandr --output "$o" --mode "$name"; then + # Success: remove old modes + others="`xrandr | sed -n 's/^.*\(kiwi[0-9x_]*\)[^*]*$/\1/p'`" + for othername in $others; do + xrandr --delmode "$o" "$othername" 2>/dev/null || true + xrandr --rmmode "$othername" 2>/dev/null || true + done echo "${x}x${y}_${r}" exit 0 else - # Delete the temp mode - replacemode "$o" "$name (tmp)" + # Delete the new mode + xrandr --delmode "$o" "$name" 2>/dev/null || true + xrandr --rmmode "$name" 2>/dev/null || true fi # Setting a custom mode failed, fall back on best match From e683b03fa497e46f5b55d59ca17e895358f68519 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Fri, 21 Nov 2014 22:00:40 +0800 Subject: [PATCH 105/334] Added instructions to enter developer mode. Link to wiki on chromium.org --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 292770f47..bd03d02f2 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ to the rest of Chromium OS. Prerequisites ------------- You need a device running Chromium OS that has been switched to developer mode. + +**How to enter developer mode?** Go to [http://goo.gl/r4Qe4O](http://goo.gl/r4Qe4O) and click on your device model, then you will see the instruction. + Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your data. crouton does support encrypting chroots, but the encryption is only as From 74f727f4b0dcde9e4a8e858e3fdb4d15aff2fdd1 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 21 Nov 2014 08:37:25 -0800 Subject: [PATCH 106/334] test/w0-common: Use croutoncycle to detect x11 server started --- test/tests/w0-common | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/test/tests/w0-common b/test/tests/w0-common index 79db55eb5..befe2d403 100644 --- a/test/tests/w0-common +++ b/test/tests/w0-common @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# This test is sourced by other w* test, do not run it on it's own +# This test is sourced by other w* test, do not run it on its own if [ -z "$release" -o -z "$target" ]; then exit 0 fi @@ -34,20 +34,31 @@ fi ( vtlock host "$startcmd" -b -n "$release" - # FIXME: Wait for croutoncycle to change? - sleep 60 host enter-chroot -n "$release" sh -exc ' + timeout=60 + while [ "$timeout" -gt 0 ]; do + timeout="$((timeout - 5))" + sleep 5 + DISPLAY="`croutoncycle display`" + if [ "$DISPLAY" != "aura" -a "$DISPLAY" != ":0" ]; then + break + fi + done # Test croutoncycle as a bonus - export DISPLAY="`croutoncycle display`" if [ "$DISPLAY" = ":0" -o "$DISPLAY" = "aura" ]; then echo "Invalid display ($DISPLAY)." 1>&2 exit 1 fi + # Let WM/DE settle, then play xte sequence + sleep 30 + export DISPLAY if [ -n "'"$xte"'" ]; then echo "'"$xte"'" | tr ";" "\n" | xte - sleep 60 fi - # Snapshot! + # Let WM/DE settle again + sleep 30 + # Snapshot! We use xwd, as import does not snapshot obscured windows + # correctly (dialogs end up as black squares) xwd -root | convert -quality 75 xwd:- ~/"screenshot-'"$target"'.jpg"' mv "$PREFIX/chroots/$release/home/test/screenshot-$target.jpg" \ From e9be08ecb8a75771ed62f8af7c3552b3fea455de Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sat, 22 Nov 2014 17:41:54 +0800 Subject: [PATCH 107/334] Fixed the language of README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd03d02f2..f7bf6f38c 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Prerequisites ------------- You need a device running Chromium OS that has been switched to developer mode. -**How to enter developer mode?** Go to [http://goo.gl/r4Qe4O](http://goo.gl/r4Qe4O) and click on your device model, then you will see the instruction. +For instructions on how to do that, go to [this Chromium OS wiki page](http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), click on your device model and follow the steps in the **Entering Developer Mode** section. Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your From 476671d0db3441408d52fde1376126a93823d74b Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 23 Nov 2014 09:39:35 +0800 Subject: [PATCH 108/334] change style from bold to italic --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7bf6f38c..074abbfcb 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Prerequisites ------------- You need a device running Chromium OS that has been switched to developer mode. -For instructions on how to do that, go to [this Chromium OS wiki page](http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), click on your device model and follow the steps in the **Entering Developer Mode** section. +For instructions on how to do that, go to [this Chromium OS wiki page](http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), click on your device model and follow the steps in the *Entering Developer Mode* section. Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your From 2dfaa763c190cf297983172205360d2d90595985 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 22 Nov 2014 18:07:36 -0800 Subject: [PATCH 109/334] Update CLA link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 292770f47..2c3366818 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,7 @@ inside the chroot (if possible). I want to be a Contributor! --------------------------- That's great! But before your code can be merged, you'll need to have signed -the [Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual#sign-electronically). +the [Individual Contributor License Agreement](https://cla.developers.google.com/clas/new?kind=KIND_INDIVIDUAL&domain=DOMAIN_GOOGLE). Don't worry, it only takes a minute and you'll definitely get to keep your firstborn, probably. If you've already signed it for contributing to Chromium or Chromium OS, you're already done. From 15e406aacd0ae5b46f65cfd2eca15b26f9bc4adf Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 23 Nov 2014 10:40:06 +0800 Subject: [PATCH 110/334] wrap the text --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 074abbfcb..e51c048cf 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,10 @@ Prerequisites ------------- You need a device running Chromium OS that has been switched to developer mode. -For instructions on how to do that, go to [this Chromium OS wiki page](http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), click on your device model and follow the steps in the *Entering Developer Mode* section. +For instructions on how to do that, go to [this Chromium OS wiki +page](http://www.chromium.org/chromium-os/developer-information-for- +chrome-os-devices), click on your device model and follow the steps in +the *Entering Developer Mode* section. Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your From 24f7108a34ddcc152c5756ac06554fb696d9d66c Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 23 Nov 2014 12:18:08 +0800 Subject: [PATCH 111/334] fix link --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e51c048cf..be109b6fb 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ Prerequisites ------------- You need a device running Chromium OS that has been switched to developer mode. -For instructions on how to do that, go to [this Chromium OS wiki -page](http://www.chromium.org/chromium-os/developer-information-for- -chrome-os-devices), click on your device model and follow the steps in -the *Entering Developer Mode* section. +For instructions on how to do that, go to [this Chromium OS wiki page] +(http://www.chromium.org/chromium-os/developer-information-for-chrome-os-devices), +click on your device model and follow the steps in the *Entering Developer Mode* +section. Note that developer mode, in its default configuration, is *completely insecure*, so don't expect a password in your chroot to keep anyone from your From e22c2d158fd8b36885bc671861b915a096438cfc Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Tue, 25 Nov 2014 12:06:56 +0800 Subject: [PATCH 112/334] Add instruction for getting system info in Crosh. Refer to the instruction given by @drinkcat in https://github.com/drinkcat/chroagh/issues/54#issuecomment-58771224 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c80b2c21..337b14942 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,9 @@ Running another OS in a chroot is a pretty messy technique (although it's hidden behind very pretty scripts), and these scripts are relatively new, so problems are not surprising. Check the issue tracker and file a bug if your issue isn't there. When filing a new bug, include the output of `croutonversion` run from -inside the chroot (if possible). +inside the chroot or, if you cannot mount your chroot, include the output +of `cat +/etc/lsb-release` in the Crosh shell. I want to be a Contributor! From cd4b43ddbf9baf4cfb0b3d82dea1c1230236046e Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 21:15:45 +0800 Subject: [PATCH 113/334] Add instruction for getting system info in Crosh. This time in the contributing section of Readme. Refer to the instruction given by @drinkcat in https://github.com/drinkcat/chroagh/issues/54#issuecomment-58771224 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 337b14942..ed077b377 100644 --- a/README.md +++ b/README.md @@ -251,7 +251,8 @@ But how? There's a way For Everyone to help! * Something broken? File a bug! Bonus points if you try to fix it. It helps if - you provide the output of `croutonversion` when you submit the bug. + you provide the output of `croutonversion`(Or the output of + `cat /etc/lsb-release` from Crosh) when you submit the bug. * Want to try and break something? Look through [requests for testing](https://github.com/dnschneid/crouton/issues?labels=needstesting&state=open) and then do your best to brutally rip the author's work to shreds. * Look through [open issues](https://github.com/dnschneid/crouton/issues?state=open) From dd984f9c11b908005d534deaad35175fc0060613 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 21:18:50 +0800 Subject: [PATCH 114/334] Reword 4bae48457043ebc8f420611311cb91a3844ec324 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed077b377..16d75eed4 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ are not surprising. Check the issue tracker and file a bug if your issue isn't there. When filing a new bug, include the output of `croutonversion` run from inside the chroot or, if you cannot mount your chroot, include the output of `cat -/etc/lsb-release` in the Crosh shell. +/etc/lsb-release` from Crosh. I want to be a Contributor! From 9b17c4169d97429bc3e373421eac64f03564d1ca Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 21:23:58 +0800 Subject: [PATCH 115/334] Move instruction for encrypting existing chroots --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 16d75eed4..ca10c798c 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,8 @@ Examples 6. Exit the chroot by logging out of Xfce. ### With encryption! - 1. Add the `-e` parameter when you run crouton to create an encrypted chroot. + 1. Add the `-e` parameter when you run crouton to create an encrypted chroot + or encrypt a non-encrypted chroot. 2. You can get some extra protection on your chroot by storing the decryption key separately from the place the chroot is stored. Use the `-k` parameter to specify a file or directory to store the keys in (such as a USB drive or @@ -131,7 +132,6 @@ Examples to see what those parameters actually do). 2. Exit the chroot and run `sudo sh ~/Downloads/crouton -u -n chrootname`. It will update all installed targets. - 3. You can use this with `-e` to encrypt a non-encrypted chroot, but make sure you don't interrupt the operation. ### A backup a day keeps the price-gouging data restoration services away From de2cda21474a5789e556cb11fe17e70e4622dba1 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 22:03:05 +0800 Subject: [PATCH 116/334] Add notice for avoiding perserving ownership issue See issue #903 --- host-bin/edit-chroot | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index effadd406..f27b72f8c 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -53,6 +53,9 @@ Options: directory, or an absolute path to move it entirely. DEST can be a directory, in which case it must end in a slash. If multiple chroots are specified, DEST must be a directory. + If you are moving a chroot to a SD card/USB drive, make sure the + storage is formatted to one of the types that work. See + http://goo.gl/ptmfH6 -r Restores a chroot from a tarball. The tarball path can be specified with -f or detected from name. If both are specified, restores to that name instead of the one in the tarball. From 213988702aea9509edeb67e648e78abc4fac0f33 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 22:20:46 +0800 Subject: [PATCH 117/334] Update tip in Readme Tell the users right away that they can disbale the proxy setting using -P option. In case some users get panic, so they do not read the descriptions carefully, when their proxies do not work and want to disable them but just cannot find out how. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca10c798c..b291ae22e 100644 --- a/README.md +++ b/README.md @@ -190,7 +190,7 @@ Tips * Chroots are cheap! Create multiple ones using `-n`, break them, then make new, better ones! * You can change the distro mirror from the default by using `-m` - * Behind a proxy? `-P` lets you specify one. + * Want to use a proxy? `-P` lets you specify one (or disable it). * A script is installed in your chroot called `brightness`. You can assign this to keyboard shortcuts to adjust the brightness of the screen (e.g. `brightness up`) or keyboard (e.g. `brightness k down`). From 50e9a175b0878f0c9c864aad05271af023eb969b Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Sun, 7 Dec 2014 23:16:51 +0800 Subject: [PATCH 118/334] Add the type a usb/sd should be formatted for -m --- host-bin/edit-chroot | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index f27b72f8c..f48418179 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -54,8 +54,7 @@ Options: DEST can be a directory, in which case it must end in a slash. If multiple chroots are specified, DEST must be a directory. If you are moving a chroot to a SD card/USB drive, make sure the - storage is formatted to one of the types that work. See - http://goo.gl/ptmfH6 + storage is formatted to ext2/3/4. -r Restores a chroot from a tarball. The tarball path can be specified with -f or detected from name. If both are specified, restores to that name instead of the one in the tarball. From 78869442e97bf5fe671504c1d2514571cd359a0c Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Mon, 8 Dec 2014 07:04:19 +0800 Subject: [PATCH 119/334] fix errors --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b291ae22e..03f9853af 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,8 @@ Examples ### With encryption! 1. Add the `-e` parameter when you run crouton to create an encrypted chroot - or encrypt a non-encrypted chroot. + or encrypt a non-encrypted chroot, but make sure you don't interrupt the + intergration. 2. You can get some extra protection on your chroot by storing the decryption key separately from the place the chroot is stored. Use the `-k` parameter to specify a file or directory to store the keys in (such as a USB drive or @@ -132,7 +133,6 @@ Examples to see what those parameters actually do). 2. Exit the chroot and run `sudo sh ~/Downloads/crouton -u -n chrootname`. It will update all installed targets. - you don't interrupt the operation. ### A backup a day keeps the price-gouging data restoration services away 1. `sudo edit-chroot -b chrootname` backs up your chroot to a timestamped @@ -251,7 +251,7 @@ But how? There's a way For Everyone to help! * Something broken? File a bug! Bonus points if you try to fix it. It helps if - you provide the output of `croutonversion`(Or the output of + you provide the output of `croutonversion` (or the output of `cat /etc/lsb-release` from Crosh) when you submit the bug. * Want to try and break something? Look through [requests for testing](https://github.com/dnschneid/crouton/issues?labels=needstesting&state=open) and then do your best to brutally rip the author's work to shreds. From 36bea0ef4c38cf828ab9a8faf149bd35986452a2 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Mon, 8 Dec 2014 07:52:21 +0800 Subject: [PATCH 120/334] fix errors --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 03f9853af..7e8ddc25c 100644 --- a/README.md +++ b/README.md @@ -106,8 +106,7 @@ Examples ### With encryption! 1. Add the `-e` parameter when you run crouton to create an encrypted chroot - or encrypt a non-encrypted chroot, but make sure you don't interrupt the - intergration. + or encrypt a non-encrypted chroot. 2. You can get some extra protection on your chroot by storing the decryption key separately from the place the chroot is stored. Use the `-k` parameter to specify a file or directory to store the keys in (such as a USB drive or @@ -214,8 +213,7 @@ behind very pretty scripts), and these scripts are relatively new, so problems are not surprising. Check the issue tracker and file a bug if your issue isn't there. When filing a new bug, include the output of `croutonversion` run from inside the chroot or, if you cannot mount your chroot, include the output -of `cat -/etc/lsb-release` from Crosh. +of `cat /etc/lsb-release` from Crosh. I want to be a Contributor! From 7b8ffff03e0552a9b695df43e0284f0d66d20a23 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 8 Dec 2014 10:03:02 +0800 Subject: [PATCH 121/334] Update CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 38f76de20..646441e11 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,4 +17,5 @@ Nicolas Boichat Stephen Barber Steven Maude Tobbe Lundberg +Tony Xue Yuri Pole From 7c8605433ea4a0592fcc378325434683273c5daf Mon Sep 17 00:00:00 2001 From: divx118 Date: Wed, 10 Dec 2014 20:49:28 +0100 Subject: [PATCH 122/334] Fix for hotkeys ctr-shift-alt F1/F2 xbmc in fullscreen. --- chroot-etc/xbmc-cycle.py | 17 +++++++++++++++++ chroot-etc/xbmc-keyboard.xml | 13 +++++++++++++ targets/xbmc | 20 ++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 chroot-etc/xbmc-cycle.py create mode 100644 chroot-etc/xbmc-keyboard.xml diff --git a/chroot-etc/xbmc-cycle.py b/chroot-etc/xbmc-cycle.py new file mode 100644 index 000000000..92afd2eb1 --- /dev/null +++ b/chroot-etc/xbmc-cycle.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Python script to call croutoncycle. This is needed to let the +# hotkeys ctr-shift-alt F1/F2 work when xbmc is in fullscreen. +import os +import sys +try: + arg = str(sys.argv[1]) +except: + arg = "" +if arg in ("prev", "next"): + os.system("/usr/local/bin/croutoncycle %s" % arg) +else: + print ("Usage: %s prev|next" % str(sys.argv[0])) diff --git a/chroot-etc/xbmc-keyboard.xml b/chroot-etc/xbmc-keyboard.xml new file mode 100644 index 000000000..95ad439b7 --- /dev/null +++ b/chroot-etc/xbmc-keyboard.xml @@ -0,0 +1,13 @@ + + + + + + + + + RunScript(/etc/crouton/xbmc-cycle.py,prev) + RunScript(/etc/crouton/xbmc-cycle.py,next) + + + diff --git a/targets/xbmc b/targets/xbmc index bcf279efd..48a128efd 100644 --- a/targets/xbmc +++ b/targets/xbmc @@ -5,11 +5,31 @@ REQUIRES='x11' DESCRIPTION='Installs the XBMC media player. (Approx. 140MB)' HOSTBIN='startxbmc' +CHROOTETC='xbmc-keyboard.xml xbmc-cycle.py' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: install xbmc +# Configure keymaps xbmc for the hotkeys ctr-shift-alt F1/F2 to +# cycle through chroots/chromeos. We use ~/.xbmc/userdata/keymaps/keyboard.xml +# for this purpose, but the main user may not have been created yet, so we +# add a script in /etc/profile.d to link ~/.xbmc/userdata/keymaps/keyboard.xml +# to /etc/crouton/xbmc-keyboard.xml + +profiledsh='/etc/profile.d/crouton-xbmc-keymaps.sh' +# Make sure symbolic link is setup on login +echo '#!/bin/sh + +keyboardxmldir="$HOME/.xbmc/userdata/keymaps" +# Do not install if user is root, or $HOME does not exist +if [ "`id -u`" -ne 0 -a -d "$HOME" -a ! -e "$keyboardxmldir" ]; then + mkdir -p "$keyboardxmldir" + ln -sfT /etc/crouton/xbmc-keyboard.xml "$keyboardxmldir/keyboard.xml" +fi' > "$profiledsh" + +chmod 755 "$profiledsh" + TIPS="$TIPS You can start XBMC via the startxbmc host command: sudo startxbmc " From bd27c6b35910802d3bbb83a00f3ac3782030b354 Mon Sep 17 00:00:00 2001 From: Mikito Takada Date: Wed, 10 Dec 2014 23:00:06 -0800 Subject: [PATCH 123/334] Clarify creating a chroot with a bootstrap tarball Clarify that you also need to specify the `-t` parameter after downloading the bootstrap files. Given that you don't get an error about missing a target when running `-f` without `-t`, it took me quite a while and some googling to figure out why the command in step 4 didn't work; given that it was in the readme and every other command before this didn't need additional parameters, it was surprising that I needed to also add the `-t` parameter. I kind of get the distinction between a bootstrap and a target now that I've done it but I think mentioning it will save someone else from having to figure out what they are doing wrong after literally following the steps in the readme. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e8ddc25c..1de4da8d2 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,8 @@ Examples 3. Include the `-r` parameter if you want to specify for which release to prepare a bootstrap. 4. You can then create chroots using the tarball by running - `sudo sh ~/Downloads/crouton -f ~/Downloads/mybootstrap.tar.bz2` + `sudo sh ~/Downloads/crouton -f ~/Downloads/mybootstrap.tar.bz2`. Make sure + you also specify the target environment with `-t`. *This is the quickest way to create multiple chroots at once, since you won't have to determine and download the bootstrap files every time.* From a1ceeea1ae0e022b463c8f97916b37c1169579b4 Mon Sep 17 00:00:00 2001 From: divx118 Date: Thu, 11 Dec 2014 20:35:15 +0100 Subject: [PATCH 124/334] Removed try/except, using subprocess.call instead of os.system. Write message to stderr. Added sys.exit with return codes, no problems with xbmc when doing that. --- chroot-etc/xbmc-cycle.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chroot-etc/xbmc-cycle.py b/chroot-etc/xbmc-cycle.py index 92afd2eb1..914b76c36 100644 --- a/chroot-etc/xbmc-cycle.py +++ b/chroot-etc/xbmc-cycle.py @@ -5,13 +5,12 @@ # # Python script to call croutoncycle. This is needed to let the # hotkeys ctr-shift-alt F1/F2 work when xbmc is in fullscreen. -import os +import subprocess import sys -try: - arg = str(sys.argv[1]) -except: - arg = "" -if arg in ("prev", "next"): - os.system("/usr/local/bin/croutoncycle %s" % arg) + +if len(sys.argv) == 2 and sys.argv[1] in ("prev", "next"): + exitcode = subprocess.call(["/usr/local/bin/croutoncycle", sys.argv[1]]) else: - print ("Usage: %s prev|next" % str(sys.argv[0])) + sys.stderr.write("Usage: %s prev|next\n" % str(sys.argv[0])) + exitcode = 2 +sys.exit(exitcode) From de8a9f14296f00207971cd9713c29c5706675a59 Mon Sep 17 00:00:00 2001 From: divx118 Date: Fri, 12 Dec 2014 18:12:41 +0100 Subject: [PATCH 125/334] Corrected mistake, check if keyboard.xml exist. --- targets/xbmc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/xbmc b/targets/xbmc index 48a128efd..956114720 100644 --- a/targets/xbmc +++ b/targets/xbmc @@ -23,7 +23,7 @@ echo '#!/bin/sh keyboardxmldir="$HOME/.xbmc/userdata/keymaps" # Do not install if user is root, or $HOME does not exist -if [ "`id -u`" -ne 0 -a -d "$HOME" -a ! -e "$keyboardxmldir" ]; then +if [ "`id -u`" -ne 0 -a -d "$HOME" -a ! -e "$keyboardxmldir/keyboard.xml" ]; then mkdir -p "$keyboardxmldir" ln -sfT /etc/crouton/xbmc-keyboard.xml "$keyboardxmldir/keyboard.xml" fi' > "$profiledsh" From 7da416ae6eab399381f766eb3bb129a24b193d10 Mon Sep 17 00:00:00 2001 From: divx118 Date: Mon, 22 Dec 2014 19:01:58 +0100 Subject: [PATCH 126/334] Added chvt 2, to be sure display also switches when the vt we are changing to is using the fbdev driver. --- chroot-bin/croutoncycle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 7e6c6174a..fd8b84a68 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -224,6 +224,10 @@ else if [ "${dest#[1-9]}" = "$dest" ]; then dest='1' fi + # When the destination we are changing to is using fbdev driver in X, + # we need first a change to vt 2, else only the session switches + # and the display will be stuck on the old vt. + sudo -n chvt 2 sudo -n chvt "$dest" fi From ccef656bb032ba71eba71042efb1651dc6736c90 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Tue, 23 Dec 2014 11:09:31 +0800 Subject: [PATCH 127/334] Create CONTRIBUTING.md --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a607fcd66 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,8 @@ +#For users who want to report an issue +Please read [this wiki page](https://github.com/dnschneid/crouton/wiki/Common-issues-and-reporting) +for instructions on reporting an issue. + +#For contributors +Please read [this +section](https://github.com/dnschneid/crouton#i-want-to-be-a-contributor) +of the README and the following relevant sections first. From db4e1b2230a8e425a96c651eb51949cb4bd7bb53 Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Tue, 23 Dec 2014 11:13:04 +0800 Subject: [PATCH 128/334] change font size for better appearence --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a607fcd66..5805a97d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,8 @@ -#For users who want to report an issue +##For users who want to report an issue Please read [this wiki page](https://github.com/dnschneid/crouton/wiki/Common-issues-and-reporting) for instructions on reporting an issue. -#For contributors +##For contributors Please read [this section](https://github.com/dnschneid/crouton#i-want-to-be-a-contributor) of the README and the following relevant sections first. From 51d12c8fc79c8291a2a9199ab8116a242df2fbff Mon Sep 17 00:00:00 2001 From: Tony Xue Date: Tue, 23 Dec 2014 11:30:00 +0800 Subject: [PATCH 129/334] warp text --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5805a97d0..48c6cc6b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,6 @@ ##For users who want to report an issue -Please read [this wiki page](https://github.com/dnschneid/crouton/wiki/Common-issues-and-reporting) +Please read [this wiki page] +(https://github.com/dnschneid/crouton/wiki/Common-issues-and-reporting) for instructions on reporting an issue. ##For contributors From 5922ed9ca053f1e1210a304798d02b6ea40aa12c Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 17:13:56 -0800 Subject: [PATCH 130/334] Update CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 646441e11..a7a6169ec 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -12,6 +12,7 @@ John Tantalo Maurice van Kruchten Micah Lee Michael Orr +Mikito Takada Nevada Romsdahl Nicolas Boichat Stephen Barber From e2cb0e1ee2430cc9aaa3b821c7718930b71a7bc9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 21:21:54 -0800 Subject: [PATCH 131/334] If unspecified, automatically find a pepper SDK in the source tree. --- .gitignore | 1 + host-ext/nacl_src/Makefile | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cfd03fe10..dfd56e7b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.tar.* .*.swp .*.tmp +nacl_sdk/ /crouton* /tests/run /releases/ diff --git a/host-ext/nacl_src/Makefile b/host-ext/nacl_src/Makefile index 1aea49c33..ef96f9769 100644 --- a/host-ext/nacl_src/Makefile +++ b/host-ext/nacl_src/Makefile @@ -7,7 +7,8 @@ VALID_TOOLCHAINS := pnacl -NACL_SDK_ROOT ?= $(HOME)/nacl_sdk/pepper_37 +NACL_SDK_ROOT ?= $(shell find ../.. -type d -regex '.*/nacl_sdk/pepper_[0-9]+' \ + | sort | head -n1) ../crouton/kiwi.pexe: pnacl/Release/kiwi.pexe cp pnacl/Release/kiwi.pexe ../crouton/kiwi.pexe From fb8b49b1bf055792ccfe37fa04a07ded22faed95 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 21:46:25 -0800 Subject: [PATCH 132/334] Dead code removal. --- chroot-bin/setres | 39 +++----------------------------- src/fbserver.c | 57 ++++++++++++++++++++--------------------------- 2 files changed, 27 insertions(+), 69 deletions(-) diff --git a/chroot-bin/setres b/chroot-bin/setres index 9ec934c69..bfcb3688d 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -85,39 +85,6 @@ else xrandr --rmmode "$name" 2>/dev/null || true fi -# Setting a custom mode failed, fall back on best match - -# This assumes modes have "canonical" names, in the form "x[_]" -res="`xrandr | awk ' - function abs(x) { return x > 0 ? x : -x } - BEGIN { - tw='"$x"' - th='"$y"' - oversize_penalty = 1000000 - } - $1 ~ /[0-9]+x[0-9]+/ { - res = $1; sub(/_[0-9]+/, "", res) - w = res; sub(/x[0-9]*/, "", w); w = int(w) - h = res; sub(/[0-9]*x/, "", h); h = int(h) - score = abs(tw-w) + abs(th-h) - if (w > tw) score += oversize_penalty - if (h > th) score += oversize_penalty - if (!cmode || score < cscore) { - cmode = $1 - cscore = score - } - } - END { - if (!cmode) exit 1 - print cmode - } -'`" - -if [ -z "$res" ]; then - echo "Cannot find resolution" 1>&2 - exit 1 -fi - -xrandr -s "$res" 2>/dev/null -echo "$res" -exit 0 +# Probably xorg-dummy got overwritten. Recommend an update. +echo "Failed to set custom resolution. Update your chroot and try again." 1>&2 +exit 1 diff --git a/src/fbserver.c b/src/fbserver.c index 9f59c8de5..1b1814b31 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -402,45 +402,36 @@ int write_image(const struct screen* screen) { trueorabort(size == screen->width*screen->height*4, "Invalid screen byte count"); - if (screen->shm) { - struct cache_entry* entry = find_shm(screen->paddr, screen->sig, size); - - reply->shm = 1; - reply->updated = 1; - reply->shmfailed = 0; - - if (entry && entry->map) { - if (size == entry->length) { - memcpy(entry->map, img->data, size); - msync(entry->map, size, MS_SYNC); - } else { - /* This should never happen (it means the client passed an - * outdated buffer to us). */ - error("Invalid shm entry length (client bug!)."); - reply->shmfailed = 1; - } + trueorabort(screen->shm, "Non-SHM rendering is not supported"); + + struct cache_entry* entry = find_shm(screen->paddr, screen->sig, size); + + reply->shm = 1; + reply->updated = 1; + reply->shmfailed = 0; + + if (entry && entry->map) { + if (size == entry->length) { + memcpy(entry->map, img->data, size); + msync(entry->map, size, MS_SYNC); } else { - /* Keep the flow going, even if we cannot find the shm. Next time - * the NaCl client reallocates the buffer, we are likely to be able - * to find it. */ - error("Cannot find shm, moving on..."); + /* This should never happen (it means the client passed an + * outdated buffer to us). */ + error("Invalid shm entry length (client bug!)."); reply->shmfailed = 1; } - - /* Confirm write is done */ - socket_client_write_frame(reply_raw, sizeof(*reply), - WS_OPCODE_BINARY, 1); } else { - trueorabort(0, "Non-SHM path is currently broken!"); - /* Confirm write is done */ - reply->shm = 0; - reply->updated = 1; - socket_client_write_frame(reply_raw, sizeof(*reply), - WS_OPCODE_BINARY, 0); - /* FIXME: This is broken with current API... */ - socket_client_write_frame(img->data, size, WS_OPCODE_BINARY, 1); + /* Keep the flow going, even if we cannot find the shm. Next time + * the NaCl client reallocates the buffer, we are likely to be able + * to find it. */ + error("Cannot find shm, moving on..."); + reply->shmfailed = 1; } + /* Confirm write is done */ + socket_client_write_frame(reply_raw, sizeof(*reply), + WS_OPCODE_BINARY, 1); + return 0; } From eafd837f06a76bfe0cbb18f11361dddaee907220 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 21:49:13 -0800 Subject: [PATCH 133/334] Allow for XMETHOD environment variables. --- chroot-bin/croutonxinitrc-wrapper | 16 +++++++++------- chroot-etc/xserverrc-x11 | 2 +- chroot-etc/xserverrc-xephyr | 2 +- chroot-etc/xserverrc-xiwi | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index dcd573233..3cc41c4f0 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -43,8 +43,10 @@ else fi # Run crouton-specific commands: -xmethod="`readlink -f '/etc/X11/xinit/xserverrc'`" -xmethod="${xmethod##*-}" +if [ -z "$XMETHOD" ]; then + XMETHOD="`readlink -f '/etc/X11/xinit/xserverrc'`" + export XMETHOD="${XMETHOD##*-}" +fi # Record the name of the chroot in the root window properties if [ -f '/etc/crouton/name' ] && hash xprop 2>/dev/null; then @@ -52,7 +54,7 @@ if [ -f '/etc/crouton/name' ] && hash xprop 2>/dev/null; then fi # Record the crouton XMETHOD in the root window properties -xprop -root -f CROUTON_XMETHOD 8s -set CROUTON_XMETHOD "$xmethod" +xprop -root -f CROUTON_XMETHOD 8s -set CROUTON_XMETHOD "$XMETHOD" # Launch the powerd poker daemon croutonpowerd --daemon & @@ -63,7 +65,7 @@ if hash croutonclip 2>/dev/null; then fi # Pass through the host cursor and correct mousewheels on xephyr -if [ "$xmethod" = 'xephyr' ]; then +if [ "$XMETHOD" = 'xephyr' ]; then host-x11 croutoncursor "$DISPLAY" & if [ -z "$CROUTON_WHEEL_PARAMS" -a -r "$HOME/.croutonwheel" ]; then CROUTON_WHEEL_PARAMS="`head -n1 "$HOME/.croutonwheel"`" @@ -72,14 +74,14 @@ if [ "$xmethod" = 'xephyr' ]; then fi # xbindkeys and other input-related apps are not needed for kiwi -if [ "$xmethod" != "xiwi" ]; then +if [ "$XMETHOD" != "xiwi" ]; then # Apply the Chromebook keyboard map if installed. if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then setxkbmap -model chromebook fi # Launch key binding daemon - XMETHOD="$xmethod" xbindkeys -fg /etc/crouton/xbindkeysrc.scm + xbindkeys -fg /etc/crouton/xbindkeysrc.scm # Launch xbindkeys for the Chromium OS X server if it isn't running mkdir -m 775 -p /tmp/crouton-lock @@ -121,7 +123,7 @@ if [ "$xmethod" != "xiwi" ]; then fi # Crouton-in-a-tab: Start fbserver and launch display -if [ "$xmethod" = 'xiwi' ]; then +if [ "$XMETHOD" = 'xiwi' ]; then # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. setres 1024 768 > /dev/null diff --git a/chroot-etc/xserverrc-x11 b/chroot-etc/xserverrc-x11 index 9f7f84f5f..56f134dcc 100644 --- a/chroot-etc/xserverrc-x11 +++ b/chroot-etc/xserverrc-x11 @@ -10,7 +10,7 @@ if [ -w "$fbc" ]; then echo 0 > "$fbc" fi -XMETHOD='x11' +export XMETHOD='x11' XARGS='-nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/chroot-etc/xserverrc-xephyr b/chroot-etc/xserverrc-xephyr index 63f791217..b613fff9a 100644 --- a/chroot-etc/xserverrc-xephyr +++ b/chroot-etc/xserverrc-xephyr @@ -5,7 +5,7 @@ export DISPLAY=':0' XAUTHORITY='/var/host/Xauthority' -XMETHOD='xephyr' +export XMETHOD='xephyr' XARGS='-fullscreen -host-cursor -nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index 5c9ebd975..5bdb64c17 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -11,7 +11,7 @@ for arg in "$@"; do fi done -XMETHOD='xiwi' +export XMETHOD='xiwi' XARGS="-nolisten tcp -config xorg-dummy.conf -logfile $logfile" if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local From a86c3f62ed0635772f7caea31c77cfa00c205bd5 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 22:04:00 -0800 Subject: [PATCH 134/334] Freon support. Require xiwi until we get xorg compat. --- targets/x11 | 4 +++- test/tests/35-xorg | 5 +++-- test/tests/36-xephyr | 6 +++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/targets/x11 b/targets/x11 index 1d624742f..4ddea7e79 100644 --- a/targets/x11 +++ b/targets/x11 @@ -2,7 +2,9 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ "${ARCH#arm}" != "$ARCH" ]; then +if [ -f /sbin/frecon ]; then + REQUIRES='xiwi' +elif [ "${ARCH#arm}" != "$ARCH" ]; then REQUIRES='xephyr' else REQUIRES='xorg' diff --git a/test/tests/35-xorg b/test/tests/35-xorg index d6797d0fc..28e7980ac 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -5,8 +5,9 @@ # All supported releases should be able to launch an xorg server if [ -z "$release" ]; then - # xorg won't work on arm - if uname -m | grep -q 'arm'; then + if [ -f /sbin/frecon ]; then + log "xorg is not available on Freon. Skipping test." + elif uname -m | grep -q 'arm'; then log "xorg is not available on ARM. Skipping test." else echo "all" diff --git a/test/tests/36-xephyr b/test/tests/36-xephyr index 6467bf229..a611a6d43 100644 --- a/test/tests/36-xephyr +++ b/test/tests/36-xephyr @@ -5,7 +5,11 @@ # All supported releases should be able to launch an xephyr server if [ -z "$release" ]; then - echo "all" + if [ -f /sbin/frecon ]; then + log "xephyr doesn't work with Freon. Skipping test." + else + echo "all" + fi exit 0 fi From 174fd616d8505d566526f3a58e390bfb9b45f3fb Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 22:05:45 -0800 Subject: [PATCH 135/334] Exclude xiwi from alltargets test --- test/tests/x0-alltargets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/x0-alltargets b/test/tests/x0-alltargets index f355ba9d5..d31b4b31a 100644 --- a/test/tests/x0-alltargets +++ b/test/tests/x0-alltargets @@ -20,7 +20,7 @@ for target in "$SCRIPTDIR/targets/"*; do # Some other targets do not require testing in this context, # or have their own w* tests - for blacklist in audio core x11 xephyr xorg \ + for blacklist in audio core x11 xephyr xiwi xorg \ e17 gnome kde lxde unity xbmc xfce; do if [ "$target" = "$blacklist" ]; then break From 91f5e4e7513aa553e174c64257cbcfb33127e63f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 22:09:22 -0800 Subject: [PATCH 136/334] Prevent installing xephyr and xorg on Freon --- targets/xephyr | 3 +++ targets/xiwi | 2 +- targets/xorg | 8 ++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/targets/xephyr b/targets/xephyr index 973278ec7..d337f3ca8 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -2,6 +2,9 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +if [ -z "$TARGETNOINSTALL" -a -f /sbin/frecon ]; then + error 1 'xephyr target does not work with Freon. Use xiwi instead.' +fi REQUIRES='audio' PROVIDES='x11' DESCRIPTION='Nested X11 backend. Improves compatibility but lacks GPU accel.' diff --git a/targets/xiwi b/targets/xiwi index 99cc1ca64..2851a6c1c 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -4,7 +4,7 @@ # found in the LICENSE file. REQUIRES='core audio extension' PROVIDES='x11' -DESCRIPTION='X11 backend running unaccelerated in a Chromium OS window' +DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xorg b/targets/xorg index 76cad6a14..e578f42a0 100644 --- a/targets/xorg +++ b/targets/xorg @@ -2,8 +2,12 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" -a "${ARCH#arm}" != "$ARCH" ]; then - error 1 'xorg target does not work on ARM.' +if [ -z "$TARGETNOINSTALL" ]; then + if [ -f /sbin/frecon ]; then + error 1 'xorg target does not yet work with Freon.' + elif [ "${ARCH#arm}" != "$ARCH" ]; then + error 1 'xorg target does not work on ARM.' + fi fi REQUIRES='audio' PROVIDES='x11' From e01fd947b12c166ad4d26c2335c81cf018e39195 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 22:21:20 -0800 Subject: [PATCH 137/334] Fix invalid target checks --- installer/main.sh | 2 +- targets/chrome-common | 4 ++-- targets/chromium | 2 +- targets/e17 | 2 +- targets/touch | 2 +- targets/unity | 2 +- targets/xephyr | 2 +- targets/xorg | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/installer/main.sh b/installer/main.sh index b513935ef..9814935ba 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -309,7 +309,7 @@ if [ -z "$DOWNLOADONLY" ]; then exit 2 elif [ ! "${TARGET%common}" = "$TARGET" ] || \ [ ! -r "$TARGETSDIR/$TARGET" ] || \ - ! (TARGETNOINSTALL='y'; . "$TARGETSDIR/$TARGET"); then + ! (TARGETNOINSTALL='c'; . "$TARGETSDIR/$TARGET"); then error 2 "Invalid target \"$TARGET\"." fi done diff --git a/targets/chrome-common b/targets/chrome-common index f11265c39..2766c7c53 100644 --- a/targets/chrome-common +++ b/targets/chrome-common @@ -9,12 +9,12 @@ # This command expects CHANNEL to be set with the requested Google Chrome # channel: stable, beta, or unstable. -if [ -z "$TARGETNOINSTALL" -a "${ARCH#arm}" != "$ARCH" ]; then +if [ "${TARGETNOINSTALL:-c}" = 'c' -a "${ARCH#arm}" != "$ARCH" ]; then echo 'Google Chrome does not yet have an ARM build. Installing Chromium instead.' 1>&2 TARGET=chromium . "${TARGETSDIR:="$PWD"}/$TARGET" fi -if [ -z "$TARGETNOINSTALL" -a "$TARGET" != 'chrome' ]; then +if [ "${TARGETNOINSTALL:-c}" = 'c' -a "$TARGET" != 'chrome' ]; then # Avoid installing multiple chrome targets if grep -q "^chrome\$" "${TARGETDEDUPFILE:-/dev/null}"; then exit diff --git a/targets/chromium b/targets/chromium index 1f1649313..1f7d6e5d2 100644 --- a/targets/chromium +++ b/targets/chromium @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" ] && +if [ "${TARGETNOINSTALL:-c}" = 'c' ] && [ "$DISTRO" = 'debian' -o "$DISTRO" = 'kali' ] && [ "${ARCH#arm}" != "$ARCH" ]; then error 99 "chromium target is not supported on Debian/ARM." diff --git a/targets/e17 b/targets/e17 index 92a3846c3..ec5afd572 100644 --- a/targets/e17 +++ b/targets/e17 @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" ] && release -lt jessie -eq kali; then +if [ "${TARGETNOINSTALL:-c}" = 'c' ] && release -lt jessie -eq kali; then error 99 "e17 target is not supported on wheezy/kali." fi REQUIRES='gtk-extra' diff --git a/targets/touch b/targets/touch index bd6fd1026..2f682b906 100644 --- a/targets/touch +++ b/targets/touch @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" ] && +if [ "${TARGETNOINSTALL:-c}" = 'c' ] && [ "$DISTRO" = 'debian' -o "$DISTRO" = 'kali' ]; then error 99 "touch target is not supported on Debian." fi diff --git a/targets/unity b/targets/unity index 301ea9261..c16b9fb9a 100644 --- a/targets/unity +++ b/targets/unity @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" ]; then +if [ "${TARGETNOINSTALL:-c}" = 'c' ]; then if [ "$DISTRO" != 'ubuntu' ]; then error 99 "unity target is only supported on Ubuntu." elif [ "${ARCH#arm}" != "$ARCH" ] && ! release -eq precise; then diff --git a/targets/xephyr b/targets/xephyr index d337f3ca8..e4fb8416b 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" -a -f /sbin/frecon ]; then +if [ "${TARGETNOINSTALL:-c}" = 'c' -a -f /sbin/frecon ]; then error 1 'xephyr target does not work with Freon. Use xiwi instead.' fi REQUIRES='audio' diff --git a/targets/xorg b/targets/xorg index e578f42a0..e630ba2c2 100644 --- a/targets/xorg +++ b/targets/xorg @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -z "$TARGETNOINSTALL" ]; then +if [ "${TARGETNOINSTALL:-c}" = 'c' ]; then if [ -f /sbin/frecon ]; then error 1 'xorg target does not yet work with Freon.' elif [ "${ARCH#arm}" != "$ARCH" ]; then From 048b8027020d0b08480e95f5d27160d123de5160 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 22:32:13 -0800 Subject: [PATCH 138/334] Update first.html with xiwi references --- host-ext/crouton/first.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/first.html b/host-ext/crouton/first.html index 4bf8eb82a..a0f004c70 100644 --- a/host-ext/crouton/first.html +++ b/host-ext/crouton/first.html @@ -26,10 +26,10 @@

crouton integration
extension<


Thank you for installing the crouton extension!
-
This extension provides clipboard synchronization and URL handler to crouton chroots.
+
This extension provides clipboard synchronization, URL handler, and an X11 window to crouton chroots.
If you have not done so yet, you should download the crouton installer.
Then follow instructions in the README to get your first chroot running.
-
Be sure to install the extension target with the chroot.
+
Be sure to install the extension or xiwi targets with the chroot.
Need more help? Check out the wiki!
From fc5da1382e70525c857f68bd92a27013fe09dcd0 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 23 Dec 2014 23:00:03 -0800 Subject: [PATCH 139/334] Update README.md, finally with reference to the extension --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 1de4da8d2..0936cf5d6 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,10 @@ insecure*, so don't expect a password in your chroot to keep anyone from your data. crouton does support encrypting chroots, but the encryption is only as strong as the quality of your passphrase. Consider this your warning. +It's also highly recommended that you install the [crouton extension] +(https://goo.gl/OVQOEt), which, when combined with the `extension` or `xiwi` +targets, provides much improved integration with Chromium OS. + That's it! Surprised? @@ -118,6 +122,11 @@ Examples 2. Run `sh ~/Downloads/crouton -r list` to list the recognized releases and which distros they belong to. +### Wasteful rendundancies are wasteful: one clipboard, one browser, one window + 1. Install the [crouton extension](https://goo.gl/OVQOEt) into Chromium OS. + 2. Add the `extension` or `xiwi` version to your chroot. + 3. Try some copy-pasta, or uninstall all your web browsers from the chroot. + ### I don't always use Linux, but when I do, I use CLI 1. You can save a chunk of space by ditching X and just installing command-line tools using `-t core` or `-t cli-extra` @@ -125,6 +134,8 @@ Examples `sudo enter-chroot` 3. Use the [Crosh Window](https://goo.gl/eczLT) extension to keep Chromium OS from eating standard keyboard shortcuts. + 4. If you installed cli-extra, `startcli` will launch a new VT right into the + chroot. ### A new version of crouton came out; my chroot is therefore obsolete and sad 1. Check for updates, download the latest version, and see what's new by From 69cb24ef152bd07374b3982eb9abcc2515d7491d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 25 Dec 2014 13:34:56 -0800 Subject: [PATCH 140/334] Move brightness and croutonpowerd into core. Fixes #258, finally. --- targets/core | 2 +- targets/xephyr | 2 +- targets/xiwi | 2 +- targets/xorg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/targets/core b/targets/core index 72a09494d..43dcf857b 100644 --- a/targets/core +++ b/targets/core @@ -4,7 +4,7 @@ # found in the LICENSE file. REQUIRES='' DESCRIPTION='Performs core system configuration. Most users would want this.' -CHROOTBIN='croutonversion host-dbus host-x11' +CHROOTBIN='brightness croutonpowerd croutonversion host-dbus host-x11' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/xephyr b/targets/xephyr index e4fb8416b..3fb78b6ff 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -8,7 +8,7 @@ fi REQUIRES='audio' PROVIDES='x11' DESCRIPTION='Nested X11 backend. Improves compatibility but lacks GPU accel.' -CHROOTBIN='brightness croutoncycle croutonpowerd croutonwheel croutonxinitrc-wrapper xinit' +CHROOTBIN='croutoncycle croutonwheel croutonxinitrc-wrapper xinit' CHROOTETC='xbindkeysrc.scm xserverrc-xephyr xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xiwi b/targets/xiwi index 2851a6c1c..40cfc2d79 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -5,7 +5,7 @@ REQUIRES='core audio extension' PROVIDES='x11' DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' -CHROOTBIN='brightness croutoncycle croutonfindnacl croutonpowerd croutonxinitrc-wrapper setres xinit' +CHROOTBIN='croutoncycle croutonfindnacl croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xorg b/targets/xorg index e630ba2c2..b85db545e 100644 --- a/targets/xorg +++ b/targets/xorg @@ -12,7 +12,7 @@ fi REQUIRES='audio' PROVIDES='x11' DESCRIPTION='X.Org X11 backend. Enables GPU acceleration on supported platforms.' -CHROOTBIN='brightness croutoncycle croutonpowerd croutonxinitrc-wrapper setres xinit' +CHROOTBIN='croutoncycle croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xserverrc-x11 xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" From bd2170bd2ffcc7ee82bf44a80fec218180389604 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 25 Dec 2014 13:36:42 -0800 Subject: [PATCH 141/334] Remove redundant REQUIRES from xiwi --- targets/xiwi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/xiwi b/targets/xiwi index 40cfc2d79..9d46fdf4d 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -2,7 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -REQUIRES='core audio extension' +REQUIRES='audio extension' PROVIDES='x11' DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' CHROOTBIN='croutoncycle croutonfindnacl croutonxinitrc-wrapper setres xinit' From 3e5202a4445be20761bd1ca4977943dda236fc75 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 25 Dec 2014 13:44:13 -0800 Subject: [PATCH 142/334] Make sure chromeos-setdevpasswd is successful. Fixes #991 --- host-bin/mount-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index dfaab295b..ba01b6a51 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -141,7 +141,7 @@ for NAME in "$@"; do if [ ! -f '/mnt/stateful_partition/etc/devmode.passwd' ]; then echo 'You must have a root password in Chromium OS to mount encrypted chroots.' 1>&2 if [ -z "$CROUTON_PASSPHRASE$CROUTON_NEW_PASSPHRASE" ]; then - chromeos-setdevpasswd + while ! chromeos-setdevpasswd; do :; done fi fi From 21db8947b12f1de4039bce66cb7cdbe9a6beb59c Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 25 Dec 2014 20:25:24 -0800 Subject: [PATCH 143/334] Try to make some sense this time. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0936cf5d6..297f7af22 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,10 @@ Examples 2. Add the `extension` or `xiwi` version to your chroot. 3. Try some copy-pasta, or uninstall all your web browsers from the chroot. +*Installing the extension and its target gives you synchronized clipboards, the +option of using Chromium OS to handle URLs, and allows chroots to create +graphical sessions as Chromium OS windows.* + ### I don't always use Linux, but when I do, I use CLI 1. You can save a chunk of space by ditching X and just installing command-line tools using `-t core` or `-t cli-extra` From d8958b5fca02002d7950e7e0db5e46d70ded7a0b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 26 Dec 2014 14:52:32 -0800 Subject: [PATCH 144/334] Rename x11 XMETHOD to xorg, and make sure coexistence works. --- chroot-bin/croutonxinitrc-wrapper | 9 +++++++-- chroot-etc/xserverrc | 21 ++++++++++++++++++++ chroot-etc/xserverrc-local.example | 2 +- chroot-etc/{xserverrc-x11 => xserverrc-xorg} | 2 +- targets/extension | 10 ++++++++-- targets/x11-common | 3 ++- targets/xephyr | 2 +- targets/xiwi | 2 +- targets/xorg | 7 +++++-- 9 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 chroot-etc/xserverrc rename chroot-etc/{xserverrc-x11 => xserverrc-xorg} (96%) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 3cc41c4f0..149504336 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -44,8 +44,13 @@ fi # Run crouton-specific commands: if [ -z "$XMETHOD" ]; then - XMETHOD="`readlink -f '/etc/X11/xinit/xserverrc'`" - export XMETHOD="${XMETHOD##*-}" + if [ -f '/etc/crouton/xmethod' ]; then + read XMETHOD _ < /etc/crouton/xmethod + export XMETHOD + else + echo 'X11 backend not set.' 1>&2 + exit 1 + fi fi # Record the name of the chroot in the root window properties diff --git a/chroot-etc/xserverrc b/chroot-etc/xserverrc new file mode 100644 index 000000000..1a3f3519b --- /dev/null +++ b/chroot-etc/xserverrc @@ -0,0 +1,21 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$XMETHOD" ]; then + if [ -f '/etc/crouton/xmethod' ]; then + read XMETHOD _ < /etc/crouton/xmethod + else + echo 'X11 backend not set.' 1>&2 + exit 1 + fi +fi + +xserverrc="/etc/crouton/xserverrc-${XMETHOD##*/}" +if [ ! -f "$xserverrc" ]; then + echo "Invalid X11 backend '$XMETHOD'" 1>&2 + exit 2 +fi + +. "$xserverrc" diff --git a/chroot-etc/xserverrc-local.example b/chroot-etc/xserverrc-local.example index 0872b6a1e..558b68be6 100644 --- a/chroot-etc/xserverrc-local.example +++ b/chroot-etc/xserverrc-local.example @@ -7,7 +7,7 @@ # /etc/crouton/xserverrc-local # # The file is sourced before invoking the X server with the variable -# XMETHOD set to x11 or xephyr and the variable XARGS containing the +# XMETHOD set to xephyr, xiwi, or xorg and the variable XARGS containing the # command line arguments that will be passed to the server. # # Uncoment if fonts look too big on machines with 1366x768 11.6" screen diff --git a/chroot-etc/xserverrc-x11 b/chroot-etc/xserverrc-xorg similarity index 96% rename from chroot-etc/xserverrc-x11 rename to chroot-etc/xserverrc-xorg index 56f134dcc..d5fb6f234 100644 --- a/chroot-etc/xserverrc-x11 +++ b/chroot-etc/xserverrc-xorg @@ -10,7 +10,7 @@ if [ -w "$fbc" ]; then echo 0 > "$fbc" fi -export XMETHOD='x11' +export XMETHOD='xorg' XARGS='-nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/targets/extension b/targets/extension index d2e08d5e5..4f7821e87 100644 --- a/targets/extension +++ b/targets/extension @@ -23,8 +23,14 @@ install x11-utils xclip compile websocket '' -# XMETHOD is defined in x11 (or xephyr), which this package depends on -if [ "$XMETHOD" = 'x11' ]; then +# vtmonitor is needed for supporting xorg. +# There are three ways xorg might be installed relative to extension: via a +# dependency before extension, via explicit selection before extension, and via +# explicit selection after extension. +# The first is handled by checking XMETHOD, as the first X11 backend brought in +# (explicit or otherwise) sets XMETHOD permanently. The latter two are handled +# by greping the explicit targets file for xorg. +if [ "$XMETHOD" = 'xorg' ] || grep -q xorg /etc/crouton/targets; then compile vtmonitor '' fi diff --git a/targets/x11-common b/targets/x11-common index 203a6280a..d646c3189 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -8,7 +8,8 @@ ### Append to prepare.sh: # Store and apply the X11 method -ln -sfT "/etc/crouton/xserverrc-$XMETHOD" '/etc/X11/xinit/xserverrc' +ln -sfT '/etc/crouton/xserverrc' '/etc/X11/xinit/xserverrc' +echo "$XMETHOD" > '/etc/crouton/xmethod' # Install utility for croutoncycle compile wmtools '-lX11' libx11-dev diff --git a/targets/xephyr b/targets/xephyr index 3fb78b6ff..3a8b18af6 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -9,7 +9,7 @@ REQUIRES='audio' PROVIDES='x11' DESCRIPTION='Nested X11 backend. Improves compatibility but lacks GPU accel.' CHROOTBIN='croutoncycle croutonwheel croutonxinitrc-wrapper xinit' -CHROOTETC='xbindkeysrc.scm xserverrc-xephyr xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xserverrc xserverrc-xephyr xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/xiwi b/targets/xiwi index 9d46fdf4d..dadbe660b 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -6,7 +6,7 @@ REQUIRES='audio extension' PROVIDES='x11' DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' CHROOTBIN='croutoncycle croutonfindnacl croutonxinitrc-wrapper setres xinit' -CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc-xiwi xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/xorg b/targets/xorg index b85db545e..8edf4278d 100644 --- a/targets/xorg +++ b/targets/xorg @@ -13,11 +13,14 @@ REQUIRES='audio' PROVIDES='x11' DESCRIPTION='X.Org X11 backend. Enables GPU acceleration on supported platforms.' CHROOTBIN='croutoncycle croutonxinitrc-wrapper setres xinit' -CHROOTETC='xbindkeysrc.scm xserverrc-x11 xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xserverrc xserverrc-xorg xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -XMETHOD="${XMETHOD:-x11}" +XMETHOD="${XMETHOD:-xorg}" + +# Migration from ambiguous XMETHOD +rm -f '/etc/crouton/xserverrc-x11' # On precise, install lts-trusty xorg server to support newer hardware if kernel # version != 3.4 (lts-trusty mesa requires version >=3.6) From 90603aa98750549a035d4984c23f083423c90dc4 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 13 Mar 2013 18:28:37 -0700 Subject: [PATCH 145/334] Add desktop distribution targets. Dedicated to people who love full environments and hate disk space. --- targets/kde-desktop | 10 ++++++++++ targets/lxde-desktop | 10 ++++++++++ targets/unity-desktop | 10 ++++++++++ targets/xfce-desktop | 10 ++++++++++ 4 files changed, 40 insertions(+) create mode 100644 targets/kde-desktop create mode 100644 targets/lxde-desktop create mode 100644 targets/unity-desktop create mode 100644 targets/xfce-desktop diff --git a/targets/kde-desktop b/targets/kde-desktop new file mode 100644 index 000000000..a7474e523 --- /dev/null +++ b/targets/kde-desktop @@ -0,0 +1,10 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +REQUIRES='kde' +DESCRIPTION='Installs KDE along with common applications. (Approx. 1000MB)' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +install kubuntu-desktop -- network-manager diff --git a/targets/lxde-desktop b/targets/lxde-desktop new file mode 100644 index 000000000..512b8e1ad --- /dev/null +++ b/targets/lxde-desktop @@ -0,0 +1,10 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +REQUIRES='lxde' +DESCRIPTION='Installs LXDE along with common applications. (Approx. 800MB)' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +install lubuntu-desktop -- chromium-browser network-manager diff --git a/targets/unity-desktop b/targets/unity-desktop new file mode 100644 index 000000000..c94b27ef1 --- /dev/null +++ b/targets/unity-desktop @@ -0,0 +1,10 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +REQUIRES='unity' +DESCRIPTION='Installs Unity along with common applications. (Approx. 1100MB)' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +install ubuntu-desktop -- network-manager firefox xorg diff --git a/targets/xfce-desktop b/targets/xfce-desktop new file mode 100644 index 000000000..b33b669ee --- /dev/null +++ b/targets/xfce-desktop @@ -0,0 +1,10 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +REQUIRES='xfce' +DESCRIPTION='Installs Xfce along with common applications. (Approx. 1200MB)' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +install xubuntu-desktop -- firefox network-manager xorg From 44947dec52176c6c52a5c14bdbdf3f19f10564f9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 29 Sep 2013 03:30:35 -0700 Subject: [PATCH 146/334] Let the desktop targets bring in whichever browser they please. --- targets/lxde-desktop | 2 +- targets/unity-desktop | 2 +- targets/xfce-desktop | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/targets/lxde-desktop b/targets/lxde-desktop index 512b8e1ad..257ce9c4d 100644 --- a/targets/lxde-desktop +++ b/targets/lxde-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs LXDE along with common applications. (Approx. 800MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install lubuntu-desktop -- chromium-browser network-manager +install lubuntu-desktop -- network-manager diff --git a/targets/unity-desktop b/targets/unity-desktop index c94b27ef1..ef066fbb2 100644 --- a/targets/unity-desktop +++ b/targets/unity-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs Unity along with common applications. (Approx. 1100MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install ubuntu-desktop -- network-manager firefox xorg +install ubuntu-desktop -- network-manager xorg diff --git a/targets/xfce-desktop b/targets/xfce-desktop index b33b669ee..8db7a7190 100644 --- a/targets/xfce-desktop +++ b/targets/xfce-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs Xfce along with common applications. (Approx. 1200MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install xubuntu-desktop -- firefox network-manager xorg +install xubuntu-desktop -- network-manager xorg From 958f4f1a13cd187bdeb354f8c23925442af81aaa Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 27 Dec 2014 10:27:54 -0800 Subject: [PATCH 147/334] Debian desktops! --- targets/kde-desktop | 2 +- targets/lxde-desktop | 2 +- targets/xfce-desktop | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/targets/kde-desktop b/targets/kde-desktop index a7474e523..0e03f0f2d 100644 --- a/targets/kde-desktop +++ b/targets/kde-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs KDE along with common applications. (Approx. 1000MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install kubuntu-desktop -- network-manager +install ubuntu=kubuntu-desktop,task-kde-desktop -- network-manager diff --git a/targets/lxde-desktop b/targets/lxde-desktop index 257ce9c4d..6a61c706d 100644 --- a/targets/lxde-desktop +++ b/targets/lxde-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs LXDE along with common applications. (Approx. 800MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install lubuntu-desktop -- network-manager +install ubuntu=lubuntu-desktop,task-lxde-desktop -- network-manager diff --git a/targets/xfce-desktop b/targets/xfce-desktop index 8db7a7190..edb539a22 100644 --- a/targets/xfce-desktop +++ b/targets/xfce-desktop @@ -7,4 +7,4 @@ DESCRIPTION='Installs Xfce along with common applications. (Approx. 1200MB)' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: -install xubuntu-desktop -- network-manager xorg +install ubuntu=xubuntu-desktop,task-xfce-desktop -- network-manager xorg From 8318150fceb790f6d5450898042f181f1cbce2f4 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 27 Dec 2014 10:46:10 -0800 Subject: [PATCH 148/334] Add gnome-desktop target --- targets/gnome-desktop | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 targets/gnome-desktop diff --git a/targets/gnome-desktop b/targets/gnome-desktop new file mode 100644 index 000000000..8e8256bd5 --- /dev/null +++ b/targets/gnome-desktop @@ -0,0 +1,13 @@ +#!/bin/sh -e +# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +if [ "${TARGETNOINSTALL:-c}" = 'c' ] && release -lt quantal; then + error 99 "gnome-desktop target is not available in $RELEASE." +fi +REQUIRES='gnome' +DESCRIPTION='Installs GNOME along with common applications. (Approx. 1100MB)' +. "${TARGETSDIR:="$PWD"}/common" + +### Append to prepare.sh: +install ubuntu=ubuntu-gnome-desktop,task-gnome-desktop -- network-manager xorg From b9634c6820b49db0597f0b2e3cdc656da4b517b6 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 27 Dec 2014 10:57:57 -0800 Subject: [PATCH 149/334] Add tests for desktop targets --- test/tests/w2d-gnome-desktop | 16 ++++++++++++++++ test/tests/w3d-kde-desktop | 16 ++++++++++++++++ test/tests/w4d-lxde-desktop | 16 ++++++++++++++++ test/tests/w5d-unity-desktop | 16 ++++++++++++++++ test/tests/w7d-xfce-desktop | 16 ++++++++++++++++ test/tests/x0-alltargets | 5 +++++ 6 files changed, 85 insertions(+) create mode 100644 test/tests/w2d-gnome-desktop create mode 100644 test/tests/w3d-kde-desktop create mode 100644 test/tests/w4d-lxde-desktop create mode 100644 test/tests/w5d-unity-desktop create mode 100644 test/tests/w7d-xfce-desktop diff --git a/test/tests/w2d-gnome-desktop b/test/tests/w2d-gnome-desktop new file mode 100644 index 000000000..e7ed520d4 --- /dev/null +++ b/test/tests/w2d-gnome-desktop @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="gnome-desktop" +startcmd="startgnome" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w3d-kde-desktop b/test/tests/w3d-kde-desktop new file mode 100644 index 000000000..fea93c3d3 --- /dev/null +++ b/test/tests/w3d-kde-desktop @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="kde-desktop" +startcmd="startkde" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w4d-lxde-desktop b/test/tests/w4d-lxde-desktop new file mode 100644 index 000000000..29099ec56 --- /dev/null +++ b/test/tests/w4d-lxde-desktop @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="lxde-desktop" +startcmd="startlxde" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w5d-unity-desktop b/test/tests/w5d-unity-desktop new file mode 100644 index 000000000..840c4748f --- /dev/null +++ b/test/tests/w5d-unity-desktop @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="unity-desktop" +startcmd="startunity" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/w7d-xfce-desktop b/test/tests/w7d-xfce-desktop new file mode 100644 index 000000000..8c2240a27 --- /dev/null +++ b/test/tests/w7d-xfce-desktop @@ -0,0 +1,16 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +if [ -z "$release" ]; then + echo "all" + exit 0 +fi + +target="xfce-desktop" +startcmd="startxfce4" +xte="" + +# Run common file +. "$SCRIPTDIR/test/tests/w0-common" diff --git a/test/tests/x0-alltargets b/test/tests/x0-alltargets index d31b4b31a..1f1525210 100644 --- a/test/tests/x0-alltargets +++ b/test/tests/x0-alltargets @@ -18,6 +18,11 @@ for target in "$SCRIPTDIR/targets/"*; do continue fi + # Ignore *desktop targets too, since they'd all be blacklisted + if [ "${target%desktop}" != "$target" ]; then + continue + fi + # Some other targets do not require testing in this context, # or have their own w* tests for blacklist in audio core x11 xephyr xiwi xorg \ From 63a9306fc5279838c9d86360b0b25e29e98d3ae7 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 28 Dec 2014 01:21:53 -0500 Subject: [PATCH 150/334] Quick hack to unbreak updates and target checking. This needs a proper fix. --- installer/main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/main.sh b/installer/main.sh index 9814935ba..4294b90a6 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -309,7 +309,7 @@ if [ -z "$DOWNLOADONLY" ]; then exit 2 elif [ ! "${TARGET%common}" = "$TARGET" ] || \ [ ! -r "$TARGETSDIR/$TARGET" ] || \ - ! (TARGETNOINSTALL='c'; . "$TARGETSDIR/$TARGET"); then + ! (TARGETNOINSTALL="${UPDATE:-c}"; . "$TARGETSDIR/$TARGET"); then error 2 "Invalid target \"$TARGET\"." fi done From 00179dae48ed72d4f4621fc169f1bbadb09f8b1a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 28 Dec 2014 01:21:53 -0500 Subject: [PATCH 151/334] Quick hack to unbreak updates and target checking. This needs a proper fix. --- installer/main.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/main.sh b/installer/main.sh index 9814935ba..4294b90a6 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -309,7 +309,7 @@ if [ -z "$DOWNLOADONLY" ]; then exit 2 elif [ ! "${TARGET%common}" = "$TARGET" ] || \ [ ! -r "$TARGETSDIR/$TARGET" ] || \ - ! (TARGETNOINSTALL='c'; . "$TARGETSDIR/$TARGET"); then + ! (TARGETNOINSTALL="${UPDATE:-c}"; . "$TARGETSDIR/$TARGET"); then error 2 "Invalid target \"$TARGET\"." fi done From 9df99a48bf570a7a66c281bcc48f0742c1f03ce1 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 28 Dec 2014 18:51:39 -0500 Subject: [PATCH 152/334] Sends username and passphrase prompts to /dev/tty when necessary Fixes #1232 --- host-bin/mount-chroot | 22 ++++++++++++---------- installer/functions | 19 +++++++++++++++++++ targets/post-common | 13 ++++++++----- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index ba01b6a51..134724f56 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -59,29 +59,30 @@ fi addtrap "stty echo 2>/dev/null" # Function to prompt the user for a passphrase. Sets $passphrase. -promptNewPassphrase() { - echo -n "Choose an encryption passphrase for $NAME: " 1>&2 +promptNewPassphrase() { ( + stdout_to_tty + echo -n "Choose an encryption passphrase for $NAME: " [ -t 0 ] && stty -echo while [ -z "$passphrase" ]; do read -r passphrase if [ -z "$passphrase" ]; then - echo 1>&2 - echo -n 'You must specify a passphrase: ' 1>&2 + echo + echo -n 'You must specify a passphrase: ' continue fi echo 1>&2 - echo -n 'Please confirm your passphrase: ' 1>&2 + echo -n 'Please confirm your passphrase: ' read -r confirmation if [ ! "$confirmation" = "$passphrase" ]; then passphrase='' - echo 1>&2 - echo -n 'Passphrases do not match; try again: ' 1>&2 + echo + echo -n 'Passphrases do not match; try again: ' fi confirmation='' done [ -t 0 ] && stty echo - echo 1>&2 -} + echo +) } # Mount each chroot for NAME in "$@"; do @@ -139,7 +140,8 @@ for NAME in "$@"; do # Ensure that there's a root password set before decrypting the chroot, # unless the passphrase was specified via env, which isn't secure anyway if [ ! -f '/mnt/stateful_partition/etc/devmode.passwd' ]; then - echo 'You must have a root password in Chromium OS to mount encrypted chroots.' 1>&2 + stdout_to_tty \ +'You must have a root password in Chromium OS to mount encrypted chroots.' if [ -z "$CROUTON_PASSPHRASE$CROUTON_NEW_PASSPHRASE" ]; then while ! chromeos-setdevpasswd; do :; done fi diff --git a/installer/functions b/installer/functions index 4cc0f02fa..e6e9246f0 100644 --- a/installer/functions +++ b/installer/functions @@ -153,6 +153,25 @@ validate_name() { return 0 } +# Redirects stdout to stderr or /dev/tty, or prints parameters onto tty if stdin +# is a tty but stderr is not +stdout_to_tty() { + # If we have a tty input but the output is redirected, output prompt to tty + if [ -t 0 -a ! -t 2 ]; then + if [ $# = 0 ]; then + exec 1>/dev/tty + else + echo "$*" 1>/dev/tty + fi + else + if [ $# = 0 ]; then + exec 1>&2 + else + echo "$*" 1>&2 + fi + fi +} + # Websocket interface PIPEDIR='/tmp/crouton-ext' CRIATDISPLAY="$PIPEDIR/kiwi-display" diff --git a/targets/post-common b/targets/post-common index 09f261b10..852b00d54 100644 --- a/targets/post-common +++ b/targets/post-common @@ -34,17 +34,20 @@ if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then apt-get clean fi +# From here on out is interactive stuff, so ensure we're printing to the user +stdout_to_tty + # Add the primary user groups='audio,video,sudo,plugdev' if ! grep -q ':1000:' /etc/passwd; then while ! echo "$username" | grep -q '^[a-z][-a-z0-9_]*$'; do if [ -n "$username" ]; then - echo 'Username must be lowercase letters, numbers, or dashes.' 1>&2 + echo 'Username must be lowercase letters, numbers, or dashes.' fi - echo -n 'Please specify a username for the primary user: ' 1>&2 + echo -n 'Please specify a username for the primary user: ' if [ -n "$USERNAME" ]; then username="$USERNAME" - echo "$username" 1>&2 + echo "$username" else read -r username junk fi @@ -60,7 +63,7 @@ if ! grep -q ':1000:' /etc/passwd; then done if [ "$tries" = 3 ]; then echo \ -"Password left unset. To set a password, inside the chroot run: passwd $username" 1>&2 +"Password left unset. To set a password, inside the chroot run: passwd $username" fi else username="`awk -F: '$3==1000{print $1; exit}' '/etc/passwd'`" @@ -70,7 +73,7 @@ fi if [ -n "$TIPS" ]; then echo " Here's some tips: -$TIPS" 1>&2 +$TIPS" fi rm -f "$0" From 7b4faa12f3089ac020b94424c3fd6f41ebd210f8 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 28 Dec 2014 21:46:08 -0500 Subject: [PATCH 153/334] Add chvt to start* scripts. Fixes #1225. Allow chvt to silently fail, i.e., in the case of Freon. --- host-bin/starte17 | 1 + host-bin/startgnome | 1 + host-bin/startkde | 1 + host-bin/startlxde | 1 + host-bin/startunity | 1 + host-bin/startxbmc | 1 + host-bin/startxfce4 | 1 + 7 files changed, 7 insertions(+) diff --git a/host-bin/starte17 b/host-bin/starte17 index 719081194..3c6b0e571 100755 --- a/host-bin/starte17 +++ b/host-bin/starte17 @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t e17 "$@" "" \ exec xinit /usr/bin/enlightenment_start diff --git a/host-bin/startgnome b/host-bin/startgnome index e3e1b5eab..c4bbc8954 100755 --- a/host-bin/startgnome +++ b/host-bin/startgnome @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t gnome "$@" "" \ exec startgnome diff --git a/host-bin/startkde b/host-bin/startkde index c3a4e55f6..91507613e 100755 --- a/host-bin/startkde +++ b/host-bin/startkde @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t kde "$@" "" \ exec xinit /usr/bin/startkde diff --git a/host-bin/startlxde b/host-bin/startlxde index bbec40e76..260daba42 100755 --- a/host-bin/startlxde +++ b/host-bin/startlxde @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t lxde "$@" "" \ exec xinit /usr/bin/startlxde diff --git a/host-bin/startunity b/host-bin/startunity index 4beea9ab4..0d29866bb 100755 --- a/host-bin/startunity +++ b/host-bin/startunity @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t unity "$@" "" \ exec startunity diff --git a/host-bin/startxbmc b/host-bin/startxbmc index 5af361e15..bc274a762 100755 --- a/host-bin/startxbmc +++ b/host-bin/startxbmc @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t xbmc "$@" "" \ exec xinit /usr/bin/xbmc diff --git a/host-bin/startxfce4 b/host-bin/startxfce4 index 9e9ca881a..f8eb1082c 100755 --- a/host-bin/startxfce4 +++ b/host-bin/startxfce4 @@ -14,5 +14,6 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." +chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t xfce "$@" "" \ exec startxfce4 From 11bb53c84abe759dfb5f7688fff3b3c17f895163 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 28 Dec 2014 21:58:40 -0500 Subject: [PATCH 154/334] Save users from themselves. Disallow start* as root. Prevent the various start* scripts from being run as root inside the chroot. If you really want to do so, explicitly launch the /usr/bin/* version yourself. --- chroot-bin/crouton-noroot | 32 ++++++++++++++++++++++++++++++++ chroot-bin/startgnome | 2 +- chroot-bin/startkde | 1 + chroot-bin/startlxde | 1 + chroot-bin/startunity | 2 +- chroot-bin/startxfce4 | 1 + targets/gnome | 2 +- targets/kde | 1 + targets/lxde | 1 + targets/unity | 2 +- targets/xfce | 1 + 11 files changed, 42 insertions(+), 4 deletions(-) create mode 100755 chroot-bin/crouton-noroot create mode 120000 chroot-bin/startkde create mode 120000 chroot-bin/startlxde create mode 120000 chroot-bin/startxfce4 diff --git a/chroot-bin/crouton-noroot b/chroot-bin/crouton-noroot new file mode 100755 index 000000000..8cbddc84b --- /dev/null +++ b/chroot-bin/crouton-noroot @@ -0,0 +1,32 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Wrapper for scripts that shouldn't be launched as root. +# Symlink to automatically launch that application. +# Launch directly with a parameter to launch that executable +# Launch directly with a blank or - first parameter and a word to just check and +# exit with an exit code, printing out the second parameter as the app name. + +APPLICATION="${0##*/}" +if [ "$APPLICATION" = 'crouton-noroot' ]; then + if [ -z "$1" -o "$1" = '-' -o "$1" = '--' ]; then + APPLICATION='' + else + APPLICATION="$1" + shift + fi +else + APPLICATION="/usr/bin/$APPLICATION" +fi + +if [ "$USER" = root -o "$UID" = 0 ]; then + app="${APPLICATION:-"$2"}" + echo "Do not launch ${app##*/} as root inside the chroot." 1>&2 + exit 2 +elif [ -z "$APPLICATION" ]; then + exit 0 +fi + +exec "$APPLICATION" "$@" diff --git a/chroot-bin/startgnome b/chroot-bin/startgnome index 26f65cb45..3b628d8e8 100755 --- a/chroot-bin/startgnome +++ b/chroot-bin/startgnome @@ -5,4 +5,4 @@ # Launches GNOME; automatically falls back to gnome-panel -exec gnome-session-wrapper gnome +exec crouton-noroot gnome-session-wrapper gnome diff --git a/chroot-bin/startkde b/chroot-bin/startkde new file mode 120000 index 000000000..95e64e85c --- /dev/null +++ b/chroot-bin/startkde @@ -0,0 +1 @@ +crouton-noroot \ No newline at end of file diff --git a/chroot-bin/startlxde b/chroot-bin/startlxde new file mode 120000 index 000000000..95e64e85c --- /dev/null +++ b/chroot-bin/startlxde @@ -0,0 +1 @@ +crouton-noroot \ No newline at end of file diff --git a/chroot-bin/startunity b/chroot-bin/startunity index 33571888e..c127f0737 100755 --- a/chroot-bin/startunity +++ b/chroot-bin/startunity @@ -9,4 +9,4 @@ export UBUNTU_MENUPROXY=1 export GTK_MODULES="unity-gtk-module" -exec gnome-session-wrapper ubuntu +exec crouton-noroot gnome-session-wrapper ubuntu diff --git a/chroot-bin/startxfce4 b/chroot-bin/startxfce4 new file mode 120000 index 000000000..95e64e85c --- /dev/null +++ b/chroot-bin/startxfce4 @@ -0,0 +1 @@ +crouton-noroot \ No newline at end of file diff --git a/targets/gnome b/targets/gnome index d68c66a45..70bb709f7 100644 --- a/targets/gnome +++ b/targets/gnome @@ -5,7 +5,7 @@ REQUIRES='gtk-extra' DESCRIPTION='Installs the GNOME desktop environment. (Approx. 400MB)' HOSTBIN='startgnome' -CHROOTBIN='startgnome gnome-session-wrapper' +CHROOTBIN='crouton-noroot startgnome gnome-session-wrapper' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/kde b/targets/kde index ff1137b45..57a1a6571 100644 --- a/targets/kde +++ b/targets/kde @@ -5,6 +5,7 @@ REQUIRES='x11' DESCRIPTION='Installs a minimal KDE desktop environment. (Approx. 600MB)' HOSTBIN='startkde' +CHROOTBIN='crouton-noroot startkde' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/lxde b/targets/lxde index 177a51ff6..512e98e8b 100644 --- a/targets/lxde +++ b/targets/lxde @@ -5,6 +5,7 @@ REQUIRES='gtk-extra' DESCRIPTION='Installs the LXDE desktop environment. (Approx. 200MB)' HOSTBIN='startlxde' +CHROOTBIN='crouton-noroot startlxde' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: diff --git a/targets/unity b/targets/unity index c16b9fb9a..98ce93585 100644 --- a/targets/unity +++ b/targets/unity @@ -12,7 +12,7 @@ fi REQUIRES='gtk-extra' DESCRIPTION='Installs the Unity desktop environment. (Approx. 700MB)' HOSTBIN='startunity' -CHROOTBIN='startunity gnome-session-wrapper crouton-unity-autostart' +CHROOTBIN='crouton-noroot startunity gnome-session-wrapper crouton-unity-autostart' CHROOTETC='unity-autostart.desktop unity-profiled' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xfce b/targets/xfce index f2e91ebc1..1d829bae2 100644 --- a/targets/xfce +++ b/targets/xfce @@ -5,6 +5,7 @@ REQUIRES='gtk-extra' DESCRIPTION='Installs the Xfce desktop environment. (Approx. 250MB)' HOSTBIN='startxfce4' +CHROOTBIN='crouton-noroot startxfce4' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: From 69d69eaaa4196cee9cc199619988c51894baff5f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 01:28:08 -0500 Subject: [PATCH 155/334] Hack getopts to allow arbitrary placement of positional arguments. Use it selectively, though. enter-chroot should not use it, for instance. --- host-bin/edit-chroot | 14 +++++++------- host-bin/mount-chroot | 10 +++++----- host-bin/unmount-chroot | 19 +++++++------------ installer/functions | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index f48418179..aca457230 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -66,23 +66,23 @@ Options: . "$BINDIR/../installer/functions" # Process arguments -while getopts 'abc:def:k:lm:ry' f; do - case "$f" in +getopts_string='abc:def:k:lm:ry' +while getopts_nextarg; do + case "$getopts_var" in a) ALLCHROOTS='y';; b) BACKUP='y';; - c) CHROOTS="`readlink -m "$OPTARG"`";; + c) CHROOTS="`readlink -m "$getopts_arg"`";; d) DELETE='y';; e) ENCRYPT='y';; - f) TARBALL="$OPTARG";; - k) KEYFILE="$OPTARG";; + f) TARBALL="$getopts_arg";; + k) KEYFILE="$getopts_arg";; l) LISTDETAILS=$(($LISTDETAILS+1));; - m) MOVE="$OPTARG";; + m) MOVE="$getopts_arg";; r) RESTORE=$(($RESTORE+1));; y) YES='a'; YESPARAM='-y';; \?) error 2 "$USAGE";; esac done -shift "$((OPTIND-1))" # If the executable name is delete*, assume DELETE. if [ ! "${APPLICATION#delete}" = "$APPLICATION" ]; then diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index ba01b6a51..f74b49b4a 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -33,17 +33,17 @@ Options: . "$BINDIR/../installer/functions" # Process arguments -while getopts 'c:ek:np' f; do - case "$f" in - c) CHROOTS="`readlink -m "$OPTARG"`";; +getopts_string='c:ek:np' +while getopts_nextarg; do + case "$getopts_var" in + c) CHROOTS="`readlink -m "$getopts_arg"`";; e) ENCRYPT="$((ENCRYPT+1))";; - k) KEYFILE="$OPTARG";; + k) KEYFILE="$getopts_arg";; n) CREATE='y';; p) PRINT='y';; \?) error 2 "$USAGE";; esac done -shift "$((OPTIND-1))" # Need at least one chroot listed if [ $# = 0 ]; then diff --git a/host-bin/unmount-chroot b/host-bin/unmount-chroot index 17a7c3ba8..511dc89a7 100755 --- a/host-bin/unmount-chroot +++ b/host-bin/unmount-chroot @@ -38,29 +38,24 @@ Options: -y Signal any remaining processes without confirmation. Automatically escalates from SIGTERM to SIGKILL." -# Function to exit with exit code $1, spitting out message $@ to stderr -error() { - local ecode="$1" - shift - echo "$*" 1>&2 - exit "$ecode" -} +# Common functions +. "$BINDIR/../installer/functions" # Process arguments -while getopts 'ac:fkpt:xy' f; do - case "$f" in +getopts_string='ac:fkpt:xy' +while getopts_nextarg; do + case "$getopts_var" in a) ALLCHROOTS='y';; - c) CHROOTS="`readlink -m "$OPTARG"`";; + c) CHROOTS="`readlink -m "$getopts_arg"`";; f) FORCE='y';; k) SIGNAL="KILL";; p) PRINT='y';; - t) TRIES="$OPTARG";; + t) TRIES="$getopts_arg";; x) EXCLUDEROOT='y';; y) YES='a';; \?) error 2 "$USAGE";; esac done -shift "$((OPTIND-1))" # Need at least one chroot listed, or -a; not both. if [ $# = 0 -a -z "$ALLCHROOTS" ] || [ ! $# = 0 -a -n "$ALLCHROOTS" ]; then diff --git a/installer/functions b/installer/functions index 4cc0f02fa..ff3a67c95 100644 --- a/installer/functions +++ b/installer/functions @@ -68,6 +68,43 @@ undotrap() { settrap "$TRAP" } +# Works mostly like built-in getopts but silently coalesces positional arguments. +# Does not take parameters. Set getopts_string prior to calling. +# Sets getopts_var and getopts_arg. +# $@ will be left with the positional arguments, so you should NOT shift at all. +# In bash, enables alias expansion, but that shouldn't impact anything. +shopt -q -s expand_aliases 2>/dev/null || true +# Running count of the number of positional arguments +# We're done processing if all of the remaining arguments are positional. +getopts_npos=0 +getopts_dashdash='' +alias getopts_nextarg='getopts_ret=1 + while [ "$#" -gt "$getopts_npos" ]; do + OPTIND=1 + if [ -z "$getopts_dashdash" ] && getopts "$getopts_string" getopts_var; then + shift "$((OPTIND-1))" + if [ "$#" -lt "$getopts_npos" ]; then + # Bad parameter usage ate a positional argument. + # Generate the proper error message by abusing getopts. + set -- "-$getopts_var" + getopts "$getopts_var:" getopts_var + shift + fi + getopts_arg="$OPTARG" + getopts_ret=0 + break + fi + if [ "$1" = "--" ]; then + # Still need to loop through to fix the ordering + getopts_dashdash=y + else + set -- "$@" "$1" + getopts_npos="$((getopts_npos+1))" + fi + shift + done + [ "$getopts_ret" = 0 ]' + # Compares $RELEASE to the specified releases, assuming $DISTRODIR/releases is # sorted oldest to newest. Every two parameters are considered criteria that are # ORed together. The first parameter is the comparator, as provided to "test". From 222fb4a714d48384a3ee57f03231b699450c2a3a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 01:51:46 -0500 Subject: [PATCH 156/334] Use locales package to generate locales. Fixes #534 --- targets/core | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/targets/core b/targets/core index 43dcf857b..f87a76a28 100644 --- a/targets/core +++ b/targets/core @@ -113,7 +113,9 @@ install --minimal sudo wget ca-certificates # Generate and set default locale if [ ! -f '/etc/default/locale' ] && hash locale-gen 2>/dev/null; then locale-gen --lang en_US.UTF-8 - update-locale LANG=en_US.UTF-8 + if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then + dpkg-reconfigure locales + fi fi # Link debian_chroot to the chroot name From 50532d5460e8dd4774d2bd9a7aff0487acfc31ac Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 02:07:08 -0500 Subject: [PATCH 157/334] Delete the chroot if bootstrapping fails. Fixes #1254 --- installer/main.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/installer/main.sh b/installer/main.sh index 4294b90a6..e8dcd4aa7 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -455,6 +455,10 @@ Valid chroots: CHROOT="`sh "$HOSTBINDIR/mount-chroot" -k "$KEYFILE" \ $create $ENCRYPT -p -c "$CHROOTS" -- "$NAME"`" + # Remove the directory if bootstrapping fails + addtrap "[ \"\`ls -A '$CHROOTS/$NAME' 2>/dev/null | wc -l\`\" -le 1 ] \ + && rm -rf '$CHROOTS/$NAME'" + # Auto-unmount the chroot when the script exits addtrap "sh '$HOSTBINDIR/unmount-chroot' -y -c '$CHROOTS' -- '$NAME' 2>/dev/null" From 545308dc74ea216cedad7015628bdf1315474ce9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 03:32:02 -0500 Subject: [PATCH 158/334] Explicitly bring in fbdev and intel drivers. If xiwi is installed first, the dummy driver will satisfy xorg dependencies. --- targets/xorg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/xorg b/targets/xorg index 8edf4278d..fe7992bef 100644 --- a/targets/xorg +++ b/targets/xorg @@ -98,7 +98,7 @@ END apt-get -y --force-yes --no-install-recommends dist-upgrade -f fi -install xorg dmz-cursor-theme +install xorg xserver-xorg-video-fbdev xserver-xorg-video-intel dmz-cursor-theme # Fix launching X11 from inside crosh (user doesn't own a TTY) sed -i 's/allowed_users=.*/allowed_users=anybody/' '/etc/X11/Xwrapper.config' From d78e513776c9d96aa0c81af7da0871ee2fb78e32 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 18:41:38 -0500 Subject: [PATCH 159/334] Fix explicit packages in the presence of backports. --- targets/xorg | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/targets/xorg b/targets/xorg index fe7992bef..78c10ad84 100644 --- a/targets/xorg +++ b/targets/xorg @@ -22,12 +22,14 @@ XMETHOD="${XMETHOD:-xorg}" # Migration from ambiguous XMETHOD rm -f '/etc/crouton/xserverrc-x11' +ltspackages='' # On precise, install lts-trusty xorg server to support newer hardware if kernel # version != 3.4 (lts-trusty mesa requires version >=3.6) if release -eq precise && ! uname -r | grep -q "^3.4."; then # We still install xorg later to pull in its dependencies install --minimal xserver-xorg-lts-trusty libgl1-mesa-glx-lts-trusty \ xserver-xorg-input-synaptics-lts-trusty + ltspackages='-lts-trusty' fi # On saucy onwards, if kernel version is 3.4, manually pin down old mesa @@ -98,7 +100,8 @@ END apt-get -y --force-yes --no-install-recommends dist-upgrade -f fi -install xorg xserver-xorg-video-fbdev xserver-xorg-video-intel dmz-cursor-theme +install xorg dmz-cursor-theme \ + xserver-xorg-video-fbdev$ltspackages xserver-xorg-video-intel$ltspackages # Fix launching X11 from inside crosh (user doesn't own a TTY) sed -i 's/allowed_users=.*/allowed_users=anybody/' '/etc/X11/Xwrapper.config' From 6f0c0b5b02a81b6ba75f3ebb822ae9573017eb8a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 20:05:59 -0500 Subject: [PATCH 160/334] getopts: Fix combined parameters, and multiple --'s. --- installer/functions | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/installer/functions b/installer/functions index ff3a67c95..e4eb0e06f 100644 --- a/installer/functions +++ b/installer/functions @@ -80,10 +80,8 @@ getopts_npos=0 getopts_dashdash='' alias getopts_nextarg='getopts_ret=1 while [ "$#" -gt "$getopts_npos" ]; do - OPTIND=1 if [ -z "$getopts_dashdash" ] && getopts "$getopts_string" getopts_var; then - shift "$((OPTIND-1))" - if [ "$#" -lt "$getopts_npos" ]; then + if [ "$(($#+1-OPTIND))" -lt "$getopts_npos" ]; then # Bad parameter usage ate a positional argument. # Generate the proper error message by abusing getopts. set -- "-$getopts_var" @@ -92,9 +90,22 @@ alias getopts_nextarg='getopts_ret=1 fi getopts_arg="$OPTARG" getopts_ret=0 + # Avoid -- confusion by shifting if OPTARG is set + if [ -n "$OPTARG" ]; then + shift "$((OPTIND-1))" + OPTIND=1 + fi break fi - if [ "$1" = "--" ]; then + # Do not let getopts consume a -- + if [ "$OPTIND" -gt 1 ]; then + shift "$((OPTIND-2))" + if [ "$1" != "--" ]; then + shift + fi + fi + OPTIND=1 + if [ -z "$getopts_dashdash" -a "$1" = "--" ]; then # Still need to loop through to fix the ordering getopts_dashdash=y else From 33842343e1bcd3efaa5132072033c38b626d8753 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 21:56:14 -0500 Subject: [PATCH 161/334] Add edit-chroot filesystem type checks. Fixes #803 Don't backup to tmpfs, advise not to move keys to tmpfs, don't move chroot to non-ext. --- host-bin/edit-chroot | 56 +++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index f48418179..916981a37 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -65,6 +65,16 @@ Options: # Common functions . "$BINDIR/../installer/functions" +# Returns the mountpoint a path is on. The path doesn't need to exist. +# $1: the path to check +# outputs on stdout +getmountpoint() { + mp="`readlink -m "$1"`" + while ! stat -c '%m' "$mp" 2>/dev/null; do + mp="${mp%/*}" + done +} + # Process arguments while getopts 'abc:def:k:lm:ry' f; do case "$f" in @@ -134,6 +144,12 @@ if [ -n "$ALLCHROOTS" ]; then fi fi +# If TARBALL is unspecified and we're in /, put the tarball in ~/Downloads +if [ -n "$BACKUP$RESTORE" -a -z "$TARBALL" -a "$PWD" = '/' \ + -a -d '/home/chronos/user/Downloads' ]; then + TARBALL="/home/chronos/user/Downloads/" +fi + # If multiple chroots are listed, KEYFILE and MOVE must be empty or directories. if [ $# -gt 1 -a -f "$KEYFILE" -a ! "$KEYFILE" = '-' ]; then error 2 "Multiple chroots specified, but $KEYFILE is not a directory." @@ -143,11 +159,35 @@ elif [ $# -gt 1 -a -f "$TARBALL" ]; then error 2 "Multiple chroots specified, but $TARBALL is not a directory." fi +# Don't allow moving to non-ext filesystems +if [ -n "$MOVE" ] && \ + df -T "`getmountpoint "$MOVE"`" | awk '$2~"^ext"{exit 1}'; then + error 2 "Chroots can only be moved to ext filesystems." +fi + +# Don't allow backing up to tmpfs filesystems +if [ -n "$BACKUP" ] && ! df -T "`getmountpoint "${TARBALL:-.}"`" \ + | awk '$2=="tmpfs"{exit 1}'; then + error 2 "Chroots cannot be backed up to temporary filesystems." +fi + # We need to run as root if [ ! "$USER" = root -a ! "$UID" = 0 ]; then error 2 "$APPLICATION must be run as root." fi +# Strongly advise against moving a keyfile to a tmpfs path +if [ "${KEYFILE:--}" != '-' ] && \ + ! df -T "`getmountpoint "$KEYFILE"`" | awk '$2=="tmpfs"{exit 1}'; then + echo -n '\ +Moving a keyfile to a temporary filesystem is a really good way to permanently +lose access to your chroot. If you still want to do this, wait for 15 seconds. +Otherwise: HIT CTRL-C RIGHT NOW > ' 1>&2 + sleep 15 + echo \ +'...okay. Be sure to put the keyfile somewhere safe before you reboot.' 1>&2 +fi + # If we're restoring and specified a tarball and no name, detect the name. if [ -n "$RESTORE" -a -n "$TARBALL" -a $# = 0 ]; then echo 'Detecting chroot name...' 1>&2 @@ -203,28 +243,12 @@ if [ -n "$TARBALL" ] && \ mkdir -p "$TARBALL" fi -# If TARBALL is unspecified and we're in /, put the tarball in ~/Downloads -if [ -n "$BACKUP$RESTORE" -a -z "$TARBALL" -a "$PWD" = '/' \ - -a -d '/home/chronos/user/Downloads' ]; then - TARBALL="/home/chronos/user/Downloads/" -fi - # Avoid kernel panics due to slow I/O disablehungtask # Make sure we always exit with echo on the tty. addtrap "stty echo 2>/dev/null" -# Returns the mountpoint a path is on. The path doesn't need to exist. -# $1: the path to check -# outputs on stdout -getmountpoint() { - mp="`readlink -m "$1"`" - while ! stat -c '%m' "$mp" 2>/dev/null; do - mp="${mp%/*}" - done -} - # Prints out a fancy spinner that updates every time a line is fed in, unless # the output is not to a tty, in which case it just prints a new line. # $1: number of lines between each update of the spinner From 6d2c997256e1f4b69612331c092d5f6d2719e3bc Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 21:58:47 -0500 Subject: [PATCH 162/334] Don't allow restoration to non-ext filesystems. --- host-bin/edit-chroot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 916981a37..73f3f8db8 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -165,6 +165,12 @@ if [ -n "$MOVE" ] && \ error 2 "Chroots can only be moved to ext filesystems." fi +# Don't allow restoring to non-ext filesystems +if [ -n "$RESTORE" ] && \ + df -T "`getmountpoint "$CHROOTS"`" | awk '$2~"^ext"{exit 1}'; then + error 2 "Chroots can only be restored to ext filesystems." +fi + # Don't allow backing up to tmpfs filesystems if [ -n "$BACKUP" ] && ! df -T "`getmountpoint "${TARBALL:-.}"`" \ | awk '$2=="tmpfs"{exit 1}'; then From e66424d8f26646dcf78dbca791d7de7ec7aae076 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 22:06:21 -0500 Subject: [PATCH 163/334] Don't allow installing to a non-ext filesystem. --- host-bin/edit-chroot | 10 ---------- installer/functions | 10 ++++++++++ installer/main.sh | 5 +++++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 73f3f8db8..5a60b4b1f 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -65,16 +65,6 @@ Options: # Common functions . "$BINDIR/../installer/functions" -# Returns the mountpoint a path is on. The path doesn't need to exist. -# $1: the path to check -# outputs on stdout -getmountpoint() { - mp="`readlink -m "$1"`" - while ! stat -c '%m' "$mp" 2>/dev/null; do - mp="${mp%/*}" - done -} - # Process arguments while getopts 'abc:def:k:lm:ry' f; do case "$f" in diff --git a/installer/functions b/installer/functions index 4cc0f02fa..7c4f7a45a 100644 --- a/installer/functions +++ b/installer/functions @@ -153,6 +153,16 @@ validate_name() { return 0 } +# Returns the mountpoint a path is on. The path doesn't need to exist. +# $1: the path to check +# outputs on stdout +getmountpoint() { + mp="`readlink -m "$1"`" + while ! stat -c '%m' "$mp" 2>/dev/null; do + mp="${mp%/*}" + done +} + # Websocket interface PIPEDIR='/tmp/crouton-ext' CRIATDISPLAY="$PIPEDIR/kiwi-display" diff --git a/installer/main.sh b/installer/main.sh index 4294b90a6..d352e1a11 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -446,6 +446,11 @@ Valid chroots: `sh "$HOSTBINDIR/edit-chroot" -c "$CHROOTS" -a`" fi + # Chroot must be located on an ext filesystem + if df -T "`getmountpoint "$CHROOT"`" | awk '$2~"^ext"{exit 1}'; then + error 1 "$CHROOTSRC is not an ext filesystem." + fi + # Restore the chroot now if [ -n "$RESTORE" ]; then sh "$HOSTBINDIR/edit-chroot" -r -f "$TARBALL" -c "$CHROOTS" -- "$NAME" From 22aa7f536a85b56fc69a5c3dce160d3da081c2a9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 22:08:55 -0500 Subject: [PATCH 164/334] Don't freak out if /usr/local is a bind-mount. Fixes #976 --- installer/main.sh | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/installer/main.sh b/installer/main.sh index d352e1a11..7798d6565 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -383,25 +383,19 @@ if [ -z "$RESTOREBIN$DOWNLOADONLY" ]; then # If no prefix is set, check that /usr/local/chroots ($CHROOTS) is a # symbolic link to /mnt/stateful_partition/crouton/chroots ($CHROOTSLINK) - if [ -z "$PREFIXSET" -a ! -h "$CHROOTS" ]; then + # /mnt/stateful_partition/dev_image is bind-mounted to /usr/local, so mv + # does not understand that they are on the same filesystem + # Instead, use the direct path, and confirm that they're actually the same + # to catch situations where things are bind-mounted over /usr/local + truechroots="/mnt/stateful_partition/dev_image/chroots" + if [ -z "$PREFIXSET" -a ! -h "$CHROOTS" ] \ + && [ "$CHROOTS" -ef "$truechroots" ]; then # Detect if chroots are left in the old chroots directory, and move them # to the new directory. if [ -e "$CHROOTS" ] && ! rmdir "$CHROOTS" 2>/dev/null; then echo \ "Migrating data from legacy chroots directory $CHROOTS to $CHROOTSLINK..." 1>&2 - # /mnt/stateful_partition/dev_image is bind-mounted to /usr/local, - # so mv does not understand that they are on the same filesystem - # Instead, use the direct path. - truechroots="/mnt/stateful_partition/dev_image/chroots" - - # Be extra careful and check both files are indeed the same - if [ "`stat -c '%i' "$truechroots"`" != \ - "`stat -c '%i' "$CHROOTS"`" ]; then - error 1 \ -"$truechroots and $CHROOTS are not the same file as expected." - fi - # Check that CHROOTSLINK is empty if [ -e "$CHROOTSLINK" ] && ! rmdir "$CHROOTSLINK" 2>/dev/null; then error 1 \ From c33ba2b333ffe151e428f893b696e901d1fdd5af Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 29 Dec 2014 23:14:20 -0500 Subject: [PATCH 165/334] Enable splitting of backups --- host-bin/edit-chroot | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index f48418179..effe24328 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -16,6 +16,7 @@ KEYFILE='' LISTDETAILS='' MOVE='' RESTORE='' +SPLIT='' TARBALL='' YES='' YESPARAM='' @@ -60,13 +61,15 @@ Options: restores to that name instead of the one in the tarball. Will not overwrite a chroot when restoring unless -r is specified twice. + -s SPLIT Force a backup archive to be split into SPLIT-sized chunks. + FAT32 filesystems are split by default to 4GB. -y Do all actions without confirmation." # Common functions . "$BINDIR/../installer/functions" # Process arguments -while getopts 'abc:def:k:lm:ry' f; do +while getopts 'abc:def:k:lm:rs:y' f; do case "$f" in a) ALLCHROOTS='y';; b) BACKUP='y';; @@ -78,6 +81,7 @@ while getopts 'abc:def:k:lm:ry' f; do l) LISTDETAILS=$(($LISTDETAILS+1));; m) MOVE="$OPTARG";; r) RESTORE=$(($RESTORE+1));; + s) SPLIT="$OPTARG";; y) YES='a'; YESPARAM='-y';; \?) error 2 "$USAGE";; esac @@ -335,15 +339,34 @@ for NAME in "$@"; do dest="$dest.gz" fi fi + # If we're writing to a fat32 filesystem, split the file at 4GB chunks + if ! df -T "`getmountpoint "$dest"`" | awk '$2~"^v?fat"{exit 1}'; then + SPLIT="${SPLIT:-4GB}" + fi + if [ -n "$SPLIT" ]; then + tardest="`mktemp -u --tmpdir=/tmp 'crouton-backup.XXX'`" + mkfifo -m 600 "$tardest" + addtrap "rm -f '$tardest'" + split -b "$SPLIT" -d "$tardest" "$dest.part" & + splitpid="$!" + else + tardest="$dest" + splitpid='' + fi echo -n " Backing up $CHROOT to $dest..." 1>&2 - addtrap "echo 'Deleting partial archive.' 1>&2; rm -f '$dest'" + addtrap "echo 'Deleting partial archive.' 1>&2; \ + kill '$splitpid' 2>/dev/null; rm -f '$dest' '$dest.part'*" ret=0 spinner 1 tar --checkpoint=100 --checkpoint-action=exec=echo \ --one-file-system -V "crouton:backup.${date%-*}${date#*-}-$NAME" \ - -caf "$dest" -C "$CHROOTS" "$NAME" || ret=$? - if [ "$ret" != 0 ]; then + -caf "$tardest" -C "$CHROOTS" "$NAME" || ret="$?" + if [ -n "$SPLIT" ]; then + wait "$splitpid" || ret="$?$ret" + mv -f "$dest.part00" "$dest" || ret="$?$ret" + fi + if [ "$ret" -ne 0 ]; then echo "Unable to backup $CHROOT." 1>&2 - exit $ret + exit "$ret" fi # Make sure filesystem is sync'ed sync @@ -477,7 +500,8 @@ for NAME in "$@"; do # safety. We don't do this when encrypting a chroot (see mount-chroot), # because that would require 2x the space on one device. When switching # filesystems like this, however, that isn't a concern. - if [ ! "`getmountpoint "$target"`" = "`getmountpoint "$CHROOT"`" ]; then + if [ ! "`getmountpoint "$target"`" = \ + "`getmountpoint "$CHROOT"`" ]; then echo "Moving $CHROOT across filesystems to $target" 1>&2 echo 'This will take a while.' 1>&2 echo "If the operation gets interrupted, you can safely delete $target" 1>&2 From a709d2d508aeaeeb01f604fe18431c6964e4b98a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 30 Dec 2014 01:24:25 -0500 Subject: [PATCH 166/334] Enable restoration of split backups. Fixes #823. --- host-bin/edit-chroot | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index effe24328..fa4245921 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -383,7 +383,7 @@ for NAME in "$@"; do # Search for the alphabetically last tarball with src. # Dated tarballs take precedence over undated tarballs. for file in "$file."* "$file-"*; do - if [ ! -f "$file" ]; then + if [ ! -f "$file" -o "${file%part[0-9][0-9]}" != "$file" ]; then continue fi # Confirm it's a tarball @@ -408,8 +408,22 @@ for NAME in "$@"; do fi echo -n " Restoring $src to $CHROOT..." 1>&2 mkdir -p "$CHROOT" + if [ -f "$src.part01" ]; then + tarsrc="`mktemp -u --tmpdir=/tmp 'crouton-restore.XXX'`" + mkfifo -m 600 "$tarsrc" + # Pipe through dd to eat the EOFs + cat "$src" "$src.part"* | dd status=none >> "$tarsrc" & + catpid="$!" + addtrap "rm -f '$tarsrc'; kill '$catpid' 2>/dev/null" + else + tarsrc="$src" + catpid='' + fi spinner 1 tar --checkpoint=200 --checkpoint-action=exec=echo \ - --one-file-system -xaf "$src" -C "$CHROOT" --strip-components=1 + --one-file-system -xaf "$tarsrc" -C "$CHROOT" --strip-components=1 + if [ -n "$catpid" ]; then + wait "$catpid" + fi # Make sure filesystem is sync'ed sync echo "Finished restoring $src to $CHROOT" 1>&2 From fcdeb42e36e7f791a9277516bc9531c112e59f2e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 30 Dec 2014 16:09:32 -0500 Subject: [PATCH 167/334] Minor fixups --- chroot-bin/croutonxinitrc-wrapper | 2 +- chroot-etc/xserverrc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 149504336..2f13849af 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -45,7 +45,7 @@ fi # Run crouton-specific commands: if [ -z "$XMETHOD" ]; then if [ -f '/etc/crouton/xmethod' ]; then - read XMETHOD _ < /etc/crouton/xmethod + read -r XMETHOD _ < /etc/crouton/xmethod export XMETHOD else echo 'X11 backend not set.' 1>&2 diff --git a/chroot-etc/xserverrc b/chroot-etc/xserverrc index 1a3f3519b..545ddfbe1 100644 --- a/chroot-etc/xserverrc +++ b/chroot-etc/xserverrc @@ -5,15 +5,15 @@ if [ -z "$XMETHOD" ]; then if [ -f '/etc/crouton/xmethod' ]; then - read XMETHOD _ < /etc/crouton/xmethod + read -r XMETHOD _ < /etc/crouton/xmethod else echo 'X11 backend not set.' 1>&2 exit 1 fi fi -xserverrc="/etc/crouton/xserverrc-${XMETHOD##*/}" -if [ ! -f "$xserverrc" ]; then +xserverrc="/etc/crouton/xserverrc-$XMETHOD" +if [ "${XMETHOD##*/}" != "$XMETHOD" -o ! -f "$xserverrc" ]; then echo "Invalid X11 backend '$XMETHOD'" 1>&2 exit 2 fi From 1bc1177e4f769765582a48b776d97dfd3ff915ca Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 30 Dec 2014 19:01:03 -0500 Subject: [PATCH 168/334] Use ls -a, and add comment --- installer/main.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/installer/main.sh b/installer/main.sh index e8dcd4aa7..ff3c567a4 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -455,8 +455,9 @@ Valid chroots: CHROOT="`sh "$HOSTBINDIR/mount-chroot" -k "$KEYFILE" \ $create $ENCRYPT -p -c "$CHROOTS" -- "$NAME"`" - # Remove the directory if bootstrapping fails - addtrap "[ \"\`ls -A '$CHROOTS/$NAME' 2>/dev/null | wc -l\`\" -le 1 ] \ + # Remove the directory if bootstrapping fails. Also delete if the only file + # there is .ecryptfs (valid chroots have far more than 1 file) + addtrap "[ \"\`ls -a '$CHROOTS/$NAME' 2>/dev/null | wc -l\`\" -le 3 ] \ && rm -rf '$CHROOTS/$NAME'" # Auto-unmount the chroot when the script exits From a56d814f3f07a7cb1c160b0f5e6ebe00f0a6a4d0 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 31 Dec 2014 00:47:05 -0500 Subject: [PATCH 169/334] Add test for split backup --- test/tests/12-edit-chroot | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index d31199d8c..3086a51af 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -88,6 +88,16 @@ host enter-chroot -n "$release" true # Verify croutonversion outputs correct information host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" +# Back up with a split +host edit-chroot -y -s 50MB -f "$BACKUPDIR/$release.tar" -b "$release" +test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 +du -b "$BACKUPDIR"/* | passes awk '$1>50000000{exit 1}' + +# Restore the split archive. Fails first due to existence +fails host edit-chroot -y -f "$BACKUPDIR/$release.tar" -r "$release" +host edit-chroot -y -f "$BACKUPDIR/$release.tar" -rr "$release" +host enter-chroot -n "$release" true + # Delete it host delete-chroot -y "$release" fails host enter-chroot -n "$release" true From fe45c38d0263d8546968351421b87e285e6c3f2f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 31 Dec 2014 00:51:25 -0500 Subject: [PATCH 170/334] Remove the tests that fail on bad parameter order. Stress parameter order by reordering some of the test parameters. --- test/tests/12-edit-chroot | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index d31199d8c..5c290f3af 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -68,18 +68,13 @@ fails host edit-chroot -y -b -f $BACKUPDIR "$release" fails host edit-chroot -y -f $BACKUPDIR -b "$release" fails ls "$BACKUPDIR"/* -# Backup a chroot (wrong parameter order fails) -fails host edit-chroot -y -b "$release" -f "$BACKUPDIR/$release.tar" +# Backup a chroot fails ls "$BACKUPDIR"/* host edit-chroot -y -f "$BACKUPDIR/$release.tar" -b "$release" -# Move an existing chroot (wrong parameter order fails) -fails host edit-chroot -y "$release" -m "$release.2" -host edit-chroot -y -m "$release.2" "$release" +# Move an existing chroot +host edit-chroot -y "$release" -m "$release.2" host enter-chroot -n "$release.2" true - -# Restore the chroot (wrong parameter order fails) -fails host edit-chroot -y -r "$release" -f "$BACKUPDIR/$release.tar" fails host enter-chroot -n "$release" true # Restore the chroot @@ -130,7 +125,7 @@ fails host edit-chroot -y -f "$BACKUPDIR" -b \ fails ls "$BACKUPDIR"/* # Backup both chroots -host edit-chroot -y -f "$BACKUPDIR" -b "$release" "$release.2" +host edit-chroot "$release" -y -f "$BACKUPDIR" -b "$release.2" # Move them to a new prefix: destination needs to end with a slash fails host edit-chroot -y -m "$PREFIX/chroots.2" "$release" "$release.2" @@ -142,7 +137,7 @@ host enter-chroot -c "$PREFIX/chroots.2" -n "$release" true host enter-chroot -c "$PREFIX/chroots.2" -n "$release.2" true # Delete both chroots -host delete-chroot -y -c "$PREFIX/chroots.2" "$release" "$release.2" +host delete-chroot "$release" "$release.2" -y -c "$PREFIX/chroots.2" fails host enter-chroot -c "$PREFIX/chroots.2" -n "$release" true fails host enter-chroot -c "$PREFIX/chroots.2" -n "$release.2" true From 992afe3e31b3c80d5dd54497a81fe45445306200 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 31 Dec 2014 01:07:07 -0500 Subject: [PATCH 171/334] Add apt-transport-https to core. Fixes #593 OOB --- targets/core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/core b/targets/core index 43dcf857b..6c5ba72f4 100644 --- a/targets/core +++ b/targets/core @@ -108,7 +108,7 @@ fi fixkeyboardmode # Install critical packages -install --minimal sudo wget ca-certificates +install --minimal sudo wget ca-certificates apt-transport-https # Generate and set default locale if [ ! -f '/etc/default/locale' ] && hash locale-gen 2>/dev/null; then From ed25d64a704652a3cc2a5749fda2e0de240ab9fb Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 31 Dec 2014 17:48:50 -0500 Subject: [PATCH 172/334] Add /etc/crouton/shares share mapping file. Fixes #620 --- host-bin/enter-chroot | 118 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index c270c982d..dc521e10e 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -385,10 +385,120 @@ if ! mountpoint -q "$destmedia"; then mount --rbind /media "$destmedia" fi -# Bind-mount ~/Downloads if we're logged in as a user -localdownloads='/home/chronos/user/Downloads' -if [ -z "$NOLOGIN" -a -n "$CHROOTHOME" -a -d "$localdownloads" ]; then - bindmount "$localdownloads" "$CHROOTHOME/Downloads" exec +# Provide a default /etc/crouton/shares file +shares="`fixabslinks '/etc/crouton/shares'`" +if [ -e "$shares" -a ! -f "$shares" ]; then + echo "Not mounting shares: /etc/crouton/shares is not a file." 1>&2 +elif [ ! -f "$shares" ]; then + cat > "$shares" < ~ +# Share an unencrypted directory between chroots. +# shared/home -> ~/Share + +# Share the downloads folder of the current user's profile +downloads -> ~/Downloads +EOF +fi +# Bind-mount the stuff in $CHROOT/etc/crouton/shares, unless NOLOGIN is set +if [ -z "$NOLOGIN" -a -f "$shares" ]; then + mountparams='exec' + localdownloads='/home/chronos/user/Downloads' + if [ ! -d "$localdownloads" ]; then + localdownloads='' + fi + localencrypted='/home/chronos/user/crouton/shared' + if [ ! -d "/home/chronos/user" ]; then + localencrypted='' + fi + localshare='/mnt/stateful_partition/crouton/shared' + # Allow line continuation characters (no -r) and strip leading/trailing + # whitespace, remove comments, and remove spaces around -> + sed 'y/\t/ /; s/^ *//; s/ *$//; s/ *-> */->/; /^#/d;' "$shares" \ + | while read line; do + src="${line%%->*}" + # If -> is not present, it's a specification of mountparams + if [ "$src" = "$line" ]; then + mountparams="${line#[\"\']}" + mountparams="${mountparams%[\"\']}" + continue + fi + # Remove quoting from src and ensure ends in / + if [ "${src#[\"\']}" != "$src" ]; then + src="${src#[\"\']}" + src="${src%[\"\']}" + fi + src="${src%/}/" + # Grab dest, remove quote, ensure ends in / + dest="${line#*->}" + if [ "${dest#[\"\']}" != "$dest" ]; then + dest="${dest#[\"\']}" + dest="${dest%[\"\']}" + fi + dest="${dest%/}/" + # .. path elements are not allowed + if [ "${src%/../*}" != "$src" -o "${dest%/../*}" != "$dest" ]; then + echo "Invalid share: $line" 1>&2 + continue + fi + # Expand src with a little bit of leniency + case "${src%%/*}" in + download*) + if [ -z "$localdownloads" ]; then + echo "Not mounting share (no Chromium OS user): $line" 1>&2 + continue + fi + src="$localdownloads/${src#*/}";; + encrypt*) + if [ -z "$localencrypted" ]; then + echo "Not mounting share (no Chromium OS user): $line" 1>&2 + continue + fi + src="$localencrypted/${src#*/}";; + share*) src="$localshare/${src#*/}";; + *) echo "Invalid share: $line" 1>&2 + continue;; + esac + # Expand dest for homedirs + if [ "${dest#[~/]}" = "$dest" ]; then + if [ -z "$CHROOTHOME" ]; then + echo "Not mounting share (no chroot user): $line" 1>&2 + continue + fi + dest="$CHROOTHOME/$dest" + elif [ "${dest#~}" != "$dest" ]; then + destuser="${dest%%/*}" + if [ "$destuser" = '~' ]; then + if [ -z "$CHROOTHOME" ]; then + echo "Not mounting share (no chroot user): $line" 1>&2 + continue + fi + dest="$CHROOTHOME/${dest#*/}" + else + dest="/home/${destuser#~}/${dest#*/}" + fi + fi + # Do the bindmount + mkdir -m 700 -p "$src" + if ! bindmount "$src" "$dest" "$mountparams"; then + echo "Failed to mount share: $line" 1>&2 + fi + done fi # Bind-mount /sys recursively, making it a slave in the chroot From d22f7aececccf75cf0839cc50a7b491ea6588b20 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 1 Jan 2015 03:37:39 -0500 Subject: [PATCH 173/334] Add -X to the bundler to run scripts directly from the bundle --- build/wrapper.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/build/wrapper.sh b/build/wrapper.sh index 502ad1bbe..8b009971e 100644 --- a/build/wrapper.sh +++ b/build/wrapper.sh @@ -62,8 +62,27 @@ if [ -z "$TRAP" ]; then exit fi +# See if we want to just run a script from the bundle +script="$SCRIPTDIR/installer/main.sh" +if [ "$1" = '-X' -a -z "$2" ]; then + ( + cd "$SCRIPTDIR" + echo "USAGE: ${0##*/} -X DIR/SCRIPT [ARGS] +Runs a script directly from the bundle. Valid DIR/SCRIPT combos:" 1>&2 + ls chroot-bin/* host-bin/* 1>&2 + ) + exit 2 +elif [ "$1" = '-X' ]; then + script="$SCRIPTDIR/$2" + if [ ! -f "$script" ]; then + echo "Invalid script '$2'" 1>&2 + exit 2 + fi + shift 2 +fi + # Execute the main script inline. It will use SCRIPTDIR to find what it needs. -. "$SCRIPTDIR/installer/main.sh" +. "$script" exit ### end of script; tarball follows From dbaf9bba3cc61737633beecfc5c75d4a47eebe01 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 1 Jan 2015 05:07:44 -0500 Subject: [PATCH 174/334] Make calls to readlink with user input more robust When relative paths are given to readlink as the canonicalizer, -- needs to be used to avoid readlink getting confused when the path starts with a dash. Once the path is absolute it's not an issue, so only selectively add --'s. --- chroot-bin/croutonfindnacl | 2 +- host-bin/edit-chroot | 6 +++--- host-bin/enter-chroot | 2 +- host-bin/mount-chroot | 2 +- host-bin/unmount-chroot | 2 +- installer/functions | 2 +- installer/main.sh | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/chroot-bin/croutonfindnacl b/chroot-bin/croutonfindnacl index ac10658d4..32c952617 100755 --- a/chroot-bin/croutonfindnacl +++ b/chroot-bin/croutonfindnacl @@ -51,7 +51,7 @@ for pid in $PIDS; do # Iterate over mappings, and check signature for fd in "/proc/$pid/fd"/*; do - link="`readlink "$fd"`" + link="`readlink -- "$fd"`" link="${link% (deleted)}" if [ "$link" = "$file" ]; then # Check if signature matches diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index d7f80a27d..9eb6efa93 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -74,7 +74,7 @@ while getopts_nextarg; do case "$getopts_var" in a) ALLCHROOTS='y';; b) BACKUP='y';; - c) CHROOTS="`readlink -m "$getopts_arg"`";; + c) CHROOTS="`readlink -m -- "$getopts_arg"`";; d) DELETE='y';; e) ENCRYPT='y';; f) TARBALL="$getopts_arg";; @@ -469,8 +469,8 @@ for NAME in "$@"; do if [ -d "$newkeyfile" -o ! "${newkeyfile%/}" = "$newkeyfile" ]; then newkeyfile="${newkeyfile%/}/$NAME" fi - oldkeyfile="`readlink -m "$oldkeyfile"`" - keyfilecanon="`readlink -m "$newkeyfile"`" + oldkeyfile="`readlink -m -- "$oldkeyfile"`" + keyfilecanon="`readlink -m -- "$newkeyfile"`" if [ ! -f "$oldkeyfile" ]; then # If there is no old keyfile, make sure we've requested encryption. if [ -z "$ENCRYPT" ]; then diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index dc521e10e..83810155d 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -65,7 +65,7 @@ while getopts 'bc:k:ln:t:u:x' f; do prevoptind="$OPTIND" case "$f" in b) BACKGROUND='y';; - c) CHROOTS="`readlink -m "$OPTARG"`";; + c) CHROOTS="`readlink -m -- "$OPTARG"`";; k) KEYFILE="$OPTARG";; l) LOGIN='y';; n) NAME="$OPTARG";; diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index 034158bb1..c2115fb9f 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -36,7 +36,7 @@ Options: getopts_string='c:ek:np' while getopts_nextarg; do case "$getopts_var" in - c) CHROOTS="`readlink -m "$getopts_arg"`";; + c) CHROOTS="`readlink -m -- "$getopts_arg"`";; e) ENCRYPT="$((ENCRYPT+1))";; k) KEYFILE="$getopts_arg";; n) CREATE='y';; diff --git a/host-bin/unmount-chroot b/host-bin/unmount-chroot index 511dc89a7..424e48e3e 100755 --- a/host-bin/unmount-chroot +++ b/host-bin/unmount-chroot @@ -46,7 +46,7 @@ getopts_string='ac:fkpt:xy' while getopts_nextarg; do case "$getopts_var" in a) ALLCHROOTS='y';; - c) CHROOTS="`readlink -m "$getopts_arg"`";; + c) CHROOTS="`readlink -m -- "$getopts_arg"`";; f) FORCE='y';; k) SIGNAL="KILL";; p) PRINT='y';; diff --git a/installer/functions b/installer/functions index f8bd116af..c96a15191 100644 --- a/installer/functions +++ b/installer/functions @@ -205,7 +205,7 @@ validate_name() { # $1: the path to check # outputs on stdout getmountpoint() { - mp="`readlink -m "$1"`" + mp="`readlink -m -- "$1"`" while ! stat -c '%m' "$mp" 2>/dev/null; do mp="${mp%/*}" done diff --git a/installer/main.sh b/installer/main.sh index 9454df4dc..6c7c6e99d 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -122,7 +122,7 @@ while getopts 'a:bdef:k:m:M:n:p:P:r:s:t:T:uUV' f; do m) MIRROR="$OPTARG";; M) MIRROR2="$OPTARG";; n) NAME="$OPTARG";; - p) PREFIX="`readlink -m "$OPTARG"`"; PREFIXSET='y';; + p) PREFIX="`readlink -m -- "$OPTARG"`"; PREFIXSET='y';; P) PROXY="$OPTARG";; r) RELEASE="$OPTARG";; t) TARGETS="$TARGETS${TARGETS:+","}$OPTARG";; @@ -694,7 +694,7 @@ echo -n '' > "$TARGETDEDUPFILE" unset SIMULATE TARGETNOINSTALL="$RESTOREHOSTBIN" if [ -n "$TARGETFILE" ]; then - TARGET="`readlink -f "$TARGETFILE"`" + TARGET="`readlink -f -- "$TARGETFILE"`" (. "$TARGET") >> "$PREPARE" fi t="${TARGETS%,},post-common," From 71f27c18865ab7524326651aa29b0b3cc7636ade Mon Sep 17 00:00:00 2001 From: divx118 Date: Fri, 2 Jan 2015 22:59:37 +0100 Subject: [PATCH 175/334] Prioritize targets which PROVIDES --- installer/main.sh | 15 +++++++++++++++ targets/common | 17 +++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/installer/main.sh b/installer/main.sh index 4294b90a6..ac6e10c53 100755 --- a/installer/main.sh +++ b/installer/main.sh @@ -686,6 +686,21 @@ else fi echo -n '' > "$TARGETDEDUPFILE" +# Check if a target has defined PROVIDES, if we are not restoring host-bin. +if [ ! -n "$RESTOREHOSTBIN" ]; then + # Create temporary file to list PROVIDES=TARGET. + PROVIDESFILE="`mktemp --tmpdir=/tmp "$APPLICATION-provides.XXX"`" + addtrap "rm -f '$PROVIDESFILE'" + t="${TARGETS%,}," + while [ -n "$t" ]; do + TARGET="${t%%,*}" + t="${t#*,}" + if [ -n "$TARGET" ]; then + (TARGETNOINSTALL="p"; . "$TARGETSDIR/$TARGET") + fi + done +fi + # Run each target, appending stdout to the prepare script. unset SIMULATE TARGETNOINSTALL="$RESTOREHOSTBIN" diff --git a/targets/common b/targets/common index 451f0cc90..03f769c3c 100644 --- a/targets/common +++ b/targets/common @@ -32,6 +32,19 @@ if [ "$TARGETS" = 'help' ]; then if [ -n "$REQUIRES" ]; then echo " Requires: $REQUIRES" fi +elif [ "$TARGETNOINSTALL" = "p" ]; then + # Avoid adding things already provided + # This will only check the provides from /etc/crouton/targets + # and targets specified with -t. + for t in $PROVIDES; do + # Don't add duplicate PROVIDES. If there are duplicates + # we will respect the order of the targets specified. + # This means on an update targets specified with -t + # will have higher priority then already installed targets. + if ! grep -q "^$t=.*\$" "${PROVIDESFILE:-/dev/null}"; then + echo "$t=$TARGET" >> "${PROVIDESFILE:-/dev/null}" + fi + done elif [ -z "$TARGETNOINSTALL" -o -n "$RESTOREHOSTBIN" ]; then # Avoid double-adding targets if grep -q "^$TARGET\$" "${TARGETDEDUPFILE:-/dev/null}"; then @@ -45,6 +58,10 @@ elif [ -z "$TARGETNOINSTALL" -o -n "$RESTOREHOSTBIN" ]; then done # Source the prerequisites for t in $REQUIRES; do + # If it is already provided change it to the target what provides it. + if grep -q "^$t=.*\$" "${PROVIDESFILE:-/dev/null}"; then + t="`awk -F= '/'$t'=/{print $2}' "${PROVIDESFILE:-/dev/null}"`" + fi (TARGET="$t" PROVIDES='' HOSTBIN='' CHROOTBIN='' CHROOTETC='' . "$TARGETSDIR/$t") done From 5d5f9c595a05ec844b0e3674a267e017944ee00d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 1 Jan 2015 22:17:56 -0800 Subject: [PATCH 176/334] Have the wrapper's -X call the script via sh to reset $0 --- build/wrapper.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/build/wrapper.sh b/build/wrapper.sh index 8b009971e..ed1a9b703 100644 --- a/build/wrapper.sh +++ b/build/wrapper.sh @@ -63,7 +63,6 @@ if [ -z "$TRAP" ]; then fi # See if we want to just run a script from the bundle -script="$SCRIPTDIR/installer/main.sh" if [ "$1" = '-X' -a -z "$2" ]; then ( cd "$SCRIPTDIR" @@ -79,10 +78,20 @@ elif [ "$1" = '-X' ]; then exit 2 fi shift 2 + # If this script was called with '-x' or '-v', pass that on + SETOPTIONS="-e" + if set -o | grep -q '^xtrace.*on$'; then + SETOPTIONS="$SETOPTIONS -x" + fi + if set -o | grep -q '^verbose.*on$'; then + SETOPTIONS="$SETOPTIONS -v" + fi + sh $SETOPTIONS "$script" "$@" + exit "$?" fi # Execute the main script inline. It will use SCRIPTDIR to find what it needs. -. "$script" +. "$SCRIPTDIR/installer/main.sh" exit ### end of script; tarball follows From cd3392fd19d82abf3a869feaccf70f76e55d7f6f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 1 Jan 2015 22:27:14 -0800 Subject: [PATCH 177/334] If we don't need to unmount, don't fail if unmounting fails. Prevents -al from stopping after a mounted chroot. --- host-bin/edit-chroot | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 9eb6efa93..a3b2e9e7a 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -15,6 +15,7 @@ ENCRYPT='' KEYFILE='' LISTDETAILS='' MOVE='' +NEEDS_UNMOUNT='' RESTORE='' SPLIT='' TARBALL='' @@ -73,15 +74,15 @@ getopts_string='abc:def:k:lm:rs:y' while getopts_nextarg; do case "$getopts_var" in a) ALLCHROOTS='y';; - b) BACKUP='y';; + b) BACKUP='y'; NEEDS_UNMOUNT='y';; c) CHROOTS="`readlink -m -- "$getopts_arg"`";; - d) DELETE='y';; - e) ENCRYPT='y';; + d) DELETE='y'; NEEDS_UNMOUNT='y';; + e) ENCRYPT='y'; NEEDS_UNMOUNT='y';; f) TARBALL="$getopts_arg";; k) KEYFILE="$getopts_arg";; l) LISTDETAILS=$(($LISTDETAILS+1));; - m) MOVE="$getopts_arg";; - r) RESTORE=$(($RESTORE+1));; + m) MOVE="$getopts_arg"; NEEDS_UNMOUNT='y';; + r) RESTORE=$(($RESTORE+1)); NEEDS_UNMOUNT='y';; s) SPLIT="$OPTARG";; y) YES='a'; YESPARAM='-y';; \?) error 2 "$USAGE";; @@ -316,8 +317,11 @@ for NAME in "$@"; do error 2 "$CHROOT already exists! Specify a second -r to overwrite it (dangerous)." elif [ -n "$RESTORE" ]; then EXISTS='y' - else - sh -e "$BINDIR/unmount-chroot" $YESPARAM -c "$CHROOTS" -- "$NAME" + elif ! sh -e "$BINDIR/unmount-chroot" $YESPARAM \ + -c "$CHROOTS" -- "$NAME"; then + if [ -n "$NEEDS_UNMOUNT" ]; then + exit 1 + fi fi elif [ -n "$RESTORE" ]; then EXISTS='' From 15edb015888cdc00a22ed455034e2cca4e913873 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 20:54:36 -0800 Subject: [PATCH 178/334] Wrapper -X: simplify --- build/wrapper.sh | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/build/wrapper.sh b/build/wrapper.sh index ed1a9b703..e0cbe9f40 100644 --- a/build/wrapper.sh +++ b/build/wrapper.sh @@ -63,18 +63,17 @@ if [ -z "$TRAP" ]; then fi # See if we want to just run a script from the bundle -if [ "$1" = '-X' -a -z "$2" ]; then - ( +if [ "$1" = '-X' ]; then + script="$SCRIPTDIR/$2" + if [ ! -f "$script" ]; then cd "$SCRIPTDIR" echo "USAGE: ${0##*/} -X DIR/SCRIPT [ARGS] Runs a script directly from the bundle. Valid DIR/SCRIPT combos:" 1>&2 ls chroot-bin/* host-bin/* 1>&2 - ) - exit 2 -elif [ "$1" = '-X' ]; then - script="$SCRIPTDIR/$2" - if [ ! -f "$script" ]; then - echo "Invalid script '$2'" 1>&2 + if [ -n "$2" ]; then + echo 1>&2 + echo "Invalid script '$2'" 1>&2 + fi exit 2 fi shift 2 From e270312784857d4c79bd1370ffc118f5458119a0 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 20:56:36 -0800 Subject: [PATCH 179/334] Commenting fix --- chroot-bin/crouton-noroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chroot-bin/crouton-noroot b/chroot-bin/crouton-noroot index 8cbddc84b..08b94fb0e 100755 --- a/chroot-bin/crouton-noroot +++ b/chroot-bin/crouton-noroot @@ -4,7 +4,7 @@ # found in the LICENSE file. # Wrapper for scripts that shouldn't be launched as root. -# Symlink to automatically launch that application. +# Symlink to launch the application with the same name in /usr/bin/ # Launch directly with a parameter to launch that executable # Launch directly with a blank or - first parameter and a word to just check and # exit with an exit code, printing out the second parameter as the app name. From a2e7d49136337acaa451a25560cb5e5b5ab58650 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 20:58:29 -0800 Subject: [PATCH 180/334] Fix SPLIT retreival of argument --- host-bin/edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index a3b2e9e7a..962dfda94 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -83,7 +83,7 @@ while getopts_nextarg; do l) LISTDETAILS=$(($LISTDETAILS+1));; m) MOVE="$getopts_arg"; NEEDS_UNMOUNT='y';; r) RESTORE=$(($RESTORE+1)); NEEDS_UNMOUNT='y';; - s) SPLIT="$OPTARG";; + s) SPLIT="$getopts_arg";; y) YES='a'; YESPARAM='-y';; \?) error 2 "$USAGE";; esac From d97bbe6291c322d8257aa91f934abab45f9b1c19 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 21:04:57 -0800 Subject: [PATCH 181/334] Avoid unsafe usage of mktemp --- host-bin/edit-chroot | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 962dfda94..95f7589e8 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -368,9 +368,10 @@ for NAME in "$@"; do SPLIT="${SPLIT:-4GB}" fi if [ -n "$SPLIT" ]; then - tardest="`mktemp -u --tmpdir=/tmp 'crouton-backup.XXX'`" + tardest="`mktemp -d --tmpdir=/tmp 'crouton-backup.XXX'`" + addtrap "rm -rf '$tardest'" + tardest="$tardest/pipe" mkfifo -m 600 "$tardest" - addtrap "rm -f '$tardest'" split -b "$SPLIT" -d "$tardest" "$dest.part" & splitpid="$!" else @@ -433,12 +434,14 @@ for NAME in "$@"; do echo -n " Restoring $src to $CHROOT..." 1>&2 mkdir -p "$CHROOT" if [ -f "$src.part01" ]; then - tarsrc="`mktemp -u --tmpdir=/tmp 'crouton-restore.XXX'`" + tarsrc="`mktemp -d --tmpdir=/tmp 'crouton-restore.XXX'`" + addtrap "rm -rf '$tarsrc'" + tarsrc="$tarsrc/pipe" mkfifo -m 600 "$tarsrc" # Pipe through dd to eat the EOFs cat "$src" "$src.part"* | dd status=none >> "$tarsrc" & catpid="$!" - addtrap "rm -f '$tarsrc'; kill '$catpid' 2>/dev/null" + addtrap "kill '$catpid' 2>/dev/null" else tarsrc="$src" catpid='' From 0f0f87f8cc72849582115128fb897a0b6bb45d88 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 21:09:26 -0800 Subject: [PATCH 182/334] Posix split doesn't take units. Specify true max filesize for FAT32 --- host-bin/edit-chroot | 2 +- test/tests/12-edit-chroot | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 95f7589e8..94a4aacca 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -365,7 +365,7 @@ for NAME in "$@"; do fi # If we're writing to a fat32 filesystem, split the file at 4GB chunks if ! df -T "`getmountpoint "$dest"`" | awk '$2~"^v?fat"{exit 1}'; then - SPLIT="${SPLIT:-4GB}" + SPLIT="${SPLIT:-4294967295}" fi if [ -n "$SPLIT" ]; then tardest="`mktemp -d --tmpdir=/tmp 'crouton-backup.XXX'`" diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index 4af21182f..f15dba9f5 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -84,7 +84,7 @@ host enter-chroot -n "$release" true host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" # Back up with a split -host edit-chroot -y -s 50MB -f "$BACKUPDIR/$release.tar" -b "$release" +host edit-chroot -y -s 50000000 -f "$BACKUPDIR/$release.tar" -b "$release" test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 du -b "$BACKUPDIR"/* | passes awk '$1>50000000{exit 1}' From 561a5c87bc311da7b79cc195c8bd7d1b5ccc1224 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 21:41:26 -0800 Subject: [PATCH 183/334] Add -T to mv when resuming encryption Hopefully fixes some poor behavior with valid absolute symlinks. --- host-bin/mount-chroot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index c2115fb9f..355e3d8f8 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -298,12 +298,12 @@ no -- You don't want to decide one way or another quite yet. -not -wholename './.crouton-targets' \ -exec mkdir -p "$tmp/{}" ';' \ -exec rmdir "$tmp/{}" ';' \ - '(' -prune , -exec mv -f '{}' "$tmp/{}" ';' ')' 1>&2 + '(' -prune , -exec mv -fT '{}' "$tmp/{}" ';' ')' 1>&2 for tmp in ECRYPTFS_MOVE_STAGING_*; do ( cd "$tmp" find '!' '(' -type d -exec test -d "$CHROOT/{}" ';' ')' \ - '(' -prune , -exec mv -f '{}' "$CHROOT/{}" ';' ')' \ + '(' -prune , -exec mv -fT '{}' "$CHROOT/{}" ';' ')' \ -exec echo -n . ';' 1>&2 find -depth -type d -not -wholename . \ -exec test -d "$CHROOT/{}" ';' \ From 707e1894ac0e51ff44798c33ace0c8c32955bcd3 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 22:04:45 -0800 Subject: [PATCH 184/334] Properly autodetect archive compression with -s --- host-bin/edit-chroot | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 94a4aacca..4d0c4ab05 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -370,7 +370,7 @@ for NAME in "$@"; do if [ -n "$SPLIT" ]; then tardest="`mktemp -d --tmpdir=/tmp 'crouton-backup.XXX'`" addtrap "rm -rf '$tardest'" - tardest="$tardest/pipe" + tardest="$tardest/pipe.${dest##*/}" mkfifo -m 600 "$tardest" split -b "$SPLIT" -d "$tardest" "$dest.part" & splitpid="$!" @@ -434,6 +434,14 @@ for NAME in "$@"; do echo -n " Restoring $src to $CHROOT..." 1>&2 mkdir -p "$CHROOT" if [ -f "$src.part01" ]; then + # Detect the type of compression before sending it through a fifo + for tarparam in -z -j -J -Z --no-auto-compress fail; do + if [ "$tarparam" = 'fail' ]; then + error 2 "Unable to detect compression method of $src" + elif tar $tarparam --test-label -f "$src" >/dev/null 2>&1; then + break + fi + done tarsrc="`mktemp -d --tmpdir=/tmp 'crouton-restore.XXX'`" addtrap "rm -rf '$tarsrc'" tarsrc="$tarsrc/pipe" @@ -444,10 +452,11 @@ for NAME in "$@"; do addtrap "kill '$catpid' 2>/dev/null" else tarsrc="$src" + tarparam='-a' catpid='' fi - spinner 1 tar --checkpoint=200 --checkpoint-action=exec=echo \ - --one-file-system -xaf "$tarsrc" -C "$CHROOT" --strip-components=1 + spinner 1 tar --checkpoint=200 --checkpoint-action=exec=echo $tarparam \ + --one-file-system -xf "$tarsrc" -C "$CHROOT" --strip-components=1 if [ -n "$catpid" ]; then wait "$catpid" fi From 70740175693e3569a5e875fff620e69ccc53b780 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 22:08:44 -0800 Subject: [PATCH 185/334] getmountpoint fixes --- host-bin/edit-chroot | 3 +-- installer/functions | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 4d0c4ab05..a440094bd 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -550,8 +550,7 @@ for NAME in "$@"; do # safety. We don't do this when encrypting a chroot (see mount-chroot), # because that would require 2x the space on one device. When switching # filesystems like this, however, that isn't a concern. - if [ ! "`getmountpoint "$target"`" = \ - "`getmountpoint "$CHROOT"`" ]; then + if [ "`getmountpoint "$target"`" != "`getmountpoint "$CHROOT"`" ]; then echo "Moving $CHROOT across filesystems to $target" 1>&2 echo 'This will take a while.' 1>&2 echo "If the operation gets interrupted, you can safely delete $target" 1>&2 diff --git a/installer/functions b/installer/functions index c96a15191..eab0bad5a 100644 --- a/installer/functions +++ b/installer/functions @@ -206,7 +206,7 @@ validate_name() { # outputs on stdout getmountpoint() { mp="`readlink -m -- "$1"`" - while ! stat -c '%m' "$mp" 2>/dev/null; do + while ! stat -c '%m' "${mp:-/}" 2>/dev/null; do mp="${mp%/*}" done } From b5624124f43fb01cd566a9a0a129405c181a39a6 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 22:14:48 -0800 Subject: [PATCH 186/334] Switch to 5-digit suffixes. FAT32 limits directories to 65534 files, so this should cover things. --- host-bin/edit-chroot | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index a440094bd..e9f2ebe66 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -372,7 +372,7 @@ for NAME in "$@"; do addtrap "rm -rf '$tardest'" tardest="$tardest/pipe.${dest##*/}" mkfifo -m 600 "$tardest" - split -b "$SPLIT" -d "$tardest" "$dest.part" & + split -b "$SPLIT" -a 5 -d "$tardest" "$dest.part" & splitpid="$!" else tardest="$dest" @@ -386,8 +386,8 @@ for NAME in "$@"; do --one-file-system -V "crouton:backup.${date%-*}${date#*-}-$NAME" \ -caf "$tardest" -C "$CHROOTS" "$NAME" || ret="$?" if [ -n "$SPLIT" ]; then - wait "$splitpid" || ret="$?$ret" - mv -f "$dest.part00" "$dest" || ret="$?$ret" + wait "$splitpid" || ret="$?" + mv -f "$dest.part00000" "$dest" || ret="$?" fi if [ "$ret" -ne 0 ]; then echo "Unable to backup $CHROOT." 1>&2 @@ -408,7 +408,8 @@ for NAME in "$@"; do # Search for the alphabetically last tarball with src. # Dated tarballs take precedence over undated tarballs. for file in "$file."* "$file-"*; do - if [ ! -f "$file" -o "${file%part[0-9][0-9]}" != "$file" ]; then + if [ "${file%part[0-9][0-9][0-9][0-9][0-9]}" != "$file" \ + -o ! -f "$file" ]; then continue fi # Confirm it's a tarball From 13fd97bb3dd9b33af80c54c7b4377a71f50d21cf Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 22:21:27 -0800 Subject: [PATCH 187/334] Revert "Add chvt to start* scripts. Fixes #1225." This reverts commit 7b4faa12f3089ac020b94424c3fd6f41ebd210f8. --- host-bin/starte17 | 1 - host-bin/startgnome | 1 - host-bin/startkde | 1 - host-bin/startlxde | 1 - host-bin/startunity | 1 - host-bin/startxbmc | 1 - host-bin/startxfce4 | 1 - 7 files changed, 7 deletions(-) diff --git a/host-bin/starte17 b/host-bin/starte17 index 3c6b0e571..719081194 100755 --- a/host-bin/starte17 +++ b/host-bin/starte17 @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t e17 "$@" "" \ exec xinit /usr/bin/enlightenment_start diff --git a/host-bin/startgnome b/host-bin/startgnome index c4bbc8954..e3e1b5eab 100755 --- a/host-bin/startgnome +++ b/host-bin/startgnome @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t gnome "$@" "" \ exec startgnome diff --git a/host-bin/startkde b/host-bin/startkde index 91507613e..c3a4e55f6 100755 --- a/host-bin/startkde +++ b/host-bin/startkde @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t kde "$@" "" \ exec xinit /usr/bin/startkde diff --git a/host-bin/startlxde b/host-bin/startlxde index 260daba42..bbec40e76 100755 --- a/host-bin/startlxde +++ b/host-bin/startlxde @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t lxde "$@" "" \ exec xinit /usr/bin/startlxde diff --git a/host-bin/startunity b/host-bin/startunity index 0d29866bb..4beea9ab4 100755 --- a/host-bin/startunity +++ b/host-bin/startunity @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t unity "$@" "" \ exec startunity diff --git a/host-bin/startxbmc b/host-bin/startxbmc index bc274a762..5af361e15 100755 --- a/host-bin/startxbmc +++ b/host-bin/startxbmc @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t xbmc "$@" "" \ exec xinit /usr/bin/xbmc diff --git a/host-bin/startxfce4 b/host-bin/startxfce4 index f8eb1082c..9e9ca881a 100755 --- a/host-bin/startxfce4 +++ b/host-bin/startxfce4 @@ -14,6 +14,5 @@ By default, it will log into the primary user on the first chroot found. Options are directly passed to enter-chroot; run enter-chroot to list them." -chvt 1 2>/dev/null || true exec sh -e "`dirname "\`readlink -f "$0"\`"`/enter-chroot" -t xfce "$@" "" \ exec startxfce4 From 1bb5a33b02dc3338ac4c02eead6c27ac8f3542f9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 22:24:04 -0800 Subject: [PATCH 188/334] Switch to VT1 in xinit. Fixes #1225 --- chroot-bin/xinit | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chroot-bin/xinit b/chroot-bin/xinit index f1256a9d3..8140fdb79 100755 --- a/chroot-bin/xinit +++ b/chroot-bin/xinit @@ -28,4 +28,7 @@ while [ -f "/tmp/.X$disp-lock" ]; do disp=$((disp+1)) done +# If possible, switch to VT1 to avoid strangeness when launching from VT2 +chvt 1 2>/dev/null || true + exec /usr/bin/xinit /usr/local/bin/croutonxinitrc-wrapper "$@" $dash $xserverrc ":$disp" From 81e993659663b82efdb46628c52c61b6e87d398c Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 4 Jan 2015 23:50:26 -0800 Subject: [PATCH 189/334] Change syntax of shares to be more like fstab --- host-bin/enter-chroot | 117 ++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 83810155d..4db641e59 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -399,25 +399,28 @@ elif [ ! -f "$shares" ]; then # downloads: ~/Downloads # shared: /mnt/stateful_partition/crouton/shared # encrypted: ~/crouton/shared -# Add a line with a single keyword to modify the mount options of all the bind -# mounts that follow. For example, the following will make all following mounts -# be bind-mounted with noexec and suid set. -# noexec,suid -# The default bind mount options are "exec". +# Syntax is as follows: +# HOSTDIR CHROOTDIR [OPTIONS] +# Directory names can have spaces (if quoted), but no quote characters or +# relative path elements (..'s). +# Options can be any options to mount. If unspecified, "exec" is assumed. # Examples: # Share a home directory encrypted by the current user's unsynced profile -# encrypted/home -> ~ +# encrypted/home ~ # Share an unencrypted directory between chroots. -# shared/home -> ~/Share +# shared/home ~/Share +# Absolute directories on the chroot side work too +# shared/bin /usr/local/bin exec,suid +# Share a noexec path that has spaces, and a specific user home directory +# "encrypted/stupid example" "~omg/Why are you even doing this?" noexec # Share the downloads folder of the current user's profile -downloads -> ~/Downloads +downloads ~/Downloads EOF fi # Bind-mount the stuff in $CHROOT/etc/crouton/shares, unless NOLOGIN is set if [ -z "$NOLOGIN" -a -f "$shares" ]; then - mountparams='exec' localdownloads='/home/chronos/user/Downloads' if [ ! -d "$localdownloads" ]; then localdownloads='' @@ -427,57 +430,73 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then localencrypted='' fi localshare='/mnt/stateful_partition/crouton/shared' - # Allow line continuation characters (no -r) and strip leading/trailing - # whitespace, remove comments, and remove spaces around -> - sed 'y/\t/ /; s/^ *//; s/ *$//; s/ *-> */->/; /^#/d;' "$shares" \ - | while read line; do - src="${line%%->*}" - # If -> is not present, it's a specification of mountparams - if [ "$src" = "$line" ]; then - mountparams="${line#[\"\']}" - mountparams="${mountparams%[\"\']}" - continue - fi - # Remove quoting from src and ensure ends in / - if [ "${src#[\"\']}" != "$src" ]; then - src="${src#[\"\']}" - src="${src%[\"\']}" - fi - src="${src%/}/" - # Grab dest, remove quote, ensure ends in / - dest="${line#*->}" - if [ "${dest#[\"\']}" != "$dest" ]; then - dest="${dest#[\"\']}" - dest="${dest%[\"\']}" - fi - dest="${dest%/}/" - # .. path elements are not allowed - if [ "${src%/../*}" != "$src" -o "${dest%/../*}" != "$dest" ]; then - echo "Invalid share: $line" 1>&2 - continue - fi - # Expand src with a little bit of leniency + # Parse file and split into three lines: src, dest, options + awk ' + BEGIN { + PARAMMATCH = "^[ \t]*(\"[^\"]+\"|[^ \t\"]+)([ \t]" + } + function strip(s) { + if (index(s, "\"")) { + sub("[ \t]*\"", "", s) + sub("\"[ \t]*", "", s) + } else { + gsub("[ \t]", "", s) + } + return s + } + $1 ~ /^#/ { next } + match($0, PARAMMATCH ")") { + dest = substr($0, RLENGTH+1) + src = strip(substr($0, 1, RLENGTH)) + if (match(dest, PARAMMATCH "|$)")) { + opts = strip(substr(dest, RLENGTH+1)) + dest = strip(substr(dest, 1, RLENGTH)) + if (length(src) && length(dest) && !match(opts, "[^a-z,]")) { + # Ensure that they end in slashes + if (substr(src, length(src)) != "/") + src = src "/" + if (substr(dest, length(dest)) != "/") + dest = dest "/" + # .. path elements are not allowed + if (!index(src, "/../") && !index(dest, "/../")) { + print src + print dest + print opts + next + } + } + } + } + /./ { + print "Invalid syntax in /etc/crouton/shares on line " NR ":\n " \ + $0 > "/dev/stderr" + } + ' "$shares" | while read -r src && read -r dest && read -r opts; do + line="\n \"$src\" \"$dest\" $opts" + # Expand src case "${src%%/*}" in - download*) + download|downloads) if [ -z "$localdownloads" ]; then - echo "Not mounting share (no Chromium OS user): $line" 1>&2 + echo "Not mounting share (no Chromium OS user):$line" 1>&2 continue fi src="$localdownloads/${src#*/}";; - encrypt*) + encrypt|encrypted) if [ -z "$localencrypted" ]; then - echo "Not mounting share (no Chromium OS user): $line" 1>&2 + echo "Not mounting share (no Chromium OS user):$line" 1>&2 continue fi src="$localencrypted/${src#*/}";; - share*) src="$localshare/${src#*/}";; - *) echo "Invalid share: $line" 1>&2 + share|shares|shared) + src="$localshare/${src#*/}";; + *) + echo "Invalid share:$line" 1>&2 continue;; esac # Expand dest for homedirs if [ "${dest#[~/]}" = "$dest" ]; then if [ -z "$CHROOTHOME" ]; then - echo "Not mounting share (no chroot user): $line" 1>&2 + echo "Not mounting share (no chroot user):$line" 1>&2 continue fi dest="$CHROOTHOME/$dest" @@ -485,7 +504,7 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then destuser="${dest%%/*}" if [ "$destuser" = '~' ]; then if [ -z "$CHROOTHOME" ]; then - echo "Not mounting share (no chroot user): $line" 1>&2 + echo "Not mounting share (no chroot user):$line" 1>&2 continue fi dest="$CHROOTHOME/${dest#*/}" @@ -495,8 +514,8 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then fi # Do the bindmount mkdir -m 700 -p "$src" - if ! bindmount "$src" "$dest" "$mountparams"; then - echo "Failed to mount share: $line" 1>&2 + if ! bindmount "$src" "$dest" "${opts:-exec}"; then + echo "Failed to mount share:$line" 1>&2 fi done fi From 514155ba5db127747fd6fab148d1125ff978dad6 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 5 Jan 2015 15:23:50 +0100 Subject: [PATCH 190/334] websocket.h: Refactor popen2, do not leak fds. Close the unneeded ends of the pipes in the parent to prevent fd leakage. Fixes #1286. This has the advantage of throwing a POLLHUP event when the child closes its stdout. Refactor popen2 accordingly: Do all input/output first, then check exit status. This allows success/error paths to share more code. Also, return an error if not all data can be written; and waitpid(pid, NULL, 10) never worked (the 3rd parameter is not a timeout). Test code: ``` // gcc -Wall test.c && (ulimit -n 16; ./a.out) #include "websocket.h" int main() { char buffer[BUFFERSIZE]; int i, status; verbose = 4; for (i = 0; i < 16; i++) { memset(buffer, 0, BUFFERSIZE); status = popen2("sha1sum", NULL, "1234", 4, buffer, BUFFERSIZE); trueorabort(status >= 40, "Answer too short"); trueorabort(!strncmp(buffer, "7110eda4d09e062aa5e4a390b0a572ac0d2c0220", 40), "Invalid sha1sum"); status = popen2("dummy1sum", NULL, "1234", 4, buffer, BUFFERSIZE); trueorabort(status < 0, "popen2 did not fail"); } return 0; } ``` --- src/websocket.h | 112 +++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 50 deletions(-) diff --git a/src/websocket.h b/src/websocket.h index f908bbcd9..5def60653 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -142,13 +142,15 @@ static int popen2(char* cmd, char *const argv[], pid_t pid = 0; int stdin_fd[2]; int stdout_fd[2]; - int ret = -1; if (pipe(stdin_fd) < 0 || pipe(stdout_fd) < 0) { syserror("Failed to create pipe."); return -1; } + log(3, "pipes: in %d/%d; out %d/%d", + stdin_fd[0], stdin_fd[1], stdout_fd[0], stdout_fd[1]); + pid = fork(); if (pid < 0) { @@ -173,32 +175,31 @@ static int popen2(char* cmd, char *const argv[], /* Parent */ - /* Write input, and read output, while waiting for process termination. - * This could be done without polling, by reacting on SIGCHLD, but this is - * good enough for our purpose, and slightly simpler. */ + /* Close uneeded halves (those are used by the child) */ + close(stdin_fd[0]); + close(stdout_fd[1]); + + /* Write input, and read output. We rely on POLLHUP getting set on stdout + * when the process exits (this assumes the process does not do anything + * strange like closing stdout and staying alive). */ struct pollfd fds[2]; fds[0].events = POLLIN; fds[0].fd = stdout_fd[0]; fds[1].events = POLLOUT; fds[1].fd = stdin_fd[1]; - pid_t wait_pid; - int status = 0; - int readlen = 0; + int readlen = 0; /* Also acts as return value */ int writelen = 0; while (1) { - /* Get child status */ - wait_pid = waitpid(pid, &status, WNOHANG); - /* Check if there is data to read, no matter the process status. */ - /* Timeout after 10ms, or immediately if the process exited already */ - int polln = poll(fds, 2, (wait_pid == pid) ? 0 : 10); + int polln = poll(fds, 2, -1); if (polln < 0) { syserror("poll error."); - goto error; + readlen = -1; + break; } - log(3, "poll=%d (%d)", polln, (wait_pid == pid)); + log(3, "poll=%d", polln); /* We can write something to stdin */ if (fds[1].revents & POLLOUT) { @@ -206,7 +207,8 @@ static int popen2(char* cmd, char *const argv[], int n = write(stdin_fd[1], input + writelen, inlen - writelen); if (n < 0) { error("write error."); - goto error; + readlen = -1; + break; } log(3, "write n=%d/%d", n, inlen); writelen += n; @@ -218,7 +220,13 @@ static int popen2(char* cmd, char *const argv[], stdin_fd[1] = -1; fds[1].fd = -1; } - polln--; + fds[1].revents &= ~POLLOUT; + } + + if (fds[1].revents != 0) { + error("Unknown poll event on stdout (%d).", fds[1].revents); + readlen = -1; + break; } /* We can read something from stdout */ @@ -226,7 +234,8 @@ static int popen2(char* cmd, char *const argv[], int n = read(stdout_fd[0], output + readlen, outlen - readlen); if (n < 0) { error("read error."); - goto error; + readlen = -1; + break; } log(3, "read n=%d", n); readlen += n; @@ -237,50 +246,53 @@ static int popen2(char* cmd, char *const argv[], if (readlen >= outlen) { error("Output too long."); - ret = readlen; - goto error; + break; } - polln--; + fds[0].revents &= ~POLLIN; } - if (polln != 0) { - error("Unknown poll event (%d).", fds[0].revents); - goto error; - } - - if (wait_pid == -1) { - error("waitpid error."); - goto error; - } else if (wait_pid == pid) { - log(3, "child exited!"); - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - error("child exited with status %d", WEXITSTATUS(status)); - ret = -WEXITSTATUS(status); - goto error; - } - } else { - error("child process did not exit: %d", status); - ret = -1; - goto error; - } + /* stdout has hung up (process terminated) */ + if (fds[0].revents == POLLHUP) { + log(3, "pollhup"); + break; + } else if (fds[0].revents != 0) { + error("Unknown poll event on stdin (%d).", fds[0].revents); + readlen = -1; break; } } - if (stdin_fd[1] >= 0) - close(stdin_fd[1]); - close(stdout_fd[0]); - return readlen; - -error: if (stdin_fd[1] >= 0) close(stdin_fd[1]); /* Closing the stdout pipe forces the child process to exit */ close(stdout_fd[0]); - /* Try to wait 10ms for the process to exit, then bail out. */ - waitpid(pid, NULL, 10); - return ret; + + /* Get child status (no timeout: we assume the child behaves well) */ + int status = 0; + pid_t wait_pid = waitpid(pid, &status, 0); + + if (wait_pid != pid) { + syserror("waitpid error."); + return -1; + } + + if (WIFEXITED(status)) { + log(3, "child exited!"); + if (WEXITSTATUS(status) != 0) { + error("child exited with status %d", WEXITSTATUS(status)); + return -WEXITSTATUS(status); + } + } else { + error("child process did not exit: %d", status); + return -1; + } + + if (writelen != inlen) { + error("Incomplete write."); + return -1; + } + + return readlen; } /**/ From 47e3cb701242d5b6acf7a61ed1d452371ed351ef Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 09:04:10 -0800 Subject: [PATCH 191/334] edit-chroot: Fix renaming --- host-bin/edit-chroot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index e9f2ebe66..93d9d4a22 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -154,8 +154,8 @@ elif [ $# -gt 1 -a -f "$TARBALL" ]; then error 2 "Multiple chroots specified, but $TARBALL is not a directory." fi -# Don't allow moving to non-ext filesystems -if [ -n "$MOVE" ] && \ +# Don't allow moving to non-ext filesystems (but don't check if just renaming) +if [ -n "$MOVE" -a "${MOVE#*/}" = "$MOVE" ] && \ df -T "`getmountpoint "$MOVE"`" | awk '$2~"^ext"{exit 1}'; then error 2 "Chroots can only be moved to ext filesystems." fi From 221cf8ea8c52fe99cef74871f6924fab4b78a161 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 09:21:52 -0800 Subject: [PATCH 192/334] edit-chroot -s should be in MB --- host-bin/edit-chroot | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 93d9d4a22..2888653fd 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -63,7 +63,8 @@ Options: Will not overwrite a chroot when restoring unless -r is specified twice. -s SPLIT Force a backup archive to be split into SPLIT-sized chunks. - FAT32 filesystems are split by default to 4GB. + SPLIT is specified in megabytes (1048576 bytes). + FAT32 filesystems are split by default to fit within 4GB. -y Do all actions without confirmation." # Common functions @@ -124,6 +125,11 @@ if [ -n "$DELETE" -a -n "$BACKUP$ENCRYPT$KEYFILE$MOVE$RESTORE" ]; then error 2 "$USAGE" fi +# Make sure SPLIT is reasonable +if [ -n "$SPLIT" ] && [ ! "$SPLIT" -gt 0 -o -z "$BACKUP" ]; then + error 2 "$USAGE" +fi + # If we specified -a option, bring in all chroots. if [ -n "$ALLCHROOTS" ]; then if [ ! -d "$CHROOTS" ]; then @@ -365,14 +371,14 @@ for NAME in "$@"; do fi # If we're writing to a fat32 filesystem, split the file at 4GB chunks if ! df -T "`getmountpoint "$dest"`" | awk '$2~"^v?fat"{exit 1}'; then - SPLIT="${SPLIT:-4294967295}" + SPLIT="${SPLIT:-4095}" fi if [ -n "$SPLIT" ]; then tardest="`mktemp -d --tmpdir=/tmp 'crouton-backup.XXX'`" addtrap "rm -rf '$tardest'" tardest="$tardest/pipe.${dest##*/}" mkfifo -m 600 "$tardest" - split -b "$SPLIT" -a 5 -d "$tardest" "$dest.part" & + split -b "$((SPLIT*1048576))" -a 5 -d "$tardest" "$dest.part" & splitpid="$!" else tardest="$dest" From 20318d7a5b6433efbd61157011433fd5ecedf9ea Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 09:22:06 -0800 Subject: [PATCH 193/334] Fix restoration of split chroot backups. Thanks to @drinkcat for figuring this out. --- host-bin/edit-chroot | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 2888653fd..d4dbe20ec 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -449,12 +449,13 @@ for NAME in "$@"; do break fi done + # Don't let tar get tripped up by cat's incomplete writes + tarparam="$tarparam -B" tarsrc="`mktemp -d --tmpdir=/tmp 'crouton-restore.XXX'`" addtrap "rm -rf '$tarsrc'" tarsrc="$tarsrc/pipe" mkfifo -m 600 "$tarsrc" - # Pipe through dd to eat the EOFs - cat "$src" "$src.part"* | dd status=none >> "$tarsrc" & + cat "$src" "$src.part"* >> "$tarsrc" & catpid="$!" addtrap "kill '$catpid' 2>/dev/null" else From 2e9911be2dd16a86b5d47510368bd9217581f82e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 09:35:57 -0800 Subject: [PATCH 194/334] Clarify dest path restrictions and validation --- host-bin/enter-chroot | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 4db641e59..3cda24e5d 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -399,6 +399,7 @@ elif [ ! -f "$shares" ]; then # downloads: ~/Downloads # shared: /mnt/stateful_partition/crouton/shared # encrypted: ~/crouton/shared +# Inside the chroot, any absolute or user-relative path (~/x or ~user/x) is OK. # Syntax is as follows: # HOSTDIR CHROOTDIR [OPTIONS] # Directory names can have spaces (if quoted), but no quote characters or @@ -451,14 +452,14 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then if (match(dest, PARAMMATCH "|$)")) { opts = strip(substr(dest, RLENGTH+1)) dest = strip(substr(dest, 1, RLENGTH)) - if (length(src) && length(dest) && !match(opts, "[^a-z,]")) { + if (src && dest ~ "^[/~]" && opts ~ "^[a-z,]*$") { # Ensure that they end in slashes - if (substr(src, length(src)) != "/") + if (src !~ "/$") src = src "/" - if (substr(dest, length(dest)) != "/") + if (dest !~ "/$") dest = dest "/" # .. path elements are not allowed - if (!index(src, "/../") && !index(dest, "/../")) { + if (src !~ "/../" && dest !~ "/../") { print src print dest print opts @@ -494,13 +495,7 @@ if [ -z "$NOLOGIN" -a -f "$shares" ]; then continue;; esac # Expand dest for homedirs - if [ "${dest#[~/]}" = "$dest" ]; then - if [ -z "$CHROOTHOME" ]; then - echo "Not mounting share (no chroot user):$line" 1>&2 - continue - fi - dest="$CHROOTHOME/$dest" - elif [ "${dest#~}" != "$dest" ]; then + if [ "${dest#~}" != "$dest" ]; then destuser="${dest%%/*}" if [ "$destuser" = '~' ]; then if [ -z "$CHROOTHOME" ]; then From 0281e1fe8608d13df2162413a5baaf347610d58b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 09:45:26 -0800 Subject: [PATCH 195/334] Test compressed, split chroot backup/restore --- test/tests/12-edit-chroot | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index f15dba9f5..766ed8f21 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -83,14 +83,16 @@ host enter-chroot -n "$release" true # Verify croutonversion outputs correct information host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" -# Back up with a split -host edit-chroot -y -s 50000000 -f "$BACKUPDIR/$release.tar" -b "$release" +# Back up with a compressed split +host edit-chroot -y -s 50 -f "$BACKUPDIR/$release.tar.gz" -b "$release" test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 du -b "$BACKUPDIR"/* | passes awk '$1>50000000{exit 1}' +# Ensure it's compressed +test "`hexdump -v -n2 -e'"%02x"' "$BACKUPDIR/$release.tar.gz"`" = "8b1f" # Restore the split archive. Fails first due to existence -fails host edit-chroot -y -f "$BACKUPDIR/$release.tar" -r "$release" -host edit-chroot -y -f "$BACKUPDIR/$release.tar" -rr "$release" +fails host edit-chroot -y -f "$BACKUPDIR/$release.tar.gz" -r "$release" +host edit-chroot -y -f "$BACKUPDIR/$release.tar.gz" -rr "$release" host enter-chroot -n "$release" true # Delete it From bf34d3f9905c2f796e3dec8dcf7f9ced616a971d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 10:18:53 -0800 Subject: [PATCH 196/334] Use POSIX split, and don't allow split sizes smaller than 10MB. --- host-bin/edit-chroot | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index d4dbe20ec..caa0b5254 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -63,7 +63,8 @@ Options: Will not overwrite a chroot when restoring unless -r is specified twice. -s SPLIT Force a backup archive to be split into SPLIT-sized chunks. - SPLIT is specified in megabytes (1048576 bytes). + SPLIT is specified in megabytes (1048576 bytes), and cannot be + smaller than 10. FAT32 filesystems are split by default to fit within 4GB. -y Do all actions without confirmation." @@ -126,7 +127,7 @@ if [ -n "$DELETE" -a -n "$BACKUP$ENCRYPT$KEYFILE$MOVE$RESTORE" ]; then fi # Make sure SPLIT is reasonable -if [ -n "$SPLIT" ] && [ ! "$SPLIT" -gt 0 -o -z "$BACKUP" ]; then +if [ -n "$SPLIT" ] && [ "$SPLIT" -lt 10 -o -z "$BACKUP" ]; then error 2 "$USAGE" fi @@ -378,7 +379,7 @@ for NAME in "$@"; do addtrap "rm -rf '$tardest'" tardest="$tardest/pipe.${dest##*/}" mkfifo -m 600 "$tardest" - split -b "$((SPLIT*1048576))" -a 5 -d "$tardest" "$dest.part" & + split -b "${SPLIT}m" -a 4 "$tardest" "$dest.part-" & splitpid="$!" else tardest="$dest" @@ -386,14 +387,14 @@ for NAME in "$@"; do fi echo -n " Backing up $CHROOT to $dest..." 1>&2 addtrap "echo 'Deleting partial archive.' 1>&2; \ - kill '$splitpid' 2>/dev/null; rm -f '$dest' '$dest.part'*" + kill '$splitpid' 2>/dev/null; rm -f '$dest' '$dest.part-'*" ret=0 spinner 1 tar --checkpoint=100 --checkpoint-action=exec=echo \ --one-file-system -V "crouton:backup.${date%-*}${date#*-}-$NAME" \ -caf "$tardest" -C "$CHROOTS" "$NAME" || ret="$?" if [ -n "$SPLIT" ]; then wait "$splitpid" || ret="$?" - mv -f "$dest.part00000" "$dest" || ret="$?" + mv -f "$dest.part-aaaa" "$dest" || ret="$?" fi if [ "$ret" -ne 0 ]; then echo "Unable to backup $CHROOT." 1>&2 @@ -414,7 +415,7 @@ for NAME in "$@"; do # Search for the alphabetically last tarball with src. # Dated tarballs take precedence over undated tarballs. for file in "$file."* "$file-"*; do - if [ "${file%part[0-9][0-9][0-9][0-9][0-9]}" != "$file" \ + if [ "${file%.part-[a-z][a-z][a-z][a-z]}" != "$file" \ -o ! -f "$file" ]; then continue fi From 4ecf9681b52a49063279ed692c5d3bc3556934f2 Mon Sep 17 00:00:00 2001 From: divx118 Date: Mon, 5 Jan 2015 19:20:56 +0100 Subject: [PATCH 197/334] Adjusted according to comments and looks cleaner now. --- targets/common | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/targets/common b/targets/common index 03f769c3c..70ef712b0 100644 --- a/targets/common +++ b/targets/common @@ -33,17 +33,12 @@ if [ "$TARGETS" = 'help' ]; then echo " Requires: $REQUIRES" fi elif [ "$TARGETNOINSTALL" = "p" ]; then - # Avoid adding things already provided - # This will only check the provides from /etc/crouton/targets - # and targets specified with -t. + # This will check the provides from /etc/crouton/targets and targets + # specified with -t. If there are duplicates we will respect the + # order of the targets specified. This means on an update targets + # specified with -t will have higher priority then already installed targets. for t in $PROVIDES; do - # Don't add duplicate PROVIDES. If there are duplicates - # we will respect the order of the targets specified. - # This means on an update targets specified with -t - # will have higher priority then already installed targets. - if ! grep -q "^$t=.*\$" "${PROVIDESFILE:-/dev/null}"; then - echo "$t=$TARGET" >> "${PROVIDESFILE:-/dev/null}" - fi + echo "$t=$TARGET" >> "${PROVIDESFILE:-/dev/null}" done elif [ -z "$TARGETNOINSTALL" -o -n "$RESTOREHOSTBIN" ]; then # Avoid double-adding targets @@ -58,10 +53,9 @@ elif [ -z "$TARGETNOINSTALL" -o -n "$RESTOREHOSTBIN" ]; then done # Source the prerequisites for t in $REQUIRES; do - # If it is already provided change it to the target what provides it. - if grep -q "^$t=.*\$" "${PROVIDESFILE:-/dev/null}"; then - t="`awk -F= '/'$t'=/{print $2}' "${PROVIDESFILE:-/dev/null}"`" - fi + # If it is already provided change it to the target what provides it. + provider="`awk -F= '/'"$t"'=/{print $2; exit}' "${PROVIDESFILE:-/dev/null}"`" + t="${provider:-"$t"}" (TARGET="$t" PROVIDES='' HOSTBIN='' CHROOTBIN='' CHROOTETC='' . "$TARGETSDIR/$t") done From 1f71dcc5e5d234b46f2073cabfb56dffdbfa5548 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 10:21:46 -0800 Subject: [PATCH 198/334] Fix edit-chroot split test --- test/tests/12-edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index 766ed8f21..f939905f1 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -86,7 +86,7 @@ host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release # Back up with a compressed split host edit-chroot -y -s 50 -f "$BACKUPDIR/$release.tar.gz" -b "$release" test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 -du -b "$BACKUPDIR"/* | passes awk '$1>50000000{exit 1}' +du -b "$BACKUPDIR"/* | passes awk '$1>50*1024*1024{exit 1}' # Ensure it's compressed test "`hexdump -v -n2 -e'"%02x"' "$BACKUPDIR/$release.tar.gz"`" = "8b1f" From 63f7adf2fcaec5642486a8190b2ed01b5d660a43 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 18:13:15 -0800 Subject: [PATCH 199/334] Fix restoration split detection --- host-bin/edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index caa0b5254..b89d90693 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -441,7 +441,7 @@ for NAME in "$@"; do fi echo -n " Restoring $src to $CHROOT..." 1>&2 mkdir -p "$CHROOT" - if [ -f "$src.part01" ]; then + if [ -f "$src.part-aaab" ]; then # Detect the type of compression before sending it through a fifo for tarparam in -z -j -J -Z --no-auto-compress fail; do if [ "$tarparam" = 'fail' ]; then From 2fa9967f91264b85467d0492215420e0ae7e30f3 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 18:42:39 -0800 Subject: [PATCH 200/334] Fix 12-edit-chroot test --- test/tests/12-edit-chroot | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index f939905f1..cbe6d7874 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -82,11 +82,12 @@ host edit-chroot -y -f "$BACKUPDIR/$release.tar" -r "$release" host enter-chroot -n "$release" true # Verify croutonversion outputs correct information host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" +rm "$BACKUPDIR/$release.tar" # Back up with a compressed split host edit-chroot -y -s 50 -f "$BACKUPDIR/$release.tar.gz" -b "$release" test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 -du -b "$BACKUPDIR"/* | passes awk '$1>50*1024*1024{exit 1}' +du -b "$BACKUPDIR/$release.tar.gz"* | passes awk '$1>50*1024*1024{exit 1}' # Ensure it's compressed test "`hexdump -v -n2 -e'"%02x"' "$BACKUPDIR/$release.tar.gz"`" = "8b1f" @@ -100,10 +101,10 @@ host delete-chroot -y "$release" fails host enter-chroot -n "$release" true # Restore the chroot using the crouton installer (without update) -crouton -f "$BACKUPDIR/$release.tar" -n "$release" +crouton -f "$BACKUPDIR/$release.tar.gz" -n "$release" host enter-chroot -n "$release" true host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" -rm "$BACKUPDIR/$release.tar" +rm "$BACKUPDIR/$release.tar.gz*" # Backup a chroot with automatic naming host edit-chroot -y -f "$BACKUPDIR" -b "$release" From a7b006408438d8805d8f0f7abf64111ffcde2805 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 19:00:31 -0800 Subject: [PATCH 201/334] Fix 12-edit-chroot for realsies, definitely --- test/tests/12-edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index cbe6d7874..d0174c6cb 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -86,7 +86,7 @@ rm "$BACKUPDIR/$release.tar" # Back up with a compressed split host edit-chroot -y -s 50 -f "$BACKUPDIR/$release.tar.gz" -b "$release" -test "`ls "$BACKUPDIR"/* | wc -l`" -ge 3 +test "`ls "$BACKUPDIR/$release.tar.gz"* | wc -l`" -ge 2 du -b "$BACKUPDIR/$release.tar.gz"* | passes awk '$1>50*1024*1024{exit 1}' # Ensure it's compressed test "`hexdump -v -n2 -e'"%02x"' "$BACKUPDIR/$release.tar.gz"`" = "8b1f" From 7e0416964871d1f90c754ecf2c714917c690b19d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 5 Jan 2015 20:56:20 -0800 Subject: [PATCH 202/334] Oh yeah, so much fixing of 12-edit-chroot going on here this test is so fixed it's amazing --- test/tests/12-edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/12-edit-chroot b/test/tests/12-edit-chroot index d0174c6cb..a46b85535 100644 --- a/test/tests/12-edit-chroot +++ b/test/tests/12-edit-chroot @@ -104,7 +104,7 @@ fails host enter-chroot -n "$release" true crouton -f "$BACKUPDIR/$release.tar.gz" -n "$release" host enter-chroot -n "$release" true host edit-chroot -l $release | tee /dev/stderr | passes grep "^release: $release" -rm "$BACKUPDIR/$release.tar.gz*" +rm "$BACKUPDIR/$release.tar.gz"* # Backup a chroot with automatic naming host edit-chroot -y -f "$BACKUPDIR" -b "$release" From 3722f49c0d2de6c33b618b4bc744ee1e2faf4bca Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 7 Jan 2015 12:12:50 -0800 Subject: [PATCH 203/334] Revert "Sends username and passphrase prompts to /dev/tty when necessary" This reverts commit 9df99a48bf570a7a66c281bcc48f0742c1f03ce1. Conflicts: installer/functions --- host-bin/mount-chroot | 22 ++++++++++------------ installer/functions | 19 ------------------- targets/post-common | 13 +++++-------- 3 files changed, 15 insertions(+), 39 deletions(-) diff --git a/host-bin/mount-chroot b/host-bin/mount-chroot index 355e3d8f8..0b1e6eea6 100755 --- a/host-bin/mount-chroot +++ b/host-bin/mount-chroot @@ -59,30 +59,29 @@ fi addtrap "stty echo 2>/dev/null" # Function to prompt the user for a passphrase. Sets $passphrase. -promptNewPassphrase() { ( - stdout_to_tty - echo -n "Choose an encryption passphrase for $NAME: " +promptNewPassphrase() { + echo -n "Choose an encryption passphrase for $NAME: " 1>&2 [ -t 0 ] && stty -echo while [ -z "$passphrase" ]; do read -r passphrase if [ -z "$passphrase" ]; then - echo - echo -n 'You must specify a passphrase: ' + echo 1>&2 + echo -n 'You must specify a passphrase: ' 1>&2 continue fi echo 1>&2 - echo -n 'Please confirm your passphrase: ' + echo -n 'Please confirm your passphrase: ' 1>&2 read -r confirmation if [ ! "$confirmation" = "$passphrase" ]; then passphrase='' - echo - echo -n 'Passphrases do not match; try again: ' + echo 1>&2 + echo -n 'Passphrases do not match; try again: ' 1>&2 fi confirmation='' done [ -t 0 ] && stty echo - echo -) } + echo 1>&2 +} # Mount each chroot for NAME in "$@"; do @@ -140,8 +139,7 @@ for NAME in "$@"; do # Ensure that there's a root password set before decrypting the chroot, # unless the passphrase was specified via env, which isn't secure anyway if [ ! -f '/mnt/stateful_partition/etc/devmode.passwd' ]; then - stdout_to_tty \ -'You must have a root password in Chromium OS to mount encrypted chroots.' + echo 'You must have a root password in Chromium OS to mount encrypted chroots.' 1>&2 if [ -z "$CROUTON_PASSPHRASE$CROUTON_NEW_PASSPHRASE" ]; then while ! chromeos-setdevpasswd; do :; done fi diff --git a/installer/functions b/installer/functions index eab0bad5a..929e89828 100644 --- a/installer/functions +++ b/installer/functions @@ -211,25 +211,6 @@ getmountpoint() { done } -# Redirects stdout to stderr or /dev/tty, or prints parameters onto tty if stdin -# is a tty but stderr is not -stdout_to_tty() { - # If we have a tty input but the output is redirected, output prompt to tty - if [ -t 0 -a ! -t 2 ]; then - if [ $# = 0 ]; then - exec 1>/dev/tty - else - echo "$*" 1>/dev/tty - fi - else - if [ $# = 0 ]; then - exec 1>&2 - else - echo "$*" 1>&2 - fi - fi -} - # Websocket interface PIPEDIR='/tmp/crouton-ext' CRIATDISPLAY="$PIPEDIR/kiwi-display" diff --git a/targets/post-common b/targets/post-common index 852b00d54..09f261b10 100644 --- a/targets/post-common +++ b/targets/post-common @@ -34,20 +34,17 @@ if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then apt-get clean fi -# From here on out is interactive stuff, so ensure we're printing to the user -stdout_to_tty - # Add the primary user groups='audio,video,sudo,plugdev' if ! grep -q ':1000:' /etc/passwd; then while ! echo "$username" | grep -q '^[a-z][-a-z0-9_]*$'; do if [ -n "$username" ]; then - echo 'Username must be lowercase letters, numbers, or dashes.' + echo 'Username must be lowercase letters, numbers, or dashes.' 1>&2 fi - echo -n 'Please specify a username for the primary user: ' + echo -n 'Please specify a username for the primary user: ' 1>&2 if [ -n "$USERNAME" ]; then username="$USERNAME" - echo "$username" + echo "$username" 1>&2 else read -r username junk fi @@ -63,7 +60,7 @@ if ! grep -q ':1000:' /etc/passwd; then done if [ "$tries" = 3 ]; then echo \ -"Password left unset. To set a password, inside the chroot run: passwd $username" +"Password left unset. To set a password, inside the chroot run: passwd $username" 1>&2 fi else username="`awk -F: '$3==1000{print $1; exit}' '/etc/passwd'`" @@ -73,7 +70,7 @@ fi if [ -n "$TIPS" ]; then echo " Here's some tips: -$TIPS" +$TIPS" 1>&2 fi rm -f "$0" From 3b348ad595928ed6b6144a43ff1f009a20eed566 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 7 Jan 2015 12:24:37 -0800 Subject: [PATCH 204/334] Require successful unmount for delete-chroot. Thanks to @divx118 for pointing this out. --- host-bin/edit-chroot | 1 + 1 file changed, 1 insertion(+) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index b89d90693..86fbf99ac 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -94,6 +94,7 @@ done # If the executable name is delete*, assume DELETE. if [ ! "${APPLICATION#delete}" = "$APPLICATION" ]; then DELETE='y' + NEEDS_UNMOUNT='y' fi # At least one command must be specified From 08f7e33013317900a315c5ea0707c113f660a6fb Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sat, 10 Jan 2015 15:33:00 +0100 Subject: [PATCH 205/334] fbserver-proto: Pass X11 keycode instead of X11 keysym Instead of transmitting a X11 key symbol (e.g. letter 'A'), which depends on the keyboard layout used in Chromium OS, pass a key code (i.e. position on the keyboard), and let X11 translate it to the appropriate symbol, depending on the keyboard layout in the chroot. We keep old structure in fbserver-proto.h, for backward compatibility. --- src/fbserver-proto.h | 10 +++++++++- src/fbserver.c | 15 +++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/fbserver-proto.h b/src/fbserver-proto.h index 4f060c1e3..2347d4dd3 100644 --- a/src/fbserver-proto.h +++ b/src/fbserver-proto.h @@ -12,7 +12,7 @@ #include /* WebSocket constants */ -#define VERSION "VF1" +#define VERSION "VF2" #define PORT_BASE 30010 /* Request for a frame */ @@ -61,6 +61,14 @@ struct __attribute__((__packed__)) resolution { /* Press a key */ struct __attribute__((__packed__)) key { + char type; /* 'K' */ + uint8_t down:1; /* 1: down, 0: up */ + uint8_t keycode; /* X11 KeyCode (8-255) */ +}; + +/* Press a key (compatibility with VF1) */ +/* TODO: Remove support for VF1. */ +struct __attribute__((__packed__)) key_vf1 { char type; /* 'K' */ uint8_t down:1; /* 1: down, 0: up */ uint32_t keysym; /* X11 KeySym */ diff --git a/src/fbserver.c b/src/fbserver.c index 1b1814b31..8f5b4615b 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -545,17 +545,12 @@ int main(int argc, char** argv) { if (!check_size(length, sizeof(struct key), "key")) break; struct key* k = (struct key*)buffer; - KeyCode kc = XKeysymToKeycode(dpy, k->keysym); - log(2, "Key: ks=%04x kc=%04x\n", k->keysym, kc); - if (kc != 0) { - XTestFakeKeyEvent(dpy, kc, k->down, CurrentTime); - if (k->down) { - kb_add(KEYBOARD, kc); - } else { - kb_remove(KEYBOARD, kc); - } + log(2, "Key: kc=%04x\n", k->keycode); + XTestFakeKeyEvent(dpy, k->keycode, k->down, CurrentTime); + if (k->down) { + kb_add(KEYBOARD, k->keycode); } else { - error("Invalid keysym %04x.", k->keysym); + kb_remove(KEYBOARD, k->keycode); } break; } From 8a3e432a2e92bdd58f9e7b4d31122a441370bf84 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sat, 10 Jan 2015 23:41:25 +0100 Subject: [PATCH 206/334] kiwi: VF1 compatibility layer Separate code path in HandleInputEvent (keyboard): depending on server version, send keysym or keycode. --- host-ext/nacl_src/kiwi.cc | 107 +++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 30 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 78fbd7170..868406911 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -229,10 +229,22 @@ class KiwiInstance : public pp::Instance { LogMessage(-1) << "Got a version while connected?!?"; return false; } - if (strcmp(data, VERSION)) { - LogMessage(-1) << "Invalid version received (" << data << ")."; - return false; + + server_version_ = data; + + if (server_version_ != VERSION) { + /* TODO: Remove VF1 compatiblity */ + if (server_version_ == "VF1") { + LogMessage(-1) << "Outdated server version (" + << server_version_ << ")."; + /* FIXME: Clearly inform the user that the chroot is outdated. */ + } else { + LogMessage(-1) << "Invalid version received (" + << server_version_ << ")."; + return false; + } } + connected_ = true; SocketSend(pp::Var("VOK"), false); ControlMessage("connected", "Version received"); @@ -452,28 +464,33 @@ class KiwiInstance : public pp::Instance { event.GetType() == PP_INPUTEVENT_TYPE_KEYUP) { pp::KeyboardInputEvent key_event(event); - uint32_t keycode = key_event.GetKeyCode(); + uint32_t jskeycode = key_event.GetKeyCode(); std::string keystr = key_event.GetCode().AsString(); - uint32_t keysym = KeyCodeToKeySym(keycode, keystr); bool down = event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN; - LogMessage(keysym == 0 ? 0 : 1) + uint8_t keycode = KeyStrToKeyCode(keystr); + /* TODO: Remove VF1 compatibility */ + uint32_t keysym = 0; + if (server_version_ == "VF1") + keysym = KeyCodeToKeySym(jskeycode, keystr); + + LogMessage(keycode == 0 ? 0 : 1) << "Key " << (down ? "DOWN" : "UP") << ": C:" << keystr - << "/KC:" << std::hex << keycode - << "/KS:" << std::hex << keysym - << (keysym == 0 ? " (KEY UNKNOWN!)" : "") + << ", JSKC:" << std::hex << jskeycode + << " => KC:" << std::hex << keycode + << (keycode == 0 ? " (KEY UNKNOWN!)" : "") << " searchstate:" << search_state_; - if (keysym == 0) { + if (keycode == 0 && keysym == 0) { return PP_TRUE; } - if (keycode == 183) { /* Fullscreen => toggle fullscreen */ + if (jskeycode == 183) { /* Fullscreen => toggle fullscreen */ if (!down) ControlMessage("state", "fullscreen"); return PP_TRUE; - } else if (keycode == 182) { /* Page flipper => minimize window */ + } else if (jskeycode == 182) { /* Page flipper => minimize window */ if (!down) ControlMessage("state", "hide"); return PP_TRUE; @@ -482,43 +499,43 @@ class KiwiInstance : public pp::Instance { /* We delay sending Super-L, and only "press" it on mouse clicks and * letter keys (a-z). This way, Home (Search+Left) appears without * modifiers (instead of Super_L+Home) */ - /* FIXME: NumpadDecimal is a dirty hack for broken freon, - see http://crbug.com/425156 */ - if (keysym == kSUPER_L && (keystr == "OSLeft" || - keystr == "NumpadDecimal")) { + if (keystr == "OSLeft") { if (down) { search_state_ = kSearchUpFirst; } else { if (search_state_ == kSearchUpFirst) { /* No other key was pressed: press+release */ - SendKey(kSUPER_L, 1); - SendKey(kSUPER_L, 0); + SendSearchKey(1); + SendSearchKey(0); } else if (search_state_ == kSearchDown) { - SendKey(kSUPER_L, 0); + SendSearchKey(0); } search_state_ = kSearchInactive; } return PP_TRUE; /* Ignore key */ } - if (keycode >= 65 && keycode <= 90) { /* letter */ + if (jskeycode >= 65 && jskeycode <= 90) { /* letter */ /* Search is active, send Super_L if needed */ if (down && (search_state_ == kSearchUpFirst || search_state_ == kSearchUp)) { - SendKey(kSUPER_L, 1); + SendSearchKey(1); search_state_ = kSearchDown; } } else { /* non-letter */ /* Release Super_L if needed */ if (search_state_ == kSearchDown) { - SendKey(kSUPER_L, 0); + SendSearchKey(0); search_state_ = kSearchUp; } else if (search_state_ == kSearchUpFirst) { /* Switch from UpFirst to Up */ search_state_ = kSearchUp; } } - SendKey(keysym, down ? 1 : 0); + if (server_version_ == "VF1") + SendKeySym(keysym, down ? 1 : 0); + else + SendKeyCode(keycode, down ? 1 : 0); } else if (event.GetType() == PP_INPUTEVENT_TYPE_MOUSEDOWN || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEUP || event.GetType() == PP_INPUTEVENT_TYPE_MOUSEMOVE) { @@ -713,8 +730,13 @@ class KiwiInstance : public pp::Instance { } } + uint8_t KeyStrToKeyCode(const std::string& code) { + return 1; + } + /* Converts "IE"/JavaScript keycode to X11 KeySym. - * See http://unixpapa.com/js/key.html */ + * See http://unixpapa.com/js/key.html + * TODO: Drop support for VF1 */ uint32_t KeyCodeToKeySym(uint32_t keycode, const std::string& code) { if (keycode >= 65 && keycode <= 90) /* A to Z */ return keycode + 32; @@ -754,7 +776,7 @@ class KiwiInstance : public pp::Instance { case 42: return 0xff61; // print screen case 45: return 0xff63; // insert case 46: return 0xffff; // delete - case 91: return kSUPER_L; // super + case 91: return 0xffeb; // super case 106: return 0xffaa; // num multiply case 107: return 0xffab; // num plus case 109: return 0xffad; // num minus @@ -804,7 +826,7 @@ class KiwiInstance : public pp::Instance { if (down && (search_state_ == kSearchUpFirst || search_state_ == kSearchUp)) { - SendKey(kSUPER_L, 1); + SendSearchKey(1); search_state_ = kSearchDown; } @@ -820,14 +842,38 @@ class KiwiInstance : public pp::Instance { SetTargetFPS(kFullFPS); } - /* Sends a key press */ - void SendKey(uint32_t keysym, int down) { + void SendSearchKey(int down) { + /* TODO: Drop support for VF1 */ + if (server_version_ == "VF1") + SendKeySym(0xffeb, down); + else + SendKeyCode(kSUPER_L, down); + } + + /* Sends a keysym (VF1) */ + /* TODO: Drop support for VF1 */ + void SendKeySym(uint32_t keysym, int down) { + struct key_vf1* k; + pp::VarArrayBuffer array_buffer(sizeof(*k)); + k = static_cast(array_buffer.Map()); + k->type = 'K'; + k->down = down; + k->keysym = keysym; + array_buffer.Unmap(); + SocketSend(array_buffer, true); + + /* That means we have focus */ + SetTargetFPS(kFullFPS); + } + + /* Sends a keycode */ + void SendKeyCode(uint8_t keycode, int down) { struct key* k; pp::VarArrayBuffer array_buffer(sizeof(*k)); k = static_cast(array_buffer.Map()); k->type = 'K'; k->down = down; - k->keysym = keysym; + k->keycode = keycode; array_buffer.Unmap(); SocketSend(array_buffer, true); @@ -958,7 +1004,7 @@ class KiwiInstance : public pp::Instance { private: /* Constants */ /* SuperL keycode (search key) */ - const uint32_t kSUPER_L = 0xffeb; + const uint8_t kSUPER_L = 123; /* FIXME */ const int kFullFPS = 30; /* Maximum fps */ const int kBlurFPS = 5; /* fps when window is possibly hidden */ @@ -978,6 +1024,7 @@ class KiwiInstance : public pp::Instance { pp::WebSocket websocket_{this}; bool connected_ = false; + std::string server_version_ = ""; bool screen_flying_ = false; pp::Var receive_var_; int target_fps_ = kFullFPS; From d52655e222a32344fd550944b7409f11611e75fb Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sun, 11 Jan 2015 13:46:26 +0100 Subject: [PATCH 207/334] kiwi: Keycode converter header file. Hashmap from version NaCl GetCode strings to X11 key codes. We do not use static/global variables as those are forbidden by the style guide. Instead, we allocate a pointer on first use. --- host-ext/nacl_src/keycode_converter.h | 215 ++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 host-ext/nacl_src/keycode_converter.h diff --git a/host-ext/nacl_src/keycode_converter.h b/host-ext/nacl_src/keycode_converter.h new file mode 100644 index 000000000..19224eee9 --- /dev/null +++ b/host-ext/nacl_src/keycode_converter.h @@ -0,0 +1,215 @@ +/* Copyright (c) 2015 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Translates NaCl pp::KeyboardInputEvent::KeyCode() strings to X11 keycodes. + */ + +#include +#include +#include + +/* Values in the keycode hashmap. */ +class KeyCode { +public: + KeyCode(uint8_t base, uint8_t search): + base_(base), search_(search) {} + + KeyCode(uint8_t base): KeyCode(base, base) {} + + uint8_t GetCode(const bool search_on) const { + if (search_on) + return search_; + else + return base_; + } + +private: + const uint8_t base_; /* Basic keycode */ + /* Reverse translation of keycode when Search is pressed: e.g. + * Search+Left => Home. In this case: + * base_ = Home keycode (0x6e), search_ = Left keycode (0x71) */ + const uint8_t search_; +}; + +/* Class with static members only: converts KeyCode string to X11 keycode */ +class KeyCodeConverter { +public: + static uint8_t GetCode(const std::string& str, const bool search_on) { + if (!strcodemap_) + InitMap(); + + auto it = strcodemap_->find(str); + /* Not found */ + if (it == strcodemap_->end()) + return 0; + + return it->second.GetCode(search_on); + } + +private: + static void InitMap(); + /* static pointer member, to avoid static variable of class type. */ + static std::map* strcodemap_; +}; + +std::map* KeyCodeConverter::strcodemap_ = NULL; + +/* Initialize string to X11 keycode mapping. Must be called once only. + * + * FIXME: Fill in search_ fields in KeyCode. + * + * Most of this data can be generated from + * ui/events/keycodes/dom4/keycode_converter_data.h in the Chromium source + * tree, using something like: + * sed -n \ +'s/.*USB_KEYMAP([^,]*, \([^,]*\),.*, \("[^"]*"\).*$/{\2, KeyCode(\1)},/p' \ +keycode_converter_data.h | grep -v "0x00" >> keymap_data.h + */ +void KeyCodeConverter::InitMap() { + strcodemap_ = new std::map({ + {"Sleep", KeyCode(0x96)}, + {"WakeUp", KeyCode(0x97)}, + {"KeyA", KeyCode(0x26)}, + {"KeyB", KeyCode(0x38)}, + {"KeyC", KeyCode(0x36)}, + {"KeyD", KeyCode(0x28)}, + {"KeyE", KeyCode(0x1a)}, + {"KeyF", KeyCode(0x29)}, + {"KeyG", KeyCode(0x2a)}, + {"KeyH", KeyCode(0x2b)}, + {"KeyI", KeyCode(0x1f)}, + {"KeyJ", KeyCode(0x2c)}, + {"KeyK", KeyCode(0x2d)}, + {"KeyL", KeyCode(0x2e)}, + {"KeyM", KeyCode(0x3a)}, + {"KeyN", KeyCode(0x39)}, + {"KeyO", KeyCode(0x20)}, + {"KeyP", KeyCode(0x21)}, + {"KeyQ", KeyCode(0x18)}, + {"KeyR", KeyCode(0x1b)}, + {"KeyS", KeyCode(0x27)}, + {"KeyT", KeyCode(0x1c)}, + {"KeyU", KeyCode(0x1e)}, + {"KeyV", KeyCode(0x37)}, + {"KeyW", KeyCode(0x19)}, + {"KeyX", KeyCode(0x35)}, + {"KeyY", KeyCode(0x1d)}, + {"KeyZ", KeyCode(0x34)}, + {"Digit1", KeyCode(0x0a)}, + {"Digit2", KeyCode(0x0b)}, + {"Digit3", KeyCode(0x0c)}, + {"Digit4", KeyCode(0x0d)}, + {"Digit5", KeyCode(0x0e)}, + {"Digit6", KeyCode(0x0f)}, + {"Digit7", KeyCode(0x10)}, + {"Digit8", KeyCode(0x11)}, + {"Digit9", KeyCode(0x12)}, + {"Digit0", KeyCode(0x13)}, + {"Enter", KeyCode(0x24)}, + {"Escape", KeyCode(0x09)}, + {"Backspace", KeyCode(0x16)}, + {"Tab", KeyCode(0x17)}, + {"Space", KeyCode(0x41)}, + {"Minus", KeyCode(0x14)}, + {"Equal", KeyCode(0x15)}, + {"BracketLeft", KeyCode(0x22)}, + {"BracketRight", KeyCode(0x23)}, + {"Backslash", KeyCode(0x33)}, + {"IntlHash", KeyCode(0x33)}, + {"Semicolon", KeyCode(0x2f)}, + {"Quote", KeyCode(0x30)}, + {"Backquote", KeyCode(0x31)}, + {"Comma", KeyCode(0x3b)}, + {"Period", KeyCode(0x3c)}, + {"Slash", KeyCode(0x3d)}, + {"CapsLock", KeyCode(0x42)}, + {"F1", KeyCode(0x43)}, + {"F2", KeyCode(0x44)}, + {"F3", KeyCode(0x45)}, + {"F4", KeyCode(0x46)}, + {"F5", KeyCode(0x47)}, + {"F6", KeyCode(0x48)}, + {"F7", KeyCode(0x49)}, + {"F8", KeyCode(0x4a)}, + {"F9", KeyCode(0x4b)}, + {"F10", KeyCode(0x4c)}, + {"F11", KeyCode(0x5f)}, + {"F12", KeyCode(0x60)}, + {"PrintScreen", KeyCode(0x6b)}, + {"ScrollLock", KeyCode(0x4e)}, + {"Pause", KeyCode(0x7f)}, + {"Insert", KeyCode(0x76)}, + {"Home", KeyCode(0x6e)}, + {"PageUp", KeyCode(0x70)}, + {"Delete", KeyCode(0x77)}, + {"End", KeyCode(0x73)}, + {"PageDown", KeyCode(0x75)}, + {"ArrowRight", KeyCode(0x72)}, + {"ArrowLeft", KeyCode(0x71)}, + {"ArrowDown", KeyCode(0x74)}, + {"ArrowUp", KeyCode(0x6f)}, + {"NumLock", KeyCode(0x4d)}, + {"NumpadDivide", KeyCode(0x6a)}, + {"NumpadMultiply", KeyCode(0x3f)}, + {"NumpadSubtract", KeyCode(0x52)}, + {"NumpadAdd", KeyCode(0x56)}, + {"NumpadEnter", KeyCode(0x68)}, + {"Numpad1", KeyCode(0x57)}, + {"Numpad2", KeyCode(0x58)}, + {"Numpad3", KeyCode(0x59)}, + {"Numpad4", KeyCode(0x53)}, + {"Numpad5", KeyCode(0x54)}, + {"Numpad6", KeyCode(0x55)}, + {"Numpad7", KeyCode(0x4f)}, + {"Numpad8", KeyCode(0x50)}, + {"Numpad9", KeyCode(0x51)}, + {"Numpad0", KeyCode(0x5a)}, + {"NumpadDecimal", KeyCode(0x5b)}, + {"IntlBackslash", KeyCode(0x5e)}, + {"ContextMenu", KeyCode(0x87)}, + {"Power", KeyCode(0x7c)}, + {"NumpadEqual", KeyCode(0x7d)}, + {"Help", KeyCode(0x92)}, + {"Again", KeyCode(0x89)}, + {"Undo", KeyCode(0x8b)}, + {"Cut", KeyCode(0x91)}, + {"Copy", KeyCode(0x8d)}, + {"Paste", KeyCode(0x8f)}, + {"Find", KeyCode(0x90)}, + {"VolumeMute", KeyCode(0x79)}, + {"VolumeUp", KeyCode(0x7b)}, + {"VolumeDown", KeyCode(0x7a)}, + {"IntlRo", KeyCode(0x61)}, + {"KanaMode", KeyCode(0x65)}, + {"IntlYen", KeyCode(0x84)}, + {"Convert", KeyCode(0x64)}, + {"NonConvert", KeyCode(0x66)}, + {"Lang1", KeyCode(0x82)}, + {"Lang2", KeyCode(0x83)}, + {"Lang3", KeyCode(0x62)}, + {"Lang4", KeyCode(0x63)}, + {"Abort", KeyCode(0x88)}, + {"NumpadParenLeft", KeyCode(0xbb)}, + {"NumpadParenRight", KeyCode(0xbc)}, + {"ControlLeft", KeyCode(0x25)}, + {"ShiftLeft", KeyCode(0x32)}, + {"AltLeft", KeyCode(0x40)}, + {"OSLeft", KeyCode(0x85)}, + {"ControlRight", KeyCode(0x69)}, + {"ShiftRight", KeyCode(0x3e)}, + {"AltRight", KeyCode(0x6c)}, + {"OSRight", KeyCode(0x86)}, + {"BrightnessUp", KeyCode(0xe9)}, + {"BrightnessDown", KeyCode(0xea)}, + {"LaunchApp2", KeyCode(0x94)}, + {"LaunchApp1", KeyCode(0xa5)}, + {"BrowserBack", KeyCode(0xa6)}, + {"BrowserForward", KeyCode(0xa7)}, + {"BrowserRefresh", KeyCode(0xb5)}, + {"BrowserFavorites", KeyCode(0xa4)}, + {"MailReply", KeyCode(0xf0)}, + {"MailForward", KeyCode(0xf1)}, + {"MailSend", KeyCode(0xef)}, + }); +} From 6ca65039e688ba3fefae7480251b45b969d774e3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sun, 11 Jan 2015 14:44:04 +0100 Subject: [PATCH 208/334] kiwi: Use KeyMapConverter (VF2) --- host-ext/nacl_src/kiwi.cc | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 868406911..0c78eb69a 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -29,6 +29,8 @@ /* Protocol data structures */ #include "../../src/fbserver-proto.h" +#include "keycode_converter.h" + class KiwiInstance : public pp::Instance { public: explicit KiwiInstance(PP_Instance instance): pp::Instance(instance) {} @@ -468,7 +470,18 @@ class KiwiInstance : public pp::Instance { std::string keystr = key_event.GetCode().AsString(); bool down = event.GetType() == PP_INPUTEVENT_TYPE_KEYDOWN; - uint8_t keycode = KeyStrToKeyCode(keystr); + if (jskeycode == 183) { /* Fullscreen => toggle fullscreen */ + if (!down) + ControlMessage("state", "fullscreen"); + return PP_TRUE; + } else if (jskeycode == 182) { /* Page flipper => minimize window */ + if (!down) + ControlMessage("state", "hide"); + return PP_TRUE; + } + + /* TODO: Reverse Search key translation when appropriate */ + uint8_t keycode = KeyCodeConverter::GetCode(keystr, false); /* TODO: Remove VF1 compatibility */ uint32_t keysym = 0; if (server_version_ == "VF1") @@ -478,7 +491,7 @@ class KiwiInstance : public pp::Instance { << "Key " << (down ? "DOWN" : "UP") << ": C:" << keystr << ", JSKC:" << std::hex << jskeycode - << " => KC:" << std::hex << keycode + << " => KC:" << (int)keycode << (keycode == 0 ? " (KEY UNKNOWN!)" : "") << " searchstate:" << search_state_; @@ -486,16 +499,6 @@ class KiwiInstance : public pp::Instance { return PP_TRUE; } - if (jskeycode == 183) { /* Fullscreen => toggle fullscreen */ - if (!down) - ControlMessage("state", "fullscreen"); - return PP_TRUE; - } else if (jskeycode == 182) { /* Page flipper => minimize window */ - if (!down) - ControlMessage("state", "hide"); - return PP_TRUE; - } - /* We delay sending Super-L, and only "press" it on mouse clicks and * letter keys (a-z). This way, Home (Search+Left) appears without * modifiers (instead of Super_L+Home) */ @@ -730,10 +733,6 @@ class KiwiInstance : public pp::Instance { } } - uint8_t KeyStrToKeyCode(const std::string& code) { - return 1; - } - /* Converts "IE"/JavaScript keycode to X11 KeySym. * See http://unixpapa.com/js/key.html * TODO: Drop support for VF1 */ @@ -847,7 +846,7 @@ class KiwiInstance : public pp::Instance { if (server_version_ == "VF1") SendKeySym(0xffeb, down); else - SendKeyCode(kSUPER_L, down); + SendKeyCode(KeyCodeConverter::GetCode("OSLeft", false), down); } /* Sends a keysym (VF1) */ @@ -1003,9 +1002,6 @@ class KiwiInstance : public pp::Instance { private: /* Constants */ - /* SuperL keycode (search key) */ - const uint8_t kSUPER_L = 123; /* FIXME */ - const int kFullFPS = 30; /* Maximum fps */ const int kBlurFPS = 5; /* fps when window is possibly hidden */ const int kHiddenFPS = 0; /* fps when window is hidden */ From 844315d88d326e8a92d55dbd39e53b3e7b1aa09e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 12 Jan 2015 11:56:52 +0100 Subject: [PATCH 209/334] croutonxinitrc-wrapper/kiwi: Set keyboard rules to evdev. --- chroot-bin/croutonxinitrc-wrapper | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 2f13849af..a28d65fd7 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -129,6 +129,8 @@ fi # Crouton-in-a-tab: Start fbserver and launch display if [ "$XMETHOD" = 'xiwi' ]; then + # The extension sends evdev key codes: fix the keyboard mapping rules + setxkbmap -rules evdev # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. setres 1024 768 > /dev/null From b5a7f2644dfb3969fe6424d5916bfaac3751203a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 12 Jan 2015 15:51:14 -0800 Subject: [PATCH 210/334] Bump extension version to 2.1.0 --- host-ext/crouton/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 1ca2cfabb..c598db771 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.0.0", + "version": "2.1.0", "icons": { "48": "icon-48.png", "128": "icon-128.png" From 2e7b8edec150249bca447dff9f231ab75fa59cc0 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 22:41:07 +0100 Subject: [PATCH 211/334] Extension: Notification handling Creates a new message type, "N", that can be sent from croutonnotify. It is a JSON-formatted map, compatible with chrome.notifications option format, with 2 additional fields: - `crouton_id`: The notification ID to pass to chrome.notifications.create. - `crouton_display`: The display to switch to when the notification is clicked. --- chroot-bin/croutonnotify | 83 ++++++++++++++++++++++++++++++++++ host-ext/crouton/background.js | 73 +++++++++++++++++++++++++++++- host-ext/crouton/manifest.json | 1 + targets/extension | 2 +- 4 files changed, 156 insertions(+), 3 deletions(-) create mode 100755 chroot-bin/croutonnotify diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify new file mode 100755 index 000000000..4b0b7c0d3 --- /dev/null +++ b/chroot-bin/croutonnotify @@ -0,0 +1,83 @@ +#!/bin/sh -e +# Copyright (c) 2015 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +USAGE="${0##*/} -j +${0##*/} -t title [-m message] [-d display] [-i id] +Raises a notification in Chromium OS (requires crouton extension). + +When -j is specified, reads JSON data from stdin, in the format required +by chrome.notifications API, with 2 additional fields, \"crouton_id\" and +\"crouton_display\", corresponding respectively to the notification id, and the +display to switch to when the notification is clicked (croutoncycle parameter). + +Otherwise, constructs a \"basic\" notification with the requested fields. + +Options: + -j Read JSON data from stdin (see example below) + -t Title to display (chrome.notifications field: \"title\") + -m Message to display (chrome.notifications field: \"message\") + -d Display to switch to when the notification is clicked (croutoncycle + parameter). Default: Do not switch display. + -i ID to pass to chrome.notifications.create: only the last + notification with a given ID is shown. + Default: Display a new notification. + +Example JSON data: +"'{ + "type": "basic", + "title": "Primary Title", + "message": "Primary message to display", + "crouton_display": ":1" +}' + +. "$(dirname "$0")/../installer/functions" + +DISPLAY="" +MESSAGE="" +TITLE="" +ID="" +JSON="" + +# Process arguments +while getopts 'd:i:jm:t:' f; do + case "$f" in + d) DISPLAY="$OPTARG";; + i) ID="$OPTARG";; + j) JSON="y";; + m) MESSAGE="$OPTARG";; + t) TITLE="$OPTARG";; + \?) error 2 "$USAGE";; + esac +done + +# No extra parameters, -j precludes other parameters, and at least title +# must be specified (or -j) +if [ "$#" != "$((OPTIND-1))" ] || + [ "$JSON" = 'y' -a -n "$DISPLAY$ID$MESSAGE$TITLE" ] || + [ -z "$JSON$TITLE" ]; then + error 2 "$USAGE" +fi + +JSONDATA="" +if [ -z "$JSON" ]; then + JSONDATA='{ + "type": "'"basic"'", + "title": "'"$TITLE"'", + "message": "'"$MESSAGE"'", + "crouton_display": "'"$DISPLAY"'", + "crouton_id": "'"$ID"'" + }' +fi + +STATUS="$({ + echo -n "N$JSONDATA" + if [ "$JSON" = 'y' ]; then + cat + fi +} | websocketcommand)" + +if [ "$STATUS" != 'NOK' ]; then + error 1 "${STATUS#?}" +fi diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index ac024a153..126ad72fe 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -45,6 +45,9 @@ var kiwi_win_ = {}; /* Map of kiwi windows. Key is display, value is object (.id, .window: window element) */ var focus_win_ = -1; /* Focused kiwi window. -1 if no kiwi window focused. */ +var notifications_ = {}; /* Map of notification id to function to be called when + the notification is clicked. */ + /* Set the current status string. * active is a boolean, true if the WebSocket connection is established. */ function setStatus(status, active) { @@ -277,6 +280,10 @@ function clipboardStart() { clipboardholder_ = document.getElementById("clipboardholder"); + /* Notification event handlers */ + chrome.notifications.onClosed.addListener(notificationClosed); + chrome.notifications.onClicked.addListener(notificationClicked); + websocketConnect(); } @@ -403,6 +410,53 @@ function websocketMessage(evt) { websocket_.send("EError: URL must be absolute"); } + break; + case 'N': /* Raise a notification */ + /* Payload in JSON format, compatible with chrome.extensions specifications */ + try { + var data = JSON.parse(payload); + + if (!data.type) + data.type = "basic"; + if (!data.iconUrl) + data.iconUrl = "icon-128.png"; + + /* Strip off crouton fields */ + var id = ""; + var display = null; + + if (data.crouton_id) { + id = data.crouton_id; + } + delete data.crouton_id; + + /* Set context message with chroot name/display */ + delete data.contextMessage; + if (data.crouton_display) { + display = data.crouton_display; + var win = windows_.filter(function(x) { + return x.display == display })[0]; + var name = win ? (win.name + " (" + display + ")") : display; + data.contextMessage = "Switch to " + name; + } + delete data.crouton_display; + + chrome.notifications.create(id, data, + function(id) { + printLog("Raised notification " + id, LogLevel.DEBUG); + notifications_[id] = function() { + if (display) + websocket_.send("C" + display); + /* Remove the notification. */ + chrome.notifications.clear(id, function(_) {}); + } + }); + websocket_.send("NOK"); + } catch(e) { + printLog("Notification parsing error: " + e + + " (payload: '" + payload + "').", LogLevel.ERROR); + websocket_.send("EError: invalid payload."); + } break; case 'C': /* Returned data from a croutoncycle command */ /* Non-zero length has a window list; otherwise it's a cycle signal */ @@ -563,6 +617,21 @@ function windowRemoved(windowid) { } } +/* Called when a notification is clicked */ +function notificationClicked(id) { + printLog("Notification " + id + " clicked.", LogLevel.DEBUG); + if (notifications_[id]) { + notifications_[id](); + } +} + +/* Called when a notification is closed */ +function notificationClosed(id, byUser) { + printLog("Notification " + id + " closed (byUser: " + byUser + ").", + LogLevel.DEBUG); + delete notifications_[id]; +} + function padstr0(i) { var s = i + ""; if (s.length < 2) @@ -578,8 +647,8 @@ function printLog(str, level) { padstr0(date.getMinutes()) + ":" + padstr0(date.getSeconds()); - if (str.length > 80) - str = str.substring(0, 77) + "..."; + if (str.length > 200) + str = str.substring(0, 197) + "..."; console.log(datestr + ": " + str); /* Add messages to logger */ diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index c598db771..bfdcf414c 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -25,6 +25,7 @@ "permissions": [ "clipboardRead", "clipboardWrite", + "notifications", "tabs" ] } diff --git a/targets/extension b/targets/extension index 4f7821e87..411fdcece 100644 --- a/targets/extension +++ b/targets/extension @@ -4,7 +4,7 @@ # found in the LICENSE file. REQUIRES='x11' DESCRIPTION='Clipboard synchronization and URL handling with Chromium OS.' -CHROOTBIN='croutonclip croutonurlhandler' +CHROOTBIN='croutonclip croutonnotify croutonurlhandler' EXTENSION='gcpneefbbnfalgjniomfjknbcgkbijom' # Check if the extension is installed and add a mark to the preparation script From 6c1198c0a18dfdf402110b9b5fc857d7997e82e7 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 16 Jan 2015 16:12:17 +0100 Subject: [PATCH 212/334] websocket: Correctly handle requests from extension Requests from the extension can happen at any time, even between a command and its acknowledgement. Clarified protocol in https://github.com/dnschneid/crouton/wiki/crouton-extension:-websocket-architecture We now enforce that requests in the form `?` must have a reply in the form `?` (where `?` is the same character), or an error reply `E`. (This is already the case, so no protocol change is required) Test: Start a Kiwi window, and run: ``` for i in `seq 1 1000`; do ./croutonnotify -i abcd -m t$i; done ``` Even when switching rapidly between Kiwi and Chromium, the command should never print anything (without this patch, it would show `scros` or `s:1` before repeating `Error: Not connected`), and the extension does not disconnect. --- src/websocket.c | 129 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 96 insertions(+), 33 deletions(-) diff --git a/src/websocket.c b/src/websocket.c index 94720b33c..a113313d3 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -28,6 +28,8 @@ static int pipein_fd = -1; static int pipeout_fd = -1; static void pipeout_close(); +static int socket_client_handle_unrequested(const char* buffer, + const int length); /* Open a pipe in non-blocking mode, then set it back to blocking mode. */ /* Returns fd on success, -1 if the pipe cannot be open, -2 if the O_NONBLOCK @@ -151,6 +153,7 @@ static void pipein_read() { int n; char buffer[FRAMEMAXHEADERSIZE+BUFFERSIZE]; int first = 1; + char firstchar = '\0'; if (client_fd < 0) { log(1, "No client FD."); @@ -171,6 +174,9 @@ static void pipein_read() { break; } + if (first) + firstchar = buffer[FRAMEMAXHEADERSIZE]; + /* Write a text frame for the first packet, then cont frames. */ n = socket_client_write_frame(buffer, n, first ? WS_OPCODE_TEXT : WS_OPCODE_CONT, 0); @@ -202,6 +208,7 @@ static void pipein_read() { int fin = 0; uint32_t maskkey; int retry = 0; + first = 1; /* Ignore return value, so we still read the frame even if pipeout * cannot be open. */ @@ -217,21 +224,49 @@ static void pipein_read() { continue; if (len < 0) - break; + goto exit; /* Read the whole frame, and write it to pipeout */ while (len > 0) { int rlen = (len > BUFFERSIZE) ? BUFFERSIZE: len; - if (socket_client_read_frame_data(buffer, rlen, maskkey) < 0) { - pipeout_close(); - return; + if (socket_client_read_frame_data(buffer, rlen, maskkey) < 0) + goto exit; + + /* Check first byte */ + if (first && buffer[0] != firstchar && buffer[0] != 'E') { + /* This is not a response: unrequested packet */ + if (!fin && len < BUFFERSIZE) { + /* !fin, and buffer not full, finish reading... */ + rlen = socket_client_read_frame(buffer+len, + sizeof(buffer)-len); + if (rlen < 0) + goto exit; + + len += rlen; + } + + if (len >= BUFFERSIZE) { + error("Unrequested command too long: (>%d bytes).", len); + socket_client_close(1); + goto exit; + } + + if (socket_client_handle_unrequested(buffer, len) < 0) + goto exit; + + /* Command was handled, try reading the answer again. */ + fin = 0; + break; } + /* Ignore return value as well */ pipeout_write(buffer, rlen); len -= rlen; + first = 0; } } +exit: pipeout_close(); } @@ -312,37 +347,37 @@ void pipe_init() { pipein_reopen(); } -/* Unrequested data came in from WebSocket client. */ -static void socket_client_read() { - char buffer[BUFFERSIZE]; - int length; - - length = socket_client_read_frame(buffer, sizeof(buffer)); - if (length < 0) { - socket_client_close(1); - return; - } - +/* Handle unrequested packet from extension. + * Returns 0 on success. On error, returns -1 and closes websocket connection. + */ +static int socket_client_handle_unrequested(const char* buffer, + const int length) { /* Process the client request. */ - buffer[length == BUFFERSIZE ? BUFFERSIZE-1 : length] = 0; switch (buffer[0]) { - case 'C': /* Send a command to croutoncycle */ - log(2, "Received croutoncycle command (%s)", &buffer[1]); + case 'C': { /* Send a command to croutoncycle */ + char reply[BUFFERSIZE]; + int replylength = 1; + reply[FRAMEMAXHEADERSIZE] = 'C'; + char* cmd = "croutoncycle"; - char* args[] = { cmd, &buffer[1], NULL }; + char param[length]; + memcpy(param, buffer+1, length-1); + param[length-1] = '\0'; + char* args[] = { cmd, param, NULL }; + + log(2, "Received croutoncycle command (%s)", param); - buffer[FRAMEMAXHEADERSIZE] = 'C'; /* We are only interested in the output for list commands */ - if (buffer[1] == 'l') { - length = popen2(cmd, args, NULL, 0, - &buffer[FRAMEMAXHEADERSIZE+1], - BUFFERSIZE-FRAMEMAXHEADERSIZE-1); - if (length == -1) { + if (param[0] == 'l') { + int n = popen2(cmd, args, NULL, 0, + &reply[FRAMEMAXHEADERSIZE+1], + BUFFERSIZE-FRAMEMAXHEADERSIZE-1); + if (n < 0) { error("Call to croutoncycle failed."); socket_client_close(0); - return; + return -1; } - length++; + replylength += n; } else { /* Launch command in background (this is necessary as croutoncycle may send a websocket command, leaving us @@ -366,20 +401,48 @@ static void socket_client_read() { } /* Wait for first fork to complete. */ waitpid(pid, NULL, 0); - length = 1; } - if (socket_client_write_frame(buffer, length, + if (socket_client_write_frame(reply, replylength, WS_OPCODE_TEXT, 1) < 0) { error("Write error."); socket_client_close(0); - return; + return -1; } break; - default: - error("Received an unexpected packet from client."); + } + default: { + int len = length > 64 ? 64 : length; + char dump[len+1]; + memcpy(dump, buffer, len); + dump[len] = '\0'; + error("Received an unexpected packet from client (%s).", dump); socket_client_close(0); - break; + return -1; + } } + + return 0; +} + +/* Unrequested data came in from WebSocket client. */ +static void socket_client_read() { + char buffer[BUFFERSIZE]; + int length; + + length = socket_client_read_frame(buffer, sizeof(buffer)); + if (length < 0) { + socket_client_close(1); + return; + } + + if (length >= BUFFERSIZE) { + error("Unrequested command too long: (>%d bytes).", length); + socket_client_close(1); + return; + } + + /* Ignore return value (connection gets closed on error) */ + socket_client_handle_unrequested(buffer, length); } static int terminate = 0; From 13b9df052c0f980ef56d7186fe46f186ffb68566 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sat, 17 Jan 2015 10:12:15 +0100 Subject: [PATCH 213/334] croutonxinitrc-wrapper/xiwi: Reapply xkbcomp Fixes #1344. Reapplying xkbcomp fixes autorepeat mask in `xset q`. I tried setting XkbRules in xorg-dummy.conf, but that does not seem to work... Test: Run `xev` and check Left and Down arrows autorepeat, and Right Ctrl/Alt do not autorepeat. --- chroot-bin/croutonxinitrc-wrapper | 3 +++ 1 file changed, 3 insertions(+) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index a28d65fd7..d4eb93b63 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -131,6 +131,9 @@ fi if [ "$XMETHOD" = 'xiwi' ]; then # The extension sends evdev key codes: fix the keyboard mapping rules setxkbmap -rules evdev + # Reapply xkb map: This fixes autorepeat mask in "xset q" + xkbcomp "$DISPLAY" - | xkbcomp - "$DISPLAY" 2>/dev/null + # Set resolution to a default 1024x768, this is important so that the DPI # looks reasonable when the WM/DE start. setres 1024 768 > /dev/null From 76a0205b673ec828684895aead1630167e312027 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Sat, 17 Jan 2015 10:29:15 +0100 Subject: [PATCH 214/334] enter-chroot: Do not fail if /var/run/cras does not exist Fixes #1340. Test: `sudo mv /var/run/cras /var/run/cras2; sudo enter-chroot` only prints a warning, and does not fail. --- host-bin/enter-chroot | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 3cda24e5d..637995b20 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -352,7 +352,6 @@ tmpfsmount /var/run 'noexec,nosuid,mode=0755,size=10%' tmpfsmount /var/run/lock 'noexec,nosuid,nodev,size=5120k' bindmount /var/run/dbus /var/host/dbus bindmount /var/run/shill /var/host/shill -bindmount /var/run/cras /var/host/cras bindmount /var/lib/timezone /var/host/timezone for m in /lib/modules/*; do if [ -d "$m" ]; then @@ -373,8 +372,14 @@ else ln -sfT /dev/.udev "`fixabslinks '/var/run'`/udev" fi -# Add a /var/host/cras symlink for CRAS clients -ln -sfT /var/host/cras "`fixabslinks '/var/run'`/cras" +if [ -d /var/run/cras ]; then + bindmount /var/run/cras /var/host/cras + # Add a /var/host/cras symlink for CRAS clients + ln -sfT /var/host/cras "$(fixabslinks '/var/run')/cras" +else + echo "\ +WARNING: CRAS control directory not found, audio forwarding will not work." 1>&2 +fi # Bind-mount /media, specifically the removable directory destmedia="`fixabslinks '/var/host/media'`" From ed815509fd1b3cd618dd22e90093706c219cea87 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 20:02:53 +0100 Subject: [PATCH 215/334] keycode_converter.h: Constructor is explicit. --- host-ext/nacl_src/keycode_converter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/nacl_src/keycode_converter.h b/host-ext/nacl_src/keycode_converter.h index 19224eee9..2b4459ccb 100644 --- a/host-ext/nacl_src/keycode_converter.h +++ b/host-ext/nacl_src/keycode_converter.h @@ -15,7 +15,7 @@ class KeyCode { KeyCode(uint8_t base, uint8_t search): base_(base), search_(search) {} - KeyCode(uint8_t base): KeyCode(base, base) {} + explicit KeyCode(uint8_t base): KeyCode(base, base) {} uint8_t GetCode(const bool search_on) const { if (search_on) From d6826d4904982c551147bb5e27fec61704fd0035 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 15:01:43 +0100 Subject: [PATCH 216/334] kiwi: Pretty handling of warnings/errors. Warnings are not used yet, but stack up, with a little cross to dismiss the warning. --- host-ext/crouton/window.html | 43 +++++++++++++++++++--- host-ext/crouton/window.js | 69 ++++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 14e507d04..2dea627bb 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -24,7 +24,7 @@ bottom: 0; left: 0; } - #status, #debug { + #debug { color: rgba(255, 255, 255, 1); } #header { @@ -34,13 +34,42 @@ left: 0; right: 0; } - #status { + #info { position: absolute; - text-align: center; width: 100%; margin-top: 100px; + font-size: 12pt; + text-align: center; + color: rgba(255, 255, 255, 1); + } + #status { background-color: rgba(0, 0, 0, 1); + } + #warning { + background-color: rgba(128, 128, 0, 1); + } + .close { + float: right; + background-color: rgba(0,0,0,0); + color: rgba(255,255,255,1); font-size: 12pt; + padding-bottom: 1px; + padding-top: 2px; + padding-left: 4px; + padding-right: 4px; + border-width: 0px; + } + .close:focus { + outline: none; + } + .close:active { + background-color: rgba(0,0,0,0.5); + } + #error { + background-color: rgba(128, 0, 0, 1); + } + #warning, #error { + display: none; } #listener { width: 100%; @@ -57,7 +86,13 @@ -
Initializing...
+
+
Initializing...
+
WARNING: + +
+
ERROR:
+
Date: Wed, 14 Jan 2015 18:03:19 +0100 Subject: [PATCH 217/334] kiwi: Throw warning/errors as needed --- host-ext/crouton/window.js | 8 +++++++- host-ext/nacl_src/kiwi.cc | 35 ++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 8a44d23fd..d97790ad9 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -19,6 +19,7 @@ var display_ = null; /* Display number to use */ var title_ = "crouton in a tab"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ +var error_ = false; /* An error has occured */ var prevstate_ = "maximized"; /* Previous window state (before full screen) */ @@ -116,6 +117,7 @@ function showWarning(message) { /* Set error message */ function showError(message) { + error_ = true; setStatus(null); addInfoLine(errordiv_, message); } @@ -151,12 +153,16 @@ function handleMessage(message) { debugEl.textContent = message.data; } else if (type == "status") { setStatus(payload); + } else if (type == "warning") { + showWarning(payload); + } else if (type == "error") { + showError(payload); } else if (type == "connected") { connected_ = true; setStatus(null); } else if (type == "disconnected") { connected_ = false; - if (debug_ < 1) { + if (debug_ < 1 && !error_) { closing_ = true; setStatus("Disconnected, closing window in " + CLOSE_TIMEOUT + " seconds."); diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 0c78eb69a..67359fa93 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -137,6 +137,17 @@ class KiwiInstance : public pp::Instance { return Message(this, "status", false); } + /* Sends a warning message to Javascript */ + Message WarningMessage() { + return Message(this, "warning", false); + } + + /* Sends an error message to Javascript: all errors are fatal and a + * disconnect message will be sent soon after. */ + Message ErrorMessage() { + return Message(this, "error", false); + } + /* Sends a logging message to Javascript */ Message LogMessage(int level) { if (level <= debug_) { @@ -167,7 +178,7 @@ class KiwiInstance : public pp::Instance { * Parameter is ignored: used for callbacks */ void SocketConnect(int32_t /*result*/ = 0) { if (display_ < 0) { - LogMessage(-1) << "SocketConnect: No display defined yet."; + ErrorMessage() << "SocketConnect: No display defined yet."; return; } @@ -220,7 +231,7 @@ class KiwiInstance : public pp::Instance { if (length == target) return true; - LogMessage(-1) << "Invalid " << type << " request (" << length + ErrorMessage() << "Invalid " << type << " request (" << length << " != " << target << ")."; return false; } @@ -228,7 +239,7 @@ class KiwiInstance : public pp::Instance { /* Receives and handles a version request */ bool SocketParseVersion(const char* data, int datalen) { if (connected_) { - LogMessage(-1) << "Got a version while connected?!?"; + ErrorMessage() << "Received a version while already connected."; return false; } @@ -237,12 +248,13 @@ class KiwiInstance : public pp::Instance { if (server_version_ != VERSION) { /* TODO: Remove VF1 compatiblity */ if (server_version_ == "VF1") { - LogMessage(-1) << "Outdated server version (" - << server_version_ << ")."; - /* FIXME: Clearly inform the user that the chroot is outdated. */ + WarningMessage() << "Outdated server version (" + << server_version_ << "), expecting " << VERSION + << ". Please update your chroot."; } else { - LogMessage(-1) << "Invalid version received (" - << server_version_ << ")."; + ErrorMessage() << "Invalid server version (" + << server_version_ << "), expecting " << VERSION + << ". Please update your chroot."; return false; } } @@ -302,7 +314,7 @@ class KiwiInstance : public pp::Instance { /* Receives and handles a cursor_reply request */ bool SocketParseCursor(const char* data, int datalen) { if (datalen < sizeof(struct cursor_reply)) { - LogMessage(-1) << "Invalid cursor_reply packet (" << datalen + ErrorMessage() << "Invalid cursor_reply packet (" << datalen << " < " << sizeof(struct cursor_reply) << ")."; return false; } @@ -365,6 +377,7 @@ class KiwiInstance : public pp::Instance { /* Not fatal: just wait for next call */ return; } else if (result != PP_OK) { + /* FIXME: Receive error is "normal" when fbserver exits. */ LogMessage(-1) << "Receive error."; SocketClose("Receive error."); return; @@ -410,12 +423,12 @@ class KiwiInstance : public pp::Instance { if (SocketParseResolution(data, datalen)) return; break; default: - LogMessage(-1) << "Invalid request. First char: " + ErrorMessage() << "Invalid request. First char: " << (int)data[0]; /* Fall-through: disconnect. */ } } else { - LogMessage(-1) << "Got some packet before version..."; + ErrorMessage() << "Got some packet before version..."; } SocketClose("Invalid payload."); From b09f3a4eda96266d09bb7a3eb44384488c4b008d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 19:02:54 +0100 Subject: [PATCH 218/334] kiwi: Fix connection retry code. Previous version was only making one attempt: Connect can only be called once per pp:WebSocket instance. --- host-ext/nacl_src/kiwi.cc | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 67359fa93..a75f32fef 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -184,19 +184,27 @@ class KiwiInstance : public pp::Instance { std::ostringstream url; url << "ws://localhost:" << (PORT_BASE + display_) << "/"; - websocket_.Connect(pp::Var(url.str()), NULL, 0, - callback_factory_.NewCallback( - &KiwiInstance::OnSocketConnectCompletion)); + websocket_.reset(new pp::WebSocket(this)); + websocket_->Connect(pp::Var(url.str()), NULL, 0, + callback_factory_.NewCallback( + &KiwiInstance::OnSocketConnectCompletion)); StatusMessage() << "Connecting..."; } /* Called when WebSocket is connected (or failed to connect) */ void OnSocketConnectCompletion(int32_t result) { if (result != PP_OK) { - StatusMessage() << "Connection failed (" - << result << "), retrying..."; - pp::Module::Get()->core()->CallOnMainThread(1000, - callback_factory_.NewCallback(&KiwiInstance::SocketConnect)); + retry_++; + if (retry_ < kMaxRetry) { + StatusMessage() << "Connection failed with code " << result + << ", " << retry_ << " attempt(s). Retrying..."; + pp::Module::Get()->core()->CallOnMainThread(1000, + callback_factory_.NewCallback(&KiwiInstance::SocketConnect)); + } else { + ErrorMessage() << "Connection failed (code: " << result << ")."; + ControlMessage("disconnected", "Connection failed"); + } + return; } @@ -209,7 +217,7 @@ class KiwiInstance : public pp::Instance { /* Closes the WebSocket connection. */ void SocketClose(const std::string& reason) { - websocket_.Close(0, pp::Var(reason), + websocket_->Close(0, pp::Var(reason), callback_factory_.NewCallback(&KiwiInstance::OnSocketClosed)); } @@ -437,7 +445,7 @@ class KiwiInstance : public pp::Instance { /* Asks to receive the next WebSocket frame * Parameter is ignored: used for callbacks */ void SocketReceive(int32_t /*result*/ = 0) { - websocket_.ReceiveMessage(&receive_var_, callback_factory_.NewCallback( + websocket_->ReceiveMessage(&receive_var_, callback_factory_.NewCallback( &KiwiInstance::OnSocketReceiveCompletion)); } @@ -457,11 +465,11 @@ class KiwiInstance : public pp::Instance { mm->x = mouse_pos_.x(); mm->y = mouse_pos_.y(); array_buffer.Unmap(); - websocket_.SendMessage(array_buffer); + websocket_->SendMessage(array_buffer); pending_mouse_move_ = false; } - websocket_.SendMessage(var); + websocket_->SendMessage(var); } /** UI functions **/ @@ -1019,6 +1027,8 @@ class KiwiInstance : public pp::Instance { const int kBlurFPS = 5; /* fps when window is possibly hidden */ const int kHiddenFPS = 0; /* fps when window is hidden */ + const int kMaxRetry = 3; /* Maximum number of connection attempts */ + /* Class members */ pp::CompletionCallbackFactory callback_factory_{this}; pp::Graphics2D context_; @@ -1031,7 +1041,8 @@ class KiwiInstance : public pp::Instance { pp::ImageData image_data_; int k_ = 0; - pp::WebSocket websocket_{this}; + std::unique_ptr websocket_; + int retry_ = 0; bool connected_ = false; std::string server_version_ = ""; bool screen_flying_ = false; From c0023425b715c89633747a68d811fce33ac5d684 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 19:44:29 +0100 Subject: [PATCH 219/334] Slightly better close button UI. --- host-ext/crouton/window.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 2dea627bb..a57d9a1ab 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -49,9 +49,8 @@ background-color: rgba(128, 128, 0, 1); } .close { - float: right; background-color: rgba(0,0,0,0); - color: rgba(255,255,255,1); + color: rgba(255,255,0,1); font-size: 12pt; padding-bottom: 1px; padding-top: 2px; @@ -65,6 +64,9 @@ .close:active { background-color: rgba(0,0,0,0.5); } + .close:hover { + color: rgba(255,0,0,1); + } #error { background-color: rgba(128, 0, 0, 1); } @@ -88,8 +90,9 @@
Initializing...
-
WARNING: - +
+ + WARNING:
ERROR:
From 6aac2c24f0387aff835e772b37def9536dab1652 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 14 Jan 2015 19:26:22 +0100 Subject: [PATCH 220/334] extension: Bump version to 2.1.1 --- host-ext/crouton/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index c598db771..9f270912c 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.1.0", + "version": "2.1.1", "icons": { "48": "icon-48.png", "128": "icon-128.png" From c96644d7fb8cc5be864364558b990e94a90b2aa5 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 19 Jan 2015 13:16:21 +0100 Subject: [PATCH 221/334] enter-chroot/audio: Fix warning text --- host-bin/enter-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 637995b20..0af8718eb 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -378,7 +378,7 @@ if [ -d /var/run/cras ]; then ln -sfT /var/host/cras "$(fixabslinks '/var/run')/cras" else echo "\ -WARNING: CRAS control directory not found, audio forwarding will not work." 1>&2 +WARNING: CRAS not running in Chromium OS. Audio forwarding will not work." 1>&2 fi # Bind-mount /media, specifically the removable directory From a983b449c519889825b4ff61271cf7a3425a7378 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 7 Jan 2015 20:17:19 +0100 Subject: [PATCH 222/334] audio/cras: Remove uneeded patches. Volume control patch will need to wait a few more weeks. (6158 is still stable on some platforms). --- targets/audio | 164 -------------------------------------------------- 1 file changed, 164 deletions(-) diff --git a/targets/audio b/targets/audio index b068de0fe..b3a9f2cb7 100644 --- a/targets/audio +++ b/targets/audio @@ -117,90 +117,6 @@ if tail -n 1 "$log" | grep -q "^Error"; then fi fi -# Patch CRAS so that x86 client can connect to x86_64 server (multilib) -# Assume current directory is "$CRASBUILDTMP"/cras/src -patch_cras_x86_x86_64() { - echo "Patching CRAS (x86 client with x86_64 server)..." 1>&2 - - common="common/cras_shm.h common/cras_messages.h common/cras_iodev_info.h \ - common/cras_types.h common/cras_audio_format.c" - cras_audio_format_h="" - - # Newer versions (>=5062) contain an additional header file - if [ -e "common/cras_audio_format.h" ]; then - cras_audio_format_h="common/cras_audio_format.h" - fi - - # Replace size_t/long by fixed-size integers corresponding to their - # respective sizes on x86_64, aligned on 8-byte boundaries - sed -i -e 's/uint64_t[ \t]/aligned_uint64_t /g - s/size_t[ \t]/aligned_uint64_t /g - s/long[ \t]/aligned_int64_t /g' $common $cras_audio_format_h - - # Hack to make sure sizeof(struct cras_server/client_message) is a - # multiple of 8 - sed -i -e \ - 's/\(enum CRAS_CLIENT_MESSAGE_ID\) id;/\1 __attribute__((aligned(8))) id;/ - s/\(enum CRAS_SERVER_MESSAGE_ID\) id;/\1 __attribute__((aligned(8))) id;/' \ - common/cras_messages.h - - # Disable syslog to remove warnings about printf formats - sed -i -e '/#include /a \ -#define syslog(...) do \{\} while(0)' \ - common/cras_fmt_conv.c libcras/cras_client.c - - # Replace timespec/timeval - sed -i -e 's/struct timespec[ \t]/struct cras_timespec /g - s/clock_gettime(/cras_clock_gettime(/' \ - $common $cras_audio_format_h common/cras_util.h \ - libcras/cras_client.h libcras/cras_client.c \ - alsa_plugin/pcm_cras.c tests/cras_test_client.c - sed -i -e \ - 's/struct timeval/struct { aligned_int64_t tv_sec; aligned_int64_t tv_usec; }/' \ - common/cras_iodev_info.h - - # Include compat file (it will get included multiple times, but this is - # harmless compared to the complexity of a sed script that replaces the - # first match only) - sed -i -e '/#include.*/i \ -#include "cras_x86_64_compat.h"' common/cras_iodev_info.h $cras_audio_format_h - - # Create a new header file with x86_64 compatibility types: aligned integers - # and timespec wrapper. - cat > common/cras_x86_64_compat.h < -#include - -typedef uint64_t __attribute__((aligned(8))) aligned_uint64_t; -typedef int64_t __attribute__((aligned(8))) aligned_int64_t; - -struct cras_timespec { - aligned_int64_t tv_sec; - aligned_int64_t tv_nsec; -}; - -static inline int cras_clock_gettime(clockid_t clk_id, struct cras_timespec *ctp) { - struct timespec tp; - int ret = clock_gettime(clk_id, &tp); - ctp->tv_sec = tp.tv_sec; - ctp->tv_nsec = tp.tv_nsec; - return ret; -} -#endif -END - - # Restore uncorrectly replaced timespecs, add 2 necessary casts - sed -i -e 's/struct cras_timespec sleep_ts;/struct timespec sleep_ts;/ - s/struct cras_timespec wait_time;/struct timespec wait_time;/ - s/\(.*\)cras_clock_gettime\(.*&wait_time);.*\)/\1clock_gettime\2/ - s/nodes\[i\].priority/(size_t)nodes[i].priority/ - s/clients\[i\].id/(size_t)clients[i].id/' \ - tests/cras_test_client.c -} - # Build CRAS ALSA plugin for the given architecture ($1) # A blank parameter means we are building for the native architecture. build_cras() { @@ -283,86 +199,6 @@ build_cras() { install --minimal --asdeps patch - # Fix delay function in CRAS ALSA plugin, see http://crbug.com/331637 - # Applied upstream in version 5215 - # FIXME: Remove this when 5215 has trickled down to stable - if [ "$CROS_VER_1" -lt 5215 ]; then - echo "Patching CRAS (delay function bug)..." 1>&2 - patch -p3 <stream == SND_PCM_STREAM_PLAYBACK) { -- cras_client_calc_playback_latency(&pcm_cras->playback_sample_time, -- &latency); -+ /* Do not compute latency if playback_sample_time is not set */ -+ if (pcm_cras->playback_sample_time.tv_sec == 0 && -+ pcm_cras->playback_sample_time.tv_nsec == 0) { -+ latency.tv_sec = latency.tv_nsec = 0; -+ } else { -+ cras_client_calc_playback_latency(&pcm_cras->playback_sample_time, -+ &latency); -+ } -+ - *delayp = limit + io->appl_ptr - pcm_cras->playback_sample_index + - latency.tv_sec * io->rate + -- latency.tv_nsec / (1000000000L / io->rate); -+ latency.tv_nsec / (1000000000L / (long)io->rate); - } else { -- cras_client_calc_capture_latency(&pcm_cras->capture_sample_time, -- &latency); -+ /* Do not compute latency if capture_sample_time is not set */ -+ if (pcm_cras->capture_sample_time.tv_sec == 0 && -+ pcm_cras->capture_sample_time.tv_nsec == 0) { -+ latency.tv_sec = latency.tv_nsec = 0; -+ } else { -+ cras_client_calc_capture_latency(&pcm_cras->capture_sample_time, -+ &latency); -+ } -+ - *delayp = limit + pcm_cras->capture_sample_index - io->appl_ptr + - latency.tv_sec * io->rate + -- latency.tv_nsec / (1000000000L / io->rate); -+ latency.tv_nsec / (1000000000L / (long)io->rate); - } - - /* Both appl and hw pointers wrap at the pcm boundary. */ -END - fi - - # Fix cras_test_client hanging on exit, see http://crbug.com/347997 - # Introduced in upstream build 5339, fixed in 5579 - # FIXME: Remove this when R34/5500 is not an active release anymore - if [ "$CROS_VER_1" -ge 5339 -a "$CROS_VER_1" -lt 5579 ]; then - echo "Patching CRAS (cras_test_client bug)..." 1>&2 - patch -p3 <server_state) - shmdt(client->server_state); - if (client->server_fd >= 0) -END - fi - - # CRAS needs to be patched so that x86 client can access x86_64 server - # Applied upstream in build 5652, see http://crbug.com/309961 - # FIXME: Remove this when 5652 has trickled down to stable - if [ "$CROS_VER_1" -lt 5652 ] && [ "`uname -m`" = "x86_64" ] && - [ "$ARCH" = "i386" -o "$cras_arch" = "i386" ]; then - patch_cras_x86_x86_64 - fi - # Fix volume control, see crbug.com/415661 # Applied upstream in build 6283 # FIXME: Remove this when 6283 has trickled down to stable From aa6631c820649ad25f7d829cc98fae45ea803ea5 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 19 Jan 2015 14:18:02 +0100 Subject: [PATCH 223/334] wrapper.sh: Update minimum version to 5712/35. --- build/wrapper.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wrapper.sh b/build/wrapper.sh index e0cbe9f40..ea7f11d5c 100644 --- a/build/wrapper.sh +++ b/build/wrapper.sh @@ -38,8 +38,8 @@ set -e VERSION='git' -# Minimum Chromium OS version is R31 stable -CROS_MIN_VERS=4731 +# Minimum Chromium OS version is R35 stable +CROS_MIN_VERS=5712 if [ "$1" = '-x' -a "$#" -le 2 ]; then # Extract to the specified directory. From 804444c47c0600bad1b0a2941b22b9de057dd34e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 7 Jan 2015 20:41:44 +0100 Subject: [PATCH 224/334] audio/cras: Remove SBC dependency from cras_test_client SBC is only needed for a test within cras_test_client. Replace SBC functions by stubs. --- targets/audio | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/targets/audio b/targets/audio index b3a9f2cb7..bfc33af62 100644 --- a/targets/audio +++ b/targets/audio @@ -165,23 +165,6 @@ build_cras() { pkg-config --variable=libdir alsa`/alsa-lib" fi - # SBC is not available in older Debian/Ubuntu: fetch it manually - if release -le quantal; then - install_mirror_package 'libsbc1' \ - 'pool/main/s/sbc' '1\.1-.*' $cras_arch - install_mirror_package --asdeps 'libsbc-dev' \ - 'pool/main/s/sbc' '1\.1-.*' $cras_arch - elif release -le wheezy -eq kali; then - # wheezy provides a backport for libsbc1 - install_mirror_package 'libsbc1' \ - 'pool/main/s/sbc' '.*~bpo7.*' $cras_arch - install_mirror_package --asdeps 'libsbc-dev' \ - 'pool/main/s/sbc' '.*~bpo7.*' $cras_arch - else - install --minimal libsbc1$pkgsuffix - install --minimal --asdeps libsbc-dev$pkgsuffix - fi - # Start subshell for compilation ( cd "$CRASBUILDTMP" @@ -350,6 +333,25 @@ quit: END fi + # Remove SBC dependency + sed -e 's/#include common/cras_sbc_codec.c < +#include +#include "cras_audio_codec.h" + +struct cras_audio_codec *cras_sbc_codec_create(uint8_t freq, + uint8_t mode, uint8_t subbands, uint8_t alloc, + uint8_t blocks, uint8_t bitpool) { + abort(); +} +void cras_sbc_codec_destroy(struct cras_audio_codec *codec) { + abort(); +} +END + # Drop SBC constants + sed -e 's/SBC_[A-Z0-9_]*/0/g' -i tests/cras_test_client.c + # Directory to install CRAS library/binaries CRASLIBDIR="/usr/local$archextrapath/lib" CRASBINDIR="/usr/local$archextrapath/bin" @@ -357,13 +359,6 @@ END echo "Compiling CRAS (${cras_arch:-native})..." 1>&2 # Convert Makefile.am to a shell script, and run it. { - echo ' - SBC_LIBS="'"`PKG_CONFIG_PATH="$pkgconfigpath" \ - pkg-config --libs sbc`"'" - SBC_CFLAGS="'"`PKG_CONFIG_PATH="$pkgconfigpath" \ - pkg-config --cflags sbc`"'" - ' - convert_automake echo ' From 4b0355a740ea81162b4e38174666e53bc9a40e8d Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 19 Jan 2015 20:07:29 +0100 Subject: [PATCH 225/334] tests: Remove references to libsbc1 --- test/tests/18-upgrade | 2 +- test/tests/30-audio | 34 ---------------------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/test/tests/18-upgrade b/test/tests/18-upgrade index 28db38a6e..01f5c2f4c 100644 --- a/test/tests/18-upgrade +++ b/test/tests/18-upgrade @@ -33,7 +33,7 @@ fi # packages from the mirror, or install alternative packages. TARGETS="core,audio,touch,x11" -PACKAGES="libsbc1 touchegg" +PACKAGES="touchegg" # upgrade [-d] release # -d: upgrade to a development release diff --git a/test/tests/30-audio b/test/tests/30-audio index d1c1bb408..f99a863b5 100644 --- a/test/tests/30-audio +++ b/test/tests/30-audio @@ -57,38 +57,4 @@ exitswithin 0 30 host enter-chroot -n "$release" sh -exc ' pulseaudio --kill ' -# On precise, test that install_mirror_package is able to up/downgrade -# libsbc1 as required. We first install version 1.2, then check that -# update downgrades it to 1.1 again. -if [ "$release" = "precise" ]; then - # Test if libsbc1 version starts with the argument - testsbcver() { - host enter-chroot -n "$release" sh -exc ' - ok='' - for ver in `dpkg-query -l libsbc1:* | - awk '"'"'/^[hi]i/ { print $3 }'"'"'`; do - echo $ver | grep -q "^'"$1"'" - ok='y' - done - test -n "$ok" - ' - } - - echo ' - cras_arch='' - if [ "`uname -m`" = "x86_64" ]; then - cras_arch="i386" - fi - install_mirror_package 'libsbc1' \ - 'pool/main/s/sbc' '1\.2-.*' $cras_arch - install_mirror_package 'libsbc-dev' \ - 'pool/main/s/sbc' '1\.2-.*' $cras_arch - ' | crouton -T -U -n "$release" - - testsbcver '1.2-' - - crouton -u -n "$release" -t audio - testsbcver '1.1-' -fi - host delete-chroot -y "$release" From af3d10fe6d44bf9770dca936dfa8270a35f669e6 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 21 Jan 2015 19:55:46 +0100 Subject: [PATCH 226/334] window.html: Space out rgba --- host-ext/crouton/window.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index a57d9a1ab..9d381c227 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -49,8 +49,8 @@ background-color: rgba(128, 128, 0, 1); } .close { - background-color: rgba(0,0,0,0); - color: rgba(255,255,0,1); + background-color: rgba(0, 0, 0, 0); + color: rgba(255, 255, 0, 1); font-size: 12pt; padding-bottom: 1px; padding-top: 2px; @@ -62,10 +62,10 @@ outline: none; } .close:active { - background-color: rgba(0,0,0,0.5); + background-color: rgba(0, 0, 0, 0.5); } .close:hover { - color: rgba(255,0,0,1); + color: rgba(255, 0, 0, 1); } #error { background-color: rgba(128, 0, 0, 1); From 04f9c87123e11f1357d20ab419e3ad34a517c539 Mon Sep 17 00:00:00 2001 From: DennisL Date: Wed, 21 Jan 2015 21:09:23 -0500 Subject: [PATCH 227/334] Update README.md Add tip about sharing files and folders. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 297f7af22..eaeefc64c 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,8 @@ Tips `croutonpowerd -i command and arguments` will automatically stop inhibiting power management when the command exits. * Have a Pixel or two or 4.352 million? `-t touch` improves touch support. + * Want to share some files and/or folders between ChromeOS and your chroot? + Then read all about it in the [wiki](https://github.com/dnschneid/crouton/wiki/Sharing-files-and-folders). * Want more tips? Check the [wiki](https://github.com/dnschneid/crouton/wiki). From f0efd7539d4be0a4eb9710da4cbbe5c87a25f2e9 Mon Sep 17 00:00:00 2001 From: DennisL Date: Wed, 21 Jan 2015 21:21:53 -0500 Subject: [PATCH 228/334] Update README.md Add mention of 'shares' file. Remove wiki link and add reference to it instead. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaeefc64c..6e9a7521e 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Tips power management when the command exits. * Have a Pixel or two or 4.352 million? `-t touch` improves touch support. * Want to share some files and/or folders between ChromeOS and your chroot? - Then read all about it in the [wiki](https://github.com/dnschneid/crouton/wiki/Sharing-files-and-folders). + Then use the `/etc/crouton/shares` file, read all about it in the wiki: Sharing files and folders. * Want more tips? Check the [wiki](https://github.com/dnschneid/crouton/wiki). From 5621444f38fe97ee19e21307531a5569eb3e83f0 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 22 Jan 2015 17:26:19 +0100 Subject: [PATCH 229/334] kiwi: Compensate for page zoom in resize messages to/from JS. Later on, we could add an option to actually obey the zoom settings. Fixes #1295. --- host-ext/nacl_src/kiwi.cc | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index a75f32fef..b17839fe9 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -70,7 +70,8 @@ class KiwiInstance : public pp::Instance { if (pos2 != std::string::npos) { int width = stoi(message.substr(pos+1, pos2-pos-1)); int height = stoi(message.substr(pos2+1)); - ChangeResolution(width*scale_, height*scale_); + ChangeResolution(width*scale_*view_zoom_level_ + 0.5, + height*scale_*view_zoom_level_ + 0.5); } } else if (type == "display") { int display = stoi(message.substr(pos+1)); @@ -161,9 +162,10 @@ class KiwiInstance : public pp::Instance { } } - /* Sends a resize message to Javascript */ - void ResizeMessage(int width, int height) { - Message(this, "resize", false) << width << "/" << height; + /* Sends a resize message to Javascript, divide width & height by scale */ + void ResizeMessage(int width, int height, float scale) { + Message(this, "resize", false) << (int)(width/scale + 0.5) << "/" + << (int)(height/scale + 0.5); } /* Sends a control message to Javascript @@ -370,7 +372,7 @@ class KiwiInstance : public pp::Instance { return false; struct resolution* r = (struct resolution*)data; /* Tell Javascript so that it can center us on the page */ - ResizeMessage(r->width/scale_, r->height/scale_); + ResizeMessage(r->width, r->height, scale_*view_zoom_level_); force_refresh_ = true; return true; } @@ -476,7 +478,8 @@ class KiwiInstance : public pp::Instance { public: /* Called when the NaCl module view changes (size, visibility) */ virtual void DidChangeView(const pp::View& view) { - view_scale_ = view.GetDeviceScale(); + view_device_scale_ = view.GetDeviceScale(); + view_zoom_level_ = view.GetCSSScale()/view.GetDeviceScale(); view_rect_ = view.GetRect(); InitContext(); } @@ -715,13 +718,15 @@ class KiwiInstance : public pp::Instance { if (view_rect_.width() <= 0 || view_rect_.height() <= 0) return; - scale_ = hidpi_ ? view_scale_ : 1.0f; + scale_ = hidpi_ ? view_device_scale_ : 1.0f; pp::Size new_size = pp::Size(view_rect_.width() * scale_, view_rect_.height() * scale_); LogMessage(0) << "InitContext " << new_size.width() << "x" << new_size.height() - << "s" << scale_; + << "s" << scale_ + << " (device scale: " << view_device_scale_ + << ", zoom level: " << view_zoom_level_ << ")"; const bool kIsAlwaysOpaque = true; context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque); @@ -750,7 +755,7 @@ class KiwiInstance : public pp::Instance { array_buffer.Unmap(); SocketSend(array_buffer, false); } else { /* Just assume we can take up the space */ - ResizeMessage(width/scale_, height/scale_); + ResizeMessage(width, height, scale_*view_zoom_level_); } } @@ -1034,7 +1039,8 @@ class KiwiInstance : public pp::Instance { pp::Graphics2D context_; pp::Graphics2D flush_context_; pp::Rect view_rect_; - float view_scale_ = 1.0f; + float view_device_scale_ = 1.0f; + float view_zoom_level_ = 1.0f; pp::Size size_; float scale_ = 1.0f; From d105509e95d50303041ddabfb38ce3679d847c0e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 22 Jan 2015 18:16:16 +0100 Subject: [PATCH 230/334] kiwi: GetCSSScale() gives DIP/CSS pixels ratio. And not device/CSS ratio. --- host-ext/nacl_src/kiwi.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index b17839fe9..7df041122 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -70,8 +70,8 @@ class KiwiInstance : public pp::Instance { if (pos2 != std::string::npos) { int width = stoi(message.substr(pos+1, pos2-pos-1)); int height = stoi(message.substr(pos2+1)); - ChangeResolution(width*scale_*view_zoom_level_ + 0.5, - height*scale_*view_zoom_level_ + 0.5); + ChangeResolution(width*scale_*view_css_scale_ + 0.5, + height*scale_*view_css_scale_ + 0.5); } } else if (type == "display") { int display = stoi(message.substr(pos+1)); @@ -372,7 +372,7 @@ class KiwiInstance : public pp::Instance { return false; struct resolution* r = (struct resolution*)data; /* Tell Javascript so that it can center us on the page */ - ResizeMessage(r->width, r->height, scale_*view_zoom_level_); + ResizeMessage(r->width, r->height, scale_*view_css_scale_); force_refresh_ = true; return true; } @@ -479,7 +479,7 @@ class KiwiInstance : public pp::Instance { /* Called when the NaCl module view changes (size, visibility) */ virtual void DidChangeView(const pp::View& view) { view_device_scale_ = view.GetDeviceScale(); - view_zoom_level_ = view.GetCSSScale()/view.GetDeviceScale(); + view_css_scale_ = view.GetCSSScale(); view_rect_ = view.GetRect(); InitContext(); } @@ -726,7 +726,7 @@ class KiwiInstance : public pp::Instance { << new_size.width() << "x" << new_size.height() << "s" << scale_ << " (device scale: " << view_device_scale_ - << ", zoom level: " << view_zoom_level_ << ")"; + << ", zoom level: " << view_css_scale_ << ")"; const bool kIsAlwaysOpaque = true; context_ = pp::Graphics2D(this, new_size, kIsAlwaysOpaque); @@ -755,7 +755,7 @@ class KiwiInstance : public pp::Instance { array_buffer.Unmap(); SocketSend(array_buffer, false); } else { /* Just assume we can take up the space */ - ResizeMessage(width, height, scale_*view_zoom_level_); + ResizeMessage(width, height, scale_*view_css_scale_); } } @@ -1040,7 +1040,7 @@ class KiwiInstance : public pp::Instance { pp::Graphics2D flush_context_; pp::Rect view_rect_; float view_device_scale_ = 1.0f; - float view_zoom_level_ = 1.0f; + float view_css_scale_ = 1.0f; pp::Size size_; float scale_ = 1.0f; From 0ad2d48ff8f09fc682473c1241e112e2d758c666 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 22 Jan 2015 11:49:40 -0800 Subject: [PATCH 231/334] Bump extension version. --- host-ext/crouton/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 9f270912c..8f16f7dd6 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.1.1", + "version": "2.1.2", "icons": { "48": "icon-48.png", "128": "icon-128.png" From c91e67d0889198baddb408d2dea810c943ee9e6f Mon Sep 17 00:00:00 2001 From: DennisL Date: Thu, 22 Jan 2015 15:05:41 -0500 Subject: [PATCH 232/334] Update README.md Tweaked per @dnschneid's line notes - I'm liking it... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e9a7521e..6de8a929c 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ Tips power management when the command exits. * Have a Pixel or two or 4.352 million? `-t touch` improves touch support. * Want to share some files and/or folders between ChromeOS and your chroot? - Then use the `/etc/crouton/shares` file, read all about it in the wiki: Sharing files and folders. + Check out the `/etc/crouton/shares` file, or read all about it in the wiki. * Want more tips? Check the [wiki](https://github.com/dnschneid/crouton/wiki). From f7301081d1b7624f3e7f10dd6af245c9dc4beb3d Mon Sep 17 00:00:00 2001 From: DennisL Date: Thu, 22 Jan 2015 17:05:48 -0500 Subject: [PATCH 233/334] Update README.md Added line-break ( 2 spaces ) after question. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6de8a929c..78151b12a 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ Tips `croutonpowerd -i command and arguments` will automatically stop inhibiting power management when the command exits. * Have a Pixel or two or 4.352 million? `-t touch` improves touch support. - * Want to share some files and/or folders between ChromeOS and your chroot? + * Want to share some files and/or folders between ChromeOS and your chroot? Check out the `/etc/crouton/shares` file, or read all about it in the wiki. * Want more tips? Check the [wiki](https://github.com/dnschneid/crouton/wiki). From 3b79cb77dd85c89f1f6103fde40b9983770396a2 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 23 Jan 2015 10:21:00 +0100 Subject: [PATCH 234/334] croutonnotify: Argument-less -d Use value in $DISPLAY by default. --- chroot-bin/croutonnotify | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify index 4b0b7c0d3..8863088d6 100755 --- a/chroot-bin/croutonnotify +++ b/chroot-bin/croutonnotify @@ -4,7 +4,7 @@ # found in the LICENSE file. USAGE="${0##*/} -j -${0##*/} -t title [-m message] [-d display] [-i id] +${0##*/} -t title [-m message] [-d] [-i id] Raises a notification in Chromium OS (requires crouton extension). When -j is specified, reads JSON data from stdin, in the format required @@ -18,8 +18,8 @@ Options: -j Read JSON data from stdin (see example below) -t Title to display (chrome.notifications field: \"title\") -m Message to display (chrome.notifications field: \"message\") - -d Display to switch to when the notification is clicked (croutoncycle - parameter). Default: Do not switch display. + -d Switch to display in environment variable DISPLAY ($DISPLAY) when + the notification is clicked. Default: Do not switch display. -i ID to pass to chrome.notifications.create: only the last notification with a given ID is shown. Default: Display a new notification. @@ -34,16 +34,16 @@ Example JSON data: . "$(dirname "$0")/../installer/functions" -DISPLAY="" +SWITCHDISPLAY="" MESSAGE="" TITLE="" ID="" JSON="" # Process arguments -while getopts 'd:i:jm:t:' f; do +while getopts 'di:jm:t:' f; do case "$f" in - d) DISPLAY="$OPTARG";; + d) SWITCHDISPLAY="$DISPLAY";; i) ID="$OPTARG";; j) JSON="y";; m) MESSAGE="$OPTARG";; @@ -55,7 +55,7 @@ done # No extra parameters, -j precludes other parameters, and at least title # must be specified (or -j) if [ "$#" != "$((OPTIND-1))" ] || - [ "$JSON" = 'y' -a -n "$DISPLAY$ID$MESSAGE$TITLE" ] || + [ "$JSON" = 'y' -a -n "$SWITCHDISPLAY$ID$MESSAGE$TITLE" ] || [ -z "$JSON$TITLE" ]; then error 2 "$USAGE" fi @@ -66,7 +66,7 @@ if [ -z "$JSON" ]; then "type": "'"basic"'", "title": "'"$TITLE"'", "message": "'"$MESSAGE"'", - "crouton_display": "'"$DISPLAY"'", + "crouton_display": "'"$SWITCHDISPLAY"'", "crouton_id": "'"$ID"'" }' fi From 63ebea8f8a5ce5183fa57bc52daa87e44a36fd07 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 23 Jan 2015 10:40:47 +0100 Subject: [PATCH 235/334] croutonnotify: Escape reverse solidi and quotation marks. --- chroot-bin/croutonnotify | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify index 8863088d6..547fd9f0f 100755 --- a/chroot-bin/croutonnotify +++ b/chroot-bin/croutonnotify @@ -60,14 +60,19 @@ if [ "$#" != "$((OPTIND-1))" ] || error 2 "$USAGE" fi +# Escape json string (backslash and double quotes) +json_escape() { + echo -n "$1" | sed -e 's/\\/\\u005C/g;s/"/\\u0022/g' +} + JSONDATA="" if [ -z "$JSON" ]; then JSONDATA='{ - "type": "'"basic"'", - "title": "'"$TITLE"'", - "message": "'"$MESSAGE"'", - "crouton_display": "'"$SWITCHDISPLAY"'", - "crouton_id": "'"$ID"'" + "type": "basic", + "title": "'"$(json_escape "$TITLE")"'", + "message": "'"$(json_escape "$MESSAGE")"'", + "crouton_display": "'"$(json_escape "$SWITCHDISPLAY")"'", + "crouton_id": "'"$(json_escape "$ID")"'" }' fi From 7f2cfc2565d862e12ddb5e1b8c515f5de2c8687f Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Fri, 23 Jan 2015 14:56:42 +0100 Subject: [PATCH 236/334] croutonnotify: Add example of how icon data can be embedded --- chroot-bin/croutonnotify | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify index 547fd9f0f..611ad9638 100755 --- a/chroot-bin/croutonnotify +++ b/chroot-bin/croutonnotify @@ -29,7 +29,8 @@ Example JSON data: "type": "basic", "title": "Primary Title", "message": "Primary message to display", - "crouton_display": ":1" + "crouton_display": ":1", + "iconUrl": "data:image/png;base64," }' . "$(dirname "$0")/../installer/functions" From 58170adcb62fb07eabc73a1fca2a2c654821ad2f Mon Sep 17 00:00:00 2001 From: DennisL Date: Tue, 27 Jan 2015 01:23:47 -0500 Subject: [PATCH 237/334] Update enter-chroot Add '-X' option to override the the auto-detected XMETHOD for this session/invocation. --- host-bin/enter-chroot | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 0af8718eb..0c0fe387e 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -14,6 +14,7 @@ LOGIN='' NAME='' TARGET='' USERNAME='1000' +TMPXMETHOD='' NOLOGIN='' SETUPSCRIPT='/prepare.sh' @@ -33,6 +34,7 @@ Options: -n NAME Name of the chroot to enter. Default: first one found in CHROOTS -t TARGET Only enter the chroot if it contains the specified TARGET. -u USERNAME Username (or UID) to log into. Default: 1000 (the primary user) + -X XMETHOD For this session, override the the auto-detected XMETHOD -x Does not log in, but directly executes the command instead. Note that the environment will be empty (sans TERM). Specify -x a second time to run the $SETUPSCRIPT script." @@ -71,6 +73,7 @@ while getopts 'bc:k:ln:t:u:x' f; do n) NAME="$OPTARG";; t) TARGET="$OPTARG";; u) USERNAME="$OPTARG";; + X) TMPXMETHOD="$OPTARG";; x) NOLOGIN="$((NOLOGIN+1))" [ "$NOLOGIN" -gt 2 ] && NOLOGIN=2;; \?) error 2 "$USAGE";; @@ -676,6 +679,12 @@ else else # Escape out the command cmd="export SHELL='$CHROOTSHELL';" + if [ -n "$TMPXMETHOD" ]; then +- case "$TMPXMETHOD" in +- xiwi|xorg|xephyr) cmd="$cmd export XMETHOD='$TMPXMETHOD';" ;; +- *) echo "WARNING: XMETHOD '$TMPXMETHOD' invalid so not set." ;; +- esac +- fi for param in "$@"; do cmd="$cmd'`echo -n "$param" | sed "s/'/'\\\\\\''/g"`' " done From 90d0469cc826e9601373d46c81ff8f49e9e41592 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 27 Jan 2015 10:26:35 +0100 Subject: [PATCH 238/334] croutonfindnacl: Match Chromium shm as well. Necessary for Chromium OS builds, fixes #1376. --- chroot-bin/croutonfindnacl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chroot-bin/croutonfindnacl b/chroot-bin/croutonfindnacl index 32c952617..80d54ea24 100755 --- a/chroot-bin/croutonfindnacl +++ b/chroot-bin/croutonfindnacl @@ -42,7 +42,8 @@ for pid in $PIDS; do [ -n "$VERBOSE" ] && echo "pid:$pid" 1>&2 # Find candidate mappings file="`awk '$1 ~ /^[0-9a-f]*'"$ADDRESS"'-/ && $2 == "rw-s" \ - && $6 ~ /\/shm\/.com.google.Chrome/ { print $6 } + && $6 ~ /\/shm\/\.(com\.google\.Chrome|org\.chromium\.Chromium)/ \ + { print $6 } ' "/proc/$pid/maps"`" [ -n "$VERBOSE" ] && echo "file:$file" 1>&2 if [ -z "$file" ]; then From df484b3b2f1b23c9c93251285539c16e31ec0577 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 28 Jan 2015 18:58:36 +0100 Subject: [PATCH 239/334] croutonnotify: Allow icon specification with '-i icon' parameter. Icon is base64 encoded and sent to the extension. --- chroot-bin/croutonnotify | 47 +++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify index 611ad9638..6a612f0d1 100755 --- a/chroot-bin/croutonnotify +++ b/chroot-bin/croutonnotify @@ -4,7 +4,7 @@ # found in the LICENSE file. USAGE="${0##*/} -j -${0##*/} -t title [-m message] [-d] [-i id] +${0##*/} -t title [-m message] [-d] [-i icon] [-I id] Raises a notification in Chromium OS (requires crouton extension). When -j is specified, reads JSON data from stdin, in the format required @@ -20,7 +20,9 @@ Options: -m Message to display (chrome.notifications field: \"message\") -d Switch to display in environment variable DISPLAY ($DISPLAY) when the notification is clicked. Default: Do not switch display. - -i ID to pass to chrome.notifications.create: only the last + -i Small icon to display on the left of the notification + (chrome.notifications field: \"iconUrl\") + -I ID to pass to chrome.notifications.create: only the last notification with a given ID is shown. Default: Display a new notification. @@ -39,13 +41,15 @@ SWITCHDISPLAY="" MESSAGE="" TITLE="" ID="" +ICON="" JSON="" # Process arguments -while getopts 'di:jm:t:' f; do +while getopts 'di:I:jm:t:' f; do case "$f" in d) SWITCHDISPLAY="$DISPLAY";; - i) ID="$OPTARG";; + i) ICON="$OPTARG";; + I) ID="$OPTARG";; j) JSON="y";; m) MESSAGE="$OPTARG";; t) TITLE="$OPTARG";; @@ -56,30 +60,37 @@ done # No extra parameters, -j precludes other parameters, and at least title # must be specified (or -j) if [ "$#" != "$((OPTIND-1))" ] || - [ "$JSON" = 'y' -a -n "$SWITCHDISPLAY$ID$MESSAGE$TITLE" ] || + [ "$JSON" = 'y' -a -n "$SWITCHDISPLAY$ICON$ID$MESSAGE$TITLE" ] || [ -z "$JSON$TITLE" ]; then error 2 "$USAGE" fi +if [ -n "$ICON" ] && [ ! -r "$ICON" -o ! -f "$ICON" ]; then + error 2 "Cannot open $ICON." +fi + # Escape json string (backslash and double quotes) json_escape() { echo -n "$1" | sed -e 's/\\/\\u005C/g;s/"/\\u0022/g' } -JSONDATA="" -if [ -z "$JSON" ]; then - JSONDATA='{ - "type": "basic", - "title": "'"$(json_escape "$TITLE")"'", - "message": "'"$(json_escape "$MESSAGE")"'", - "crouton_display": "'"$(json_escape "$SWITCHDISPLAY")"'", - "crouton_id": "'"$(json_escape "$ID")"'" - }' -fi - STATUS="$({ - echo -n "N$JSONDATA" - if [ "$JSON" = 'y' ]; then + echo -n "N" + if [ -z "$JSON" ]; then + echo -n '{ + "type": "basic", + "title": "'"$(json_escape "$TITLE")"'", + "message": "'"$(json_escape "$MESSAGE")"'", + "crouton_display": "'"$(json_escape "$SWITCHDISPLAY")"'", + "crouton_id": "'"$(json_escape "$ID")"'"' + if [ -n "$ICON" ]; then + ext="${ICON##*.}" + echo -n ', "iconUrl": "data:image/'"${ext:-png}"';base64,' + base64 -w 0 "$ICON" + echo -n '"' + fi + echo -n '}' + else cat fi } | websocketcommand)" From 79b1a826e3bd47a7eaa4e75967d8c3396fa9b864 Mon Sep 17 00:00:00 2001 From: DennisL Date: Thu, 29 Jan 2015 16:06:48 -0500 Subject: [PATCH 240/334] Update enter-chroot Added missing X option to getopts string --- host-bin/enter-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 0c0fe387e..6f64bf165 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -59,7 +59,7 @@ chrootcmd() { # Process arguments prevoptind=1 -while getopts 'bc:k:ln:t:u:x' f; do +while getopts 'bc:k:ln:t:u:X:x' f; do # Disallow empty string as option argument if [ "$((OPTIND-prevoptind))" = 2 -a -z "$OPTARG" ]; then error 2 "$USAGE" From 1ff0ad17b74640585ce9d42ee20fb097019dbf5e Mon Sep 17 00:00:00 2001 From: DennisL Date: Thu, 29 Jan 2015 17:01:56 -0500 Subject: [PATCH 241/334] Updare enter-chroot Check that XMETHOD requested is actually installed. --- host-bin/enter-chroot | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 6f64bf165..903f3fa5b 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -136,6 +136,13 @@ elif [ -n "$TARGET" ]; then fi fi +# Check to ensure that the XMETHOD requested has been installed +if [ -n "$TMPXMETHOD" ]; then + if ! grep -q "^$TMPXMETHOD$" "$CHROOTS/$NAME/.crouton-targets" 2>/dev/null; then + error 1 "$CHROOTS/$NAME does not contain XMETHOD '$TMPXMETHOD'" + fi +fi + # Avoid kernel panics due to slow I/O disablehungtask @@ -680,11 +687,8 @@ else # Escape out the command cmd="export SHELL='$CHROOTSHELL';" if [ -n "$TMPXMETHOD" ]; then -- case "$TMPXMETHOD" in -- xiwi|xorg|xephyr) cmd="$cmd export XMETHOD='$TMPXMETHOD';" ;; -- *) echo "WARNING: XMETHOD '$TMPXMETHOD' invalid so not set." ;; -- esac -- fi + cmd="$cmd export XMETHOD='$TMPXMETHOD';" + fi for param in "$@"; do cmd="$cmd'`echo -n "$param" | sed "s/'/'\\\\\\''/g"`' " done From 0e4f97124c4d1ab44684f25aa0910a08560b968e Mon Sep 17 00:00:00 2001 From: DennisL Date: Fri, 30 Jan 2015 23:35:53 -0500 Subject: [PATCH 242/334] Update chroot Clarified -X option description. --- host-bin/enter-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 903f3fa5b..74ab05f95 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -34,7 +34,7 @@ Options: -n NAME Name of the chroot to enter. Default: first one found in CHROOTS -t TARGET Only enter the chroot if it contains the specified TARGET. -u USERNAME Username (or UID) to log into. Default: 1000 (the primary user) - -X XMETHOD For this session, override the the auto-detected XMETHOD + -X XMETHOD Override the auto-detected XMETHOD for this session. -x Does not log in, but directly executes the command instead. Note that the environment will be empty (sans TERM). Specify -x a second time to run the $SETUPSCRIPT script." From 4a26158c2b42aa830afa5d9b2dea133e1ca90453 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 19:02:38 -0800 Subject: [PATCH 243/334] croutonnotify: fix newline escaping and add SVG support --- chroot-bin/croutonnotify | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chroot-bin/croutonnotify b/chroot-bin/croutonnotify index 6a612f0d1..f86d31bcd 100755 --- a/chroot-bin/croutonnotify +++ b/chroot-bin/croutonnotify @@ -71,7 +71,8 @@ fi # Escape json string (backslash and double quotes) json_escape() { - echo -n "$1" | sed -e 's/\\/\\u005C/g;s/"/\\u0022/g' + echo -n "$1" | sed -e 's/\\/\\u005C/g;s/"/\\u0022/g;2~1s/^/\\u000A/;' \ + | tr -d '\n' } STATUS="$({ @@ -85,9 +86,12 @@ STATUS="$({ "crouton_id": "'"$(json_escape "$ID")"'"' if [ -n "$ICON" ]; then ext="${ICON##*.}" + if grep -Iq ' Date: Fri, 6 Feb 2015 19:08:13 -0800 Subject: [PATCH 244/334] Bump extension version --- host-ext/crouton/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 373aaf0de..300874770 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.1.2", + "version": "2.2.0", "icons": { "48": "icon-48.png", "128": "icon-128.png" From 3401dc491560e113052c888f220475936b6c452f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 19:14:11 -0800 Subject: [PATCH 245/334] Fix switching to displays with .0's at the end --- chroot-bin/croutoncycle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 9e423d813..1d1c152c1 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -39,7 +39,7 @@ case "$1" in [Pp]*) cmd='p';; [Nn]*) cmd='n';; [Ss]*) cmd='s' disp="${1#s}";; -:*) cmd=":$((${1#:}))";; +:*) cmd="${1%%.*}"; cmd=":$((${cmd#:}))";; [0-9]*) cmd="$(($1))";; *) error 2 "$USAGE";; esac From 400c9196835c53f7dbc0ebc3568fd0d7e15ef02a Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 11 Feb 2015 15:20:47 +0800 Subject: [PATCH 246/334] autotest: Remove hack to download crouton autotest crouton autotest files are now included in test images, making this irrelevant. --- test/autotest_control.template | 7 ------- test/daemon.sh | 4 ---- 2 files changed, 11 deletions(-) diff --git a/test/autotest_control.template b/test/autotest_control.template index 4cc106eef..72b7695ed 100644 --- a/test/autotest_control.template +++ b/test/autotest_control.template @@ -25,13 +25,6 @@ This test fetches a specific branch of crouton, and runs crouton tests. # For debugging purpose utils.system('cat /etc/lsb-release') -# Add repository so that the test tarball can be fetched -# FIXME: Remove this hack when test is merged -devserver_url = 'http://172.19.140.1:8082' -testcsum = """###TESTCSUM###""" -utils.system("curl '" + devserver_url + "/stage?archive_url=gs://drinkcat-crouton/crouton-test/packages-" + testcsum + "&files=packages.checksum,test-platform_Crouton.tar.bz2'") -job.add_repository([devserver_url + "/static/crouton-test/packages-" + testcsum]) - args_dict = { 'repo': """###REPO###""", 'branch': """###BRANCH###""", diff --git a/test/daemon.sh b/test/daemon.sh index a113b0344..6dbafc533 100755 --- a/test/daemon.sh +++ b/test/daemon.sh @@ -38,9 +38,6 @@ MIRRORENV="" # Maximum test run time (minutes): 24 hours MAXTESTRUNTIME="$((24*60))" GSAUTOTEST="gs://chromeos-autotest-results" -# FIXME: Remove this when test is merged (>=R39): a temporary hack fetches -# the crouton test from gs://drinkcat-crouton/crouton-test/packages -TESTCSUM="811e9713f6357ee3996cb7cce80a3e2b" USAGE="$APPLICATION [options] -q QUEUEURL @@ -373,7 +370,6 @@ while sleep "$POLLINTERVAL"; do -e "s|###BRANCH###|$branch|" \ -e "s|###RUNARGS###|$params|" \ -e "s|###ENV###|$MIRRORENV|" \ - -e "s|###TESTCSUM###|$TESTCSUM|" \ $SCRIPTDIR/test/autotest_control.template \ > "$curtesthostroot/control" From e561cd944d326174de63456c02c0a9ce5938dbe7 Mon Sep 17 00:00:00 2001 From: DennisL Date: Sat, 14 Feb 2015 02:40:53 -0500 Subject: [PATCH 247/334] Add xmethod Show the current XMETHOD in the output. --- chroot-bin/croutonversion | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chroot-bin/croutonversion b/chroot-bin/croutonversion index 5289f83c8..169f31664 100755 --- a/chroot-bin/croutonversion +++ b/chroot-bin/croutonversion @@ -72,6 +72,10 @@ if [ -z "$CHANGES$DOWNLOAD$UPDATES" ]; then echo "crouton: version $VERSION" echo "release: $RELEASE" echo "architecture: $ARCH" + xmethodfile='/etc/crouton/xmethod' + if [ -r "$xmethodfile" ]; then + echo "xmethod: `cat "$xmethodfile"`" + fi targetfile='/etc/crouton/targets' if [ -r "$targetfile" ]; then echo "targets: `sed 's/^,//' "$targetfile"`" From 4dfe9e0a4c170e6083f60417a9af651d2366f811 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 14 Feb 2015 14:20:20 -0800 Subject: [PATCH 248/334] Make croutonfbserver setuid root. Workaround for #1411. This is not a good final solution. --- targets/xiwi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/targets/xiwi b/targets/xiwi index dadbe660b..8dbc61e67 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -23,6 +23,9 @@ install xorg xserver-xorg-video-dummy compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ libx11-dev libxfixes-dev libxdamage-dev libxext-dev libxtst-dev +# Make croutonfbserver setuid root. See issue #1411; this is way insecure +chmod u+s /usr/local/bin/croutonfbserver + ln -sf /etc/crouton/xorg-dummy.conf /etc/X11/ # Download the latest xf86-video-dummy package From 67287efb3c7308c9dafefe189f88962159843266 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 13 Nov 2014 18:31:14 -0800 Subject: [PATCH 249/334] chroot-etc/xserverrc-x11-freon Should probably be merged into xserverrc-x11, and the right approach selected at runtime. --- chroot-etc/xserverrc-x11-freon | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 chroot-etc/xserverrc-x11-freon diff --git a/chroot-etc/xserverrc-x11-freon b/chroot-etc/xserverrc-x11-freon new file mode 100644 index 000000000..73117bca9 --- /dev/null +++ b/chroot-etc/xserverrc-x11-freon @@ -0,0 +1,21 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Disable framebuffer compression when displaying natively. +# TODO: test FBC with SNA enabled on Haswell to regain power savings +fbc='/sys/module/i915/parameters/i915_enable_fbc' +if [ -w "$fbc" ]; then + echo 0 > "$fbc" +fi + +XMETHOD='x11' +XARGS='-nolisten tcp' +XARGS="$XARGS -logfile /tmp/Xorg.1234.log" +if [ -f /etc/crouton/xserverrc-local ]; then + . /etc/crouton/xserverrc-local +fi + +export LD_PRELOAD=/usr/local/lib/crouton_freon.so +exec /usr/bin/Xorg $XARGS "$@" From fe5bfb2e6a618e96a128363d846804a8ed792166 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 13 Nov 2014 18:43:28 -0800 Subject: [PATCH 250/334] freon.c: Dark magic LD_PRELOAD hack to make X11 happy in freon A simple pkill -USR1 Xorg switches display. Neat. --- Makefile | 3 + chroot-etc/xserverrc-x11-freon | 2 +- src/freon.c | 117 +++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/freon.c diff --git a/Makefile b/Makefile index 9a3eb63a1..d16b848d5 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,9 @@ $(EXTTARGET): $(EXTSOURCES) Makefile $(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ +croutonfreon.so: src/freon.c Makefile + gcc -g -Wall -Werror -shared -fPIC -ldl src/freon.c -o croutonfreon.so + extension: $(EXTTARGET) $(CONTRIBUTORS): $(GITHEAD) $(CONTRIBUTORSSED) diff --git a/chroot-etc/xserverrc-x11-freon b/chroot-etc/xserverrc-x11-freon index 73117bca9..0335f356b 100644 --- a/chroot-etc/xserverrc-x11-freon +++ b/chroot-etc/xserverrc-x11-freon @@ -17,5 +17,5 @@ if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi -export LD_PRELOAD=/usr/local/lib/crouton_freon.so +export LD_PRELOAD=/usr/local/lib/croutonfreon.so exec /usr/bin/Xorg $XARGS "$@" diff --git a/src/freon.c b/src/freon.c new file mode 100644 index 000000000..fb6b6038f --- /dev/null +++ b/src/freon.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2014 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * LD_PRELOAD hack to make Xorg happy in a system without VT-switching. + * gcc -shared -fPIC -ldl -Wall -O2 freon.c -o croutonfreon.so + * + * Powered by black magic. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int tty0fd = -1; +static int tty7fd = -1; + +static int (*orig_ioctl)(int d, int request, void* data); +static int (*orig_open)(const char *pathname, int flags, mode_t mode); +static int (*orig_close)(int fd); + +static void preload_init() { + orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); + orig_open = dlsym(RTLD_NEXT, "open"); + orig_close = dlsym(RTLD_NEXT, "close"); +} + +int ioctl(int fd, unsigned long int request, ...) { + if (!orig_ioctl) preload_init(); + + int ret = 0; + va_list argp; + + va_start(argp, request); + + void* data = va_arg(argp, void*); + + if (fd == tty0fd) { + fprintf(stderr, "ioctl tty0 %d %lx %p\n", fd, request, data); + switch (request) { + case VT_OPENQRY: { + fprintf(stderr, "OPEN\n"); + *(int*)data = 7; + break; + } + } + ret = 0; + } else if (fd == tty7fd) { + fprintf(stderr, "ioctl tty7 %d %lx %p\n", fd, request, data); + if (request == VT_GETSTATE) { + fprintf(stderr, "STATE\n"); + struct vt_stat* stat = data; + stat->v_active = 0; + } + + if ((request == VT_RELDISP && (long)data == 1) || + (request == VT_ACTIVATE && (long)data == 0)) { + fprintf(stderr, "Telling Chromium OS to regain control\n"); + system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService --type=method_call /org/chromium/LibCrosService org.chromium.LibCrosServiceInterface.TakeDisplayOwnership"); + } else if ((request == VT_RELDISP && (long)data == 2) || + (request == VT_ACTIVATE && (long)data == 7)) { + fprintf(stderr, "Telling Chromium OS to drop control\n"); + system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService --type=method_call /org/chromium/LibCrosService org.chromium.LibCrosServiceInterface.ReleaseDisplayOwnership"); + } + ret = 0; + } else { + if (request == EVIOCGRAB) { + fprintf(stderr, "ioctl GRAB %d %lx %p\n", fd, request, data); + /* Driver requested a grab: assume we have it already and report + * success */ + ret = 0; + } else { + ret = orig_ioctl(fd, request, data); + } + } + va_end(argp); + return ret; +} + +int open(const char *pathname, int flags, mode_t mode) { + if (!orig_open) preload_init(); + + fprintf(stderr, "open %s\n", pathname); + if (!strcmp(pathname, "/dev/tty0")) { + tty0fd = orig_open("/dev/null", flags, mode); + return tty0fd; + } else if (!strcmp(pathname, "/dev/tty7")) { + tty7fd = orig_open("/dev/null", flags, mode); + return tty7fd; + } else { + const char* event = "/dev/input/event"; + int fd = orig_open(pathname, flags, mode); + fprintf(stderr, "open %s %d\n", pathname, fd); + if (!strncmp(pathname, event, strlen(event))) { + fprintf(stderr, "GRAB\n"); + orig_ioctl(fd, EVIOCGRAB, (void *) 1); + } + return fd; + } +} + +int close(int fd) { + if (!orig_close) preload_init(); + + fprintf(stderr, "close %d\n", fd); + + if (fd == tty0fd) tty0fd = -1; + if (fd == tty7fd) tty7fd = -1; + return orig_close(fd); +} From 3a9a1f2aa0f26203fddd830809c3283fd982ab99 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 15 Jan 2015 07:57:22 -0800 Subject: [PATCH 251/334] Add support for Xorg on K1 (after 6689). Explicitly installing xorg on K1 is allowed (x11 target will use xephyr by default until 6689) so that people who know what they're doing can test things. --- targets/x11 | 6 +++++- targets/xorg | 22 +++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/targets/x11 b/targets/x11 index 4ddea7e79..32c26ba29 100644 --- a/targets/x11 +++ b/targets/x11 @@ -4,7 +4,11 @@ # found in the LICENSE file. if [ -f /sbin/frecon ]; then REQUIRES='xiwi' -elif [ "${ARCH#arm}" != "$ARCH" ]; then +elif grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ + ([ "${ARCH#arm}" = "$ARCH" ] && \ + awk -F= '/_RELEASE_VERSION=/ { exit int($2) < 6689 }' \ + '/etc/lsb-release'); then + # Xorg won't work on Samsung ARM devices or K1 on release less than 6689 REQUIRES='xephyr' else REQUIRES='xorg' diff --git a/targets/xorg b/targets/xorg index 78c10ad84..340aa9aa2 100644 --- a/targets/xorg +++ b/targets/xorg @@ -5,8 +5,8 @@ if [ "${TARGETNOINSTALL:-c}" = 'c' ]; then if [ -f /sbin/frecon ]; then error 1 'xorg target does not yet work with Freon.' - elif [ "${ARCH#arm}" != "$ARCH" ]; then - error 1 'xorg target does not work on ARM.' + elif grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo; then + error 1 'xorg target does not work on Samsung ARM devices.' fi fi REQUIRES='audio' @@ -23,13 +23,14 @@ XMETHOD="${XMETHOD:-xorg}" rm -f '/etc/crouton/xserverrc-x11' ltspackages='' -# On precise, install lts-trusty xorg server to support newer hardware if kernel -# version != 3.4 (lts-trusty mesa requires version >=3.6) -if release -eq precise && ! uname -r | grep -q "^3.4."; then +# On non-ARM precise, install lts-trusty xorg server to support newer hardware +# if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) +if [ "${ARCH#arm}" = "$ARCH" ] && release -eq precise \ + && ! uname -r | grep -q "^3.4."; then # We still install xorg later to pull in its dependencies - install --minimal xserver-xorg-lts-trusty libgl1-mesa-glx-lts-trusty \ - xserver-xorg-input-synaptics-lts-trusty ltspackages='-lts-trusty' + install --minimal "xserver-xorg$ltspackages" "libgl1-mesa-glx$ltspackages" \ + "xserver-xorg-input-synaptics$ltspackages" fi # On saucy onwards, if kernel version is 3.4, manually pin down old mesa @@ -100,8 +101,11 @@ END apt-get -y --force-yes --no-install-recommends dist-upgrade -f fi -install xorg dmz-cursor-theme \ - xserver-xorg-video-fbdev$ltspackages xserver-xorg-video-intel$ltspackages +install xorg dmz-cursor-theme xserver-xorg-video-fbdev$ltspackages +if [ "${ARCH#arm}" = "$ARCH" ]; then + install xserver-xorg-video-intel$ltspackages +fi + # Fix launching X11 from inside crosh (user doesn't own a TTY) sed -i 's/allowed_users=.*/allowed_users=anybody/' '/etc/X11/Xwrapper.config' From 52aaa436c6b74339609bd4ed175a2a7146673c94 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 20:07:54 -0800 Subject: [PATCH 252/334] Dedup makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d16b848d5..f73ebba2f 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ $(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ croutonfreon.so: src/freon.c Makefile - gcc -g -Wall -Werror -shared -fPIC -ldl src/freon.c -o croutonfreon.so + gcc $(CFLAGS) -shared -fPIC -ldl src/freon.c -o croutonfreon.so extension: $(EXTTARGET) From 2c1cd34140e11db38f2ff93c8d76223348ec7189 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 20:40:55 -0800 Subject: [PATCH 253/334] Compile and install croutonfreon.so --- installer/prepare.sh | 14 ++++++++++---- targets/xorg | 5 +++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/installer/prepare.sh b/installer/prepare.sh index 18d1f1de3..a8c06fde4 100755 --- a/installer/prepare.sh +++ b/installer/prepare.sh @@ -176,19 +176,25 @@ fixkeyboardmode() { # stdin to the specified output and strips it. Finally, removes whatever it # installed. This allows targets to provide on-demand binaries without # increasing the size of the chroot after install. -# $1: name; target is /usr/local/bin/crouton$1 +# $1: name; target is /usr/local/(bin|lib)/crouton$1[.so] # $2: linker flags, quoted together +# [$3]: if 'so', compiles as a shared object and installs it into lib # $3+: any package dependencies other than gcc and libc-dev, crouton-style. compile() { - local out="/usr/local/bin/crouton$1" linker="$2" - echo "Installing dependencies for $out..." 1>&2 + local out="/usr/local/bin/crouton$1" linker="$2" cflags='-xc -Os' + if [ "$3" = 'so' ]; then + out="/usr/local/lib/crouton$1.so" + cflags="$cflags -shared -fPIC" + shift 1 + fi shift 2 + echo "Installing dependencies for $out..." 1>&2 local pkgs="gcc libc6-dev $*" install --minimal --asdeps $pkgs &2 local tmp="`mktemp crouton.XXXXXX --tmpdir=/tmp`" addtrap "rm -f '$tmp'" - gcc -xc -Os - $linker -o "$tmp" + gcc $cflags - $linker -o "$tmp" /usr/bin/install -sDT "$tmp" "$out" } diff --git a/targets/xorg b/targets/xorg index 340aa9aa2..4c77b64dd 100644 --- a/targets/xorg +++ b/targets/xorg @@ -22,6 +22,11 @@ XMETHOD="${XMETHOD:-xorg}" # Migration from ambiguous XMETHOD rm -f '/etc/crouton/xserverrc-x11' +# On Freon, we need crazy xorg hacks +if [ ! -f "/sys/class/tty/tty0/active" ]; then + compile freon '-ldl' so +fi + ltspackages='' # On non-ARM precise, install lts-trusty xorg server to support newer hardware # if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) From 5d9d41ae9bff2f4ec24f03cc520c9285c5fbef2b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 21:54:47 -0800 Subject: [PATCH 254/334] Fix compile warnings and clean up dbus calls --- src/freon.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/freon.c b/src/freon.c index fb6b6038f..03c8ae9c3 100644 --- a/src/freon.c +++ b/src/freon.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014 The crouton Authors. All rights reserved. +/* Copyright (c) 2015 The crouton Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * @@ -19,6 +19,11 @@ #include #include +#define FREON_DBUS_METHOD_CALL(function) \ + system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService " \ + "--type=method_call --print-reply /org/chromium/LibCrosService " \ + "org.chromium.LibCrosServiceInterface." #function) + static int tty0fd = -1; static int tty7fd = -1; @@ -63,13 +68,14 @@ int ioctl(int fd, unsigned long int request, ...) { if ((request == VT_RELDISP && (long)data == 1) || (request == VT_ACTIVATE && (long)data == 0)) { fprintf(stderr, "Telling Chromium OS to regain control\n"); - system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService --type=method_call /org/chromium/LibCrosService org.chromium.LibCrosServiceInterface.TakeDisplayOwnership"); + ret = FREON_DBUS_METHOD_CALL(TakeDisplayOwnership); } else if ((request == VT_RELDISP && (long)data == 2) || (request == VT_ACTIVATE && (long)data == 7)) { fprintf(stderr, "Telling Chromium OS to drop control\n"); - system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService --type=method_call /org/chromium/LibCrosService org.chromium.LibCrosServiceInterface.ReleaseDisplayOwnership"); + ret = FREON_DBUS_METHOD_CALL(ReleaseDisplayOwnership); + } else { + ret = 0; } - ret = 0; } else { if (request == EVIOCGRAB) { fprintf(stderr, "ioctl GRAB %d %lx %p\n", fd, request, data); From 93f2408c8c38a8d12118984610f1c972a7f2a320 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 6 Feb 2015 20:13:33 -0800 Subject: [PATCH 255/334] Support Freon in xserverrc-xorg --- chroot-etc/xserverrc-x11-freon | 21 --------------------- chroot-etc/xserverrc-xorg | 11 ++++++++++- targets/post-common | 2 +- targets/x11 | 4 +--- targets/xorg | 4 +--- test/tests/35-xorg | 4 +--- 6 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 chroot-etc/xserverrc-x11-freon diff --git a/chroot-etc/xserverrc-x11-freon b/chroot-etc/xserverrc-x11-freon deleted file mode 100644 index 0335f356b..000000000 --- a/chroot-etc/xserverrc-x11-freon +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -e -# Copyright (c) 2014 The crouton Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Disable framebuffer compression when displaying natively. -# TODO: test FBC with SNA enabled on Haswell to regain power savings -fbc='/sys/module/i915/parameters/i915_enable_fbc' -if [ -w "$fbc" ]; then - echo 0 > "$fbc" -fi - -XMETHOD='x11' -XARGS='-nolisten tcp' -XARGS="$XARGS -logfile /tmp/Xorg.1234.log" -if [ -f /etc/crouton/xserverrc-local ]; then - . /etc/crouton/xserverrc-local -fi - -export LD_PRELOAD=/usr/local/lib/croutonfreon.so -exec /usr/bin/Xorg $XARGS "$@" diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index d5fb6f234..c516ca5c4 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -16,4 +16,13 @@ if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi -exec /usr/bin/X $XARGS "$@" +X=/usr/bin/X + +# Freon needs the preload hack for X to coexist +if [ ! -f "/sys/class/tty/tty0/active" ]; then + X=/usr/bin/Xorg + XARGS="-logfile /tmp/Xorg.$$.log $XARGS" + export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" +fi + +exec "$X" $XARGS "$@" diff --git a/targets/post-common b/targets/post-common index 09f261b10..a9a8dfc15 100644 --- a/targets/post-common +++ b/targets/post-common @@ -35,7 +35,7 @@ if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then fi # Add the primary user -groups='audio,video,sudo,plugdev' +groups='audio,input,video,sudo,plugdev' if ! grep -q ':1000:' /etc/passwd; then while ! echo "$username" | grep -q '^[a-z][-a-z0-9_]*$'; do if [ -n "$username" ]; then diff --git a/targets/x11 b/targets/x11 index 32c26ba29..da76368da 100644 --- a/targets/x11 +++ b/targets/x11 @@ -2,9 +2,7 @@ # Copyright (c) 2014 The crouton Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -if [ -f /sbin/frecon ]; then - REQUIRES='xiwi' -elif grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ +if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ ([ "${ARCH#arm}" = "$ARCH" ] && \ awk -F= '/_RELEASE_VERSION=/ { exit int($2) < 6689 }' \ '/etc/lsb-release'); then diff --git a/targets/xorg b/targets/xorg index 4c77b64dd..7785887d5 100644 --- a/targets/xorg +++ b/targets/xorg @@ -3,9 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. if [ "${TARGETNOINSTALL:-c}" = 'c' ]; then - if [ -f /sbin/frecon ]; then - error 1 'xorg target does not yet work with Freon.' - elif grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo; then + if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo; then error 1 'xorg target does not work on Samsung ARM devices.' fi fi diff --git a/test/tests/35-xorg b/test/tests/35-xorg index 28e7980ac..33afbe7c0 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -5,9 +5,7 @@ # All supported releases should be able to launch an xorg server if [ -z "$release" ]; then - if [ -f /sbin/frecon ]; then - log "xorg is not available on Freon. Skipping test." - elif uname -m | grep -q 'arm'; then + if uname -m | grep -q 'arm'; then log "xorg is not available on ARM. Skipping test." else echo "all" From c1515890999c2c41b0d84fd496cf18b1c721f0e2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 8 Feb 2015 22:12:52 -0800 Subject: [PATCH 256/334] Add display lock file to coordinate active display Also, silence tracing --- Makefile | 2 +- src/freon.c | 113 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index f73ebba2f..65e954941 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ $(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ croutonfreon.so: src/freon.c Makefile - gcc $(CFLAGS) -shared -fPIC -ldl src/freon.c -o croutonfreon.so + gcc $(CFLAGS) -shared -fPIC src/freon.c -ldl -o croutonfreon.so extension: $(EXTTARGET) diff --git a/src/freon.c b/src/freon.c index 03c8ae9c3..bd4fc38a7 100644 --- a/src/freon.c +++ b/src/freon.c @@ -11,30 +11,81 @@ #define _GNU_SOURCE #include #include +#include #include #include #include #include #include +#include #include #include +#define LOCK_FILE_DIR "/tmp/crouton-lock" +#define DISPLAY_LOCK_FILE LOCK_FILE_DIR "/display" #define FREON_DBUS_METHOD_CALL(function) \ system("host-dbus dbus-send --system --dest=org.chromium.LibCrosService " \ "--type=method_call --print-reply /org/chromium/LibCrosService " \ "org.chromium.LibCrosServiceInterface." #function) +// #define TRACE(...) fprintf(stderr, __VA_ARGS__) +#define TRACE(...) +#define ERROR(...) fprintf(stderr, __VA_ARGS__) + static int tty0fd = -1; static int tty7fd = -1; +static int lockfd = -1; static int (*orig_ioctl)(int d, int request, void* data); static int (*orig_open)(const char *pathname, int flags, mode_t mode); static int (*orig_close)(int fd); static void preload_init() { - orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); - orig_open = dlsym(RTLD_NEXT, "open"); - orig_close = dlsym(RTLD_NEXT, "close"); + orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); + orig_open = dlsym(RTLD_NEXT, "open"); + orig_close = dlsym(RTLD_NEXT, "close"); +} + +static int set_display_lock(unsigned int pid) { + if (lockfd == -1) { + if (!pid) { + ERROR("No display lock to release.\n"); + return 0; + } + (void) mkdir(LOCK_FILE_DIR, 0777); + lockfd = orig_open(DISPLAY_LOCK_FILE, O_CREAT | O_WRONLY, 0666); + if (lockfd == -1) { + ERROR("Unable to open display lock file.\n"); + return -1; + } + if (flock(lockfd, LOCK_EX) == -1) { + ERROR("Unable to lock display lock file.\n"); + return -1; + } + } + if (ftruncate(lockfd, 0) == -1) { + ERROR("Unable to truncate display lock file.\n"); + return -1; + } + char buf[11]; + int len; + if ((len = snprintf(buf, sizeof(buf), "%d\n", pid)) < 0) { + ERROR("pid sprintf failed.\n"); + return -1; + } + if (write(lockfd, buf, len) == -1) { + ERROR("Unable to write to display lock file.\n"); + return -1; + } + if (!pid) { + int ret = orig_close(lockfd); + lockfd = -1; + if (ret == -1) { + ERROR("Failure when closing display lock file.\n"); + } + return ret; + } + return 0; } int ioctl(int fd, unsigned long int request, ...) { @@ -42,43 +93,48 @@ int ioctl(int fd, unsigned long int request, ...) { int ret = 0; va_list argp; - va_start(argp, request); - void* data = va_arg(argp, void*); if (fd == tty0fd) { - fprintf(stderr, "ioctl tty0 %d %lx %p\n", fd, request, data); - switch (request) { - case VT_OPENQRY: { - fprintf(stderr, "OPEN\n"); + TRACE("ioctl tty0 %d %lx %p\n", fd, request, data); + if (request == VT_OPENQRY) { + TRACE("OPEN\n"); *(int*)data = 7; - break; - } } ret = 0; } else if (fd == tty7fd) { - fprintf(stderr, "ioctl tty7 %d %lx %p\n", fd, request, data); + TRACE("ioctl tty7 %d %lx %p\n", fd, request, data); if (request == VT_GETSTATE) { - fprintf(stderr, "STATE\n"); + TRACE("STATE\n"); struct vt_stat* stat = data; stat->v_active = 0; } if ((request == VT_RELDISP && (long)data == 1) || (request == VT_ACTIVATE && (long)data == 0)) { - fprintf(stderr, "Telling Chromium OS to regain control\n"); - ret = FREON_DBUS_METHOD_CALL(TakeDisplayOwnership); + if (lockfd != -1) { + TRACE("Telling Chromium OS to regain control\n"); + ret = FREON_DBUS_METHOD_CALL(TakeDisplayOwnership); + if (set_display_lock(0)) { + ERROR("Failed to release display lock\n"); + } + } } else if ((request == VT_RELDISP && (long)data == 2) || (request == VT_ACTIVATE && (long)data == 7)) { - fprintf(stderr, "Telling Chromium OS to drop control\n"); - ret = FREON_DBUS_METHOD_CALL(ReleaseDisplayOwnership); + if (!set_display_lock(getpid())) { + TRACE("Telling Chromium OS to drop control\n"); + ret = FREON_DBUS_METHOD_CALL(ReleaseDisplayOwnership); + } else { + ERROR("Unable to claim display lock\n"); + ret = -1; + } } else { ret = 0; } } else { if (request == EVIOCGRAB) { - fprintf(stderr, "ioctl GRAB %d %lx %p\n", fd, request, data); + TRACE("ioctl GRAB %d %lx %p\n", fd, request, data); /* Driver requested a grab: assume we have it already and report * success */ ret = 0; @@ -90,10 +146,14 @@ int ioctl(int fd, unsigned long int request, ...) { return ret; } -int open(const char *pathname, int flags, mode_t mode) { +int open(const char *pathname, int flags, ...) { if (!orig_open) preload_init(); - fprintf(stderr, "open %s\n", pathname); + va_list argp; + va_start(argp, flags); + int mode = va_arg(argp, int); + + TRACE("open %s\n", pathname); if (!strcmp(pathname, "/dev/tty0")) { tty0fd = orig_open("/dev/null", flags, mode); return tty0fd; @@ -103,9 +163,9 @@ int open(const char *pathname, int flags, mode_t mode) { } else { const char* event = "/dev/input/event"; int fd = orig_open(pathname, flags, mode); - fprintf(stderr, "open %s %d\n", pathname, fd); + TRACE("open %s %d\n", pathname, fd); if (!strncmp(pathname, event, strlen(event))) { - fprintf(stderr, "GRAB\n"); + TRACE("GRAB\n"); orig_ioctl(fd, EVIOCGRAB, (void *) 1); } return fd; @@ -115,9 +175,12 @@ int open(const char *pathname, int flags, mode_t mode) { int close(int fd) { if (!orig_close) preload_init(); - fprintf(stderr, "close %d\n", fd); + TRACE("close %d\n", fd); - if (fd == tty0fd) tty0fd = -1; - if (fd == tty7fd) tty7fd = -1; + if (fd == tty0fd) { + tty0fd = -1; + } else if (fd == tty7fd) { + tty7fd = -1; + } return orig_close(fd); } From 6b77a8a9cb3b4546b3d7f36e724bc58203a42050 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 9 Feb 2015 00:30:12 -0800 Subject: [PATCH 257/334] Add freon support to croutoncycle --- chroot-bin/croutoncycle | 57 ++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 1d1c152c1..e1f8c518a 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -76,9 +76,6 @@ export XAUTHORITY='' # Set to y if there is any xiwi instance running xiwiactive='' -# Set to y if aura is running without a X server -noaurax='' - # Prepare display list for easier looping displist='' for disp in /tmp/.X*-lock; do @@ -97,27 +94,30 @@ for disp in /tmp/.X*-lock; do fi done -# host-x11 fails if no Chromium OS server exist -if host-x11 true 2>/dev/null; then +# Set to the freon display owner if freon is used +freonowner='' +if [ ! -f "/sys/class/tty/tty0/active" ]; then + if [ -f "/tmp/crouton-lock/display" ]; then + read freonowner < "/tmp/crouton-lock/display" + fi + freonowner="${freonowner:-0}" + winlist="aura*" + aurawin="aura" + tty='' +else # List windows on :0. Includes aura winlist="`host-x11 croutonwmtools list nim | \ sort | awk '{ printf $NF " " }'`" aurawin="`host-x11 croutonwmtools list ni | \ awk '$1 == "aura_root_0" { print $NF; exit }'`" tty="`cat '/sys/class/tty/tty0/active'`" -else - # No X11 server - noaurax='y' - winlist="aura*" - aurawin="aura" - tty="tty1" fi # Combine the two fulllist="$winlist$displist" fulllist="${fulllist% }" -if [ "$tty" = 'tty1' ]; then +if [ "$freonowner" = 0 -o "$tty" = 'tty1' ]; then # Either in Chromium OS, xephyr/xiwi chroot, or window. # Active window is starred. for disp in $winlist; do @@ -133,7 +133,7 @@ if [ "$tty" = 'tty1' ]; then break fi done -else +elif [ -z "$freonowner" ]; then # Poll the displays to figure out which one owns this VT curdisp="$tty" for disp in $displist; do @@ -143,6 +143,14 @@ else break fi done +else + # Match the pid to the current freon owner + for lockfile in /tmp/.X*-lock; do + if grep -q "\\<$freonowner$" "$lockfile"; then + curdisp="${lockfile#*X}" + curdisp=":${curdisp%%-*}" + fi + done fi # List the displays if requested @@ -153,7 +161,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then '/var/host/lsb-release'`" fi ( - if [ -z "$noaurax" ]; then + if [ -z "$freonowner" ]; then host-x11 croutonwmtools list nim else echo "aura_root_0 aura*" @@ -261,7 +269,7 @@ if [ "$destdisp" = "$curdisp" -a -z "$force" ]; then fi # Make sure tap-to-click is enabled -if [ -z "$noaurax" ] && hash xinput 2>/dev/null; then +if [ -z "$freonowner" ] && hash xinput 2>/dev/null; then for id in `host-x11 xinput --list --id-only`; do host-x11 xinput set-prop "$id" 'Tap Paused' 0 2>/dev/null || true done @@ -269,7 +277,7 @@ fi # Determine if the target display is on a VT if [ "${destdisp#:}" = "$destdisp" ]; then - if [ -z "$noaurax" ]; then + if [ -z "$freonowner" ]; then eval "`host-x11`" # Raise the right window after chvting, so that it can update if [ "$tty" != 'tty1' ]; then @@ -277,6 +285,8 @@ if [ "${destdisp#:}" = "$destdisp" ]; then sleep .1 fi croutonwmtools raise "${destdisp%"*"}" + elif [ "${freonowner:-0}" != 0 ]; then + kill -USR1 "$freonowner" fi if [ -n "$xiwiactive" -a "${destdisp%"*"}" = "$aurawin" ]; then @@ -288,15 +298,17 @@ if [ "${destdisp#:}" = "$destdisp" ]; then else export DISPLAY="$destdisp" if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiwi"$'; then - if [ -z "$noaurax" -a "$tty" != 'tty1' ]; then + if [ -z "$freonowner" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 + elif [ "${freonowner:-0}" != 0 ]; then + kill -USR1 "$freonowner" fi STATUS="`echo -n "X${destdisp}" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi - else + elif [ -z "$freonowner" ]; then dest="`xprop -root 'XFree86_VT' 2>/dev/null`" dest="${dest##* }" if [ "${dest#[1-9]}" = "$dest" ]; then @@ -307,6 +319,15 @@ else # display will be stuck on the old vt. sudo -n chvt 2 sudo -n chvt "$dest" + else + dest="/tmp/.X${destdisp#:}-lock" + if [ -f "$dest" ]; then + # Trigger the target before releasing the current owner + kill -USR1 `cat /tmp/.X${destdisp#:}-lock` + fi + if [ "${freonowner:-0}" != 0 ]; then + kill -USR1 "$freonowner" + fi fi fi From bbc34f48943e2bfacd3f19a1aaf23c5d2d380c9d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 9 Feb 2015 17:05:16 -0800 Subject: [PATCH 258/334] Use SNA+TearFree if FBC is present. Saves power on Haswell and works better with Freon. Fixes #520 and hopefully doesn't break any platforms. --- chroot-bin/croutonxinitrc-wrapper | 9 --------- chroot-etc/xorg-intel-sna.conf | 12 ++++++++++++ chroot-etc/xserverrc-xorg | 7 ------- host-bin/enter-chroot | 8 -------- targets/xorg | 12 +++++++++++- 5 files changed, 23 insertions(+), 25 deletions(-) create mode 100644 chroot-etc/xorg-intel-sna.conf diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index d4eb93b63..b2a27cd7a 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -180,13 +180,4 @@ fi echo "Running exit commands..." 1>&2 -# Restore framebuffer compression if there are no other non-Chromium X servers -fbc='/sys/module/i915/parameters/i915_enable_fbc' -if [ -w "$fbc" ]; then - # There is at least 2 servers running (the current one and Chromium OS) - if [ "`ps -CX -CXorg -CXephyr -opid= | wc -l`" -le 2 ]; then - echo 1 > "$fbc" - fi -fi - exit "$ret" diff --git a/chroot-etc/xorg-intel-sna.conf b/chroot-etc/xorg-intel-sna.conf new file mode 100644 index 000000000..3f2b369b9 --- /dev/null +++ b/chroot-etc/xorg-intel-sna.conf @@ -0,0 +1,12 @@ +# Copyright (c) 2015 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# On Intel platforms with FBC enabled, in order to see anything we need to use +# the SNA driver with the TearFree option. +Section "Device" + Identifier "Intel Graphics SNA+TearFree" + Driver "intel" + Option "AccelMethod" "sna" + Option "TearFree" "true" +EndSection diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index c516ca5c4..679c644b6 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -3,13 +3,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Disable framebuffer compression when displaying natively. -# TODO: test FBC with SNA enabled on Haswell to regain power savings -fbc='/sys/module/i915/parameters/i915_enable_fbc' -if [ -w "$fbc" ]; then - echo 0 > "$fbc" -fi - export XMETHOD='xorg' XARGS='-nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 74ab05f95..2b5662cc8 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -146,14 +146,6 @@ fi # Avoid kernel panics due to slow I/O disablehungtask -# Enable control of framebuffer compression (if used) by video users -for fbc in '/sys/kernel/debug/dri/0/'*fbc*; do :; done -if [ -f "$fbc" ] && ! grep -q 'disabled per module param' "$fbc"; then - fbc='/sys/module/i915/parameters/i915_enable_fbc' - chgrp video "$fbc" - chmod g+w "$fbc" -fi - # Allow X server running as normal user to set/drop DRM master drm_relax_file="/sys/kernel/debug/dri/drm_master_relax" if [ -f "$drm_relax_file" ]; then diff --git a/targets/xorg b/targets/xorg index 7785887d5..e902e85e0 100644 --- a/targets/xorg +++ b/targets/xorg @@ -11,7 +11,7 @@ REQUIRES='audio' PROVIDES='x11' DESCRIPTION='X.Org X11 backend. Enables GPU acceleration on supported platforms.' CHROOTBIN='croutoncycle croutonxinitrc-wrapper setres xinit' -CHROOTETC='xbindkeysrc.scm xserverrc xserverrc-xorg xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xorg-intel-sna.conf xserverrc xserverrc-xorg xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: @@ -109,6 +109,16 @@ if [ "${ARCH#arm}" = "$ARCH" ]; then install xserver-xorg-video-intel$ltspackages fi +# This is a system with framebuffer compression, so we need SNA+tearfree +xorgconf='/usr/share/X11/xorg.conf.d/20-crouton-intel-sna.conf' +if grep -q 1 '/sys/module/i915/parameters/i915_enable_fbc' 2>/dev/null; then + mkdir -p "${xorgconf%/*}" + ln -sfT /etc/crouton/xorg-intel-sna.conf "$xorgconf" +else + # In case this got moved to a different system, delete the config + rm -f "$xorgconf" +fi + # Fix launching X11 from inside crosh (user doesn't own a TTY) sed -i 's/allowed_users=.*/allowed_users=anybody/' '/etc/X11/Xwrapper.config' From e78267c4e54598ccb1febbba284416dcaa89bfb2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 11 Feb 2015 16:41:18 -0800 Subject: [PATCH 259/334] Fix target display switching and xiwi switching with Xephyr --- chroot-bin/croutoncycle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index e1f8c518a..18847ea99 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -224,6 +224,12 @@ fi if [ -n "${cmd#[pn]}" ]; then if [ "${cmd#:}" != "$cmd" ]; then destdisp="$cmd" + # Resolve a xephyr display into its ID + if DISPLAY="$destdisp" xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ + | grep -q '= "xephyr"$'; then + destdisp="`host-x11 croutonwmtools list ni | \ + awk "/^Xephyr on $destdisp\.0/"' { print $NF; exit }'`" + fi else i=0 destdisp='' @@ -304,6 +310,9 @@ else elif [ "${freonowner:-0}" != 0 ]; then kill -USR1 "$freonowner" fi + if [ -z "$freonowner" ]; then + host-x11 croutonwmtools raise "$aurawin" + fi STATUS="`echo -n "X${destdisp}" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" From 63faaa51a4d9478dcc311f35629c4d4e12387b16 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 13 Feb 2015 18:42:25 -0800 Subject: [PATCH 260/334] Add triggerhappy as the system-wide keyboard daemon --- chroot-bin/croutonxinitrc-wrapper | 47 +++++++++++++++++++++++-------- chroot-etc/xbindkeysrc.scm | 12 ++++++-- targets/x11-common | 4 +-- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index b2a27cd7a..9b1d3db44 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -78,7 +78,42 @@ if [ "$XMETHOD" = 'xephyr' ]; then croutonwheel $CROUTON_WHEEL_PARAMS & fi -# xbindkeys and other input-related apps are not needed for kiwi +# Launch system-wide triggerhappy if it isn't running +mkdir -m 775 -p /tmp/crouton-lock +{ + # Only let one instance *really* run at a time + flock 3 + cmdprev='/usr/local/bin/croutoncycle p' + cmdnext='/usr/local/bin/croutoncycle n' + echo "# +# Ctrl+Alt+Shift+F1 = previous. Require full key release to avoid stuck keys. +KEY_F1+KEY_LEFTSHIFT+KEY_LEFTCTRL+KEY_LEFTALT@ 1 @p +KEY_F1@p 0 $cmdprev +KEY_LEFTSHIFT@p 0 $cmdprev +KEY_LEFTCTRL@p 0 $cmdprev +KEY_LEFTALT@p 0 $cmdprev +KEY_F1@p 0 @ +KEY_LEFTSHIFT@p 0 @ +KEY_LEFTCTRL@p 0 @ +KEY_LEFTALT@p 0 @ +# +# Ctrl+Alt+Shift+F2 = next Require full key release to avoid stuck keys. +KEY_F2+KEY_LEFTSHIFT+KEY_LEFTCTRL+KEY_LEFTALT@ 1 @n +KEY_F2@n 0 $cmdnext +KEY_LEFTSHIFT@n 0 $cmdnext +KEY_LEFTCTRL@n 0 $cmdnext +KEY_LEFTALT@n 0 $cmdnext +KEY_F2@n 0 @ +KEY_LEFTSHIFT@n 0 @ +KEY_LEFTCTRL@n 0 @ +KEY_LEFTALT@n 0 @ +#" | /usr/sbin/thd --triggers /dev/stdin \ + --socket /tmp/crouton-lock/thd.socket /dev/input/event* >/dev/null & + trap "kill '$!' 2>/dev/null" HUP INT TERM + wait "$!" || true +} 3>/tmp/crouton-lock/xbindkeys & + +# Input-related stuff is not needed for kiwi if [ "$XMETHOD" != "xiwi" ]; then # Apply the Chromebook keyboard map if installed. if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then @@ -88,16 +123,6 @@ if [ "$XMETHOD" != "xiwi" ]; then # Launch key binding daemon xbindkeys -fg /etc/crouton/xbindkeysrc.scm - # Launch xbindkeys for the Chromium OS X server if it isn't running - mkdir -m 775 -p /tmp/crouton-lock - { - # Only let one instance *really* run at a time - flock 3 - XMETHOD='' host-x11 xbindkeys -n -fg /etc/crouton/xbindkeysrc.scm & - trap "kill '$!' 2>/dev/null" HUP INT TERM - wait "$!" || true - } 3>/tmp/crouton-lock/xbindkeys & - # Launch touchegg if it is requested. toucheggconf='/etc/touchegg.conf' if [ -f "$toucheggconf" ]; then diff --git a/chroot-etc/xbindkeysrc.scm b/chroot-etc/xbindkeysrc.scm index ad06b55f1..1fa69ff4b 100644 --- a/chroot-etc/xbindkeysrc.scm +++ b/chroot-etc/xbindkeysrc.scm @@ -4,9 +4,15 @@ ;; Run xbindkeys -dg for some example configuration file with explanation -; Cycle chroots -(xbindkey '(control shift alt F1) "xte 'keyup F1'; croutoncycle prev") -(xbindkey '(control shift alt F2) "xte 'keyup F2'; croutoncycle next") +; Cycle chroots. On most systems, this is handled by the triggerhappy daemon. +; On freon, we have to do it ourselves since we currently grab the event device. +(if (access? "/sys/class/tty/tty0/active" F_OK) (begin + (xbindkey '(control shift alt F1) "") + (xbindkey '(control shift alt F2) "") +) (begin + (xbindkey '(control shift alt F1) "xte 'keyup F1'; croutoncycle prev") + (xbindkey '(control shift alt F2) "xte 'keyup F2'; croutoncycle next") +)) ; Extra bindings that must only be activated in chroot X11/Xephyr (if (not (string-null? (getenv "XMETHOD"))) diff --git a/targets/x11-common b/targets/x11-common index d646c3189..ee0098ce7 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -20,8 +20,8 @@ install --minimal dbus xdg-utils ln -sf croutonpowerd /usr/local/bin/gnome-screensaver-command ln -sf croutonpowerd /usr/local/bin/xscreensaver-command -# Install xbindkeys and xautomation for key shortcuts and kbd for chvt -install --minimal xbindkeys xautomation kbd +# Install triggerhappy, xbindkeys and xautomation for shortcuts; kbd for chvt +install --minimal triggerhappy xbindkeys xautomation kbd # Allow users to run sudo chvt without password, so we don't need to run # croutoncycle as root echo '%sudo ALL = NOPASSWD:/bin/chvt' > /etc/sudoers.d/chvt From 2646a9f4e3009152ffddc6696315abba4ab0e444 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 11 Feb 2015 18:35:41 -0800 Subject: [PATCH 261/334] Disallow launching Xorg from frecon --- chroot-etc/xserverrc-xorg | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index 679c644b6..145953363 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -11,8 +11,19 @@ fi X=/usr/bin/X -# Freon needs the preload hack for X to coexist +# Handle Freon systems if [ ! -f "/sys/class/tty/tty0/active" ]; then + # We won't be able to launch properly if running from frecon + ppid="$$" + while [ -n "$ppid" -a "$ppid" -ne 1 ]; do + ppid="`ps -p "$ppid" -o 'ppid=' 2>/dev/null | sed 's/ //g'`" + if ps -p "$ppid" -o 'comm=' | grep -q '^frecon$'; then + echo 'Xorg X11 servers cannot be launched from Frecon.' 1>&2 + echo 'Return to Chromium OS and use crosh to launch X.' 1>&2 + exit 2 + fi + done + # Freon necessitates the preload hack for X to coexist X=/usr/bin/Xorg XARGS="-logfile /tmp/Xorg.$$.log $XARGS" export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" From f11169f4cdb4f6502ef8d023713543b937e80f71 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 14 Feb 2015 18:09:13 -0800 Subject: [PATCH 262/334] Kill frecon on launching xorg to workaround crbug.com/457075 --- chroot-etc/xserverrc-xorg | 5 +++++ host-bin/enter-chroot | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index 145953363..36acb66d6 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -23,6 +23,11 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then exit 2 fi done + # Use the daemon to kill frecon. + # FIXME: remove when crbug.com/457075 is resolved + if [ -w '/tmp/crouton-lock/frecon' ]; then + echo >> '/tmp/crouton-lock/frecon' + fi # Freon necessitates the preload hack for X to coexist X=/usr/bin/Xorg XARGS="-logfile /tmp/Xorg.$$.log $XARGS" diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index 2b5662cc8..d16419ff1 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -646,6 +646,27 @@ if [ ! "$NOLOGIN" = 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then # systemd-logind doesn't fork chrootcmd "/lib/systemd/systemd-logind >/dev/null 2>&1 /dev/null 2>&1 > '$CROUTONLOCKDIR/frecon') & :" + fi fi # Start the chroot and any specified command From 89ebac8dad743e5a174babb05c92f0c67238d755 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 19 Feb 2015 18:46:25 -0800 Subject: [PATCH 263/334] Replace triggerhappy with croutontriggerd. Doesn't pull in any dependencies. Monitors for new keyboards. Can be used for other event-file-based triggering in the future. --- chroot-bin/croutontriggerd | 88 +++++++++++++++++++++++++++++++ chroot-bin/croutonxinitrc-wrapper | 38 ++----------- targets/x11-common | 4 +- targets/xephyr | 2 +- targets/xiwi | 2 +- targets/xorg | 2 +- 6 files changed, 96 insertions(+), 40 deletions(-) create mode 100755 chroot-bin/croutontriggerd diff --git a/chroot-bin/croutontriggerd b/chroot-bin/croutontriggerd new file mode 100755 index 000000000..811f7092b --- /dev/null +++ b/chroot-bin/croutontriggerd @@ -0,0 +1,88 @@ +#!/bin/sh -e +# Copyright (c) 2015 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Monitors keyboard events for the crouton switch command. + +. "`dirname "$0"`/../installer/functions" + +# hexdump output format variables +SECONDS_LO='$1' +SECONDS_HI='$2' +USECONDS_LO='$3' +USECONDS_HI='$4' +TYPE='$5' +KEY='$6' +STATE='$7' + +# constants +TYPE_EV_KEY=1 +STATE_DOWN=1 +STATE_UP=0 +KEY_LEFTCTRL=29 +KEY_LEFTALT=56 +KEY_LEFTSHIFT=42 +KEY_F1=59 +KEY_F2=60 + +EVENT_DEV_POLL=15 + +# Only one at a time. xbindkeys lockfile is for legacy compatibility +mkdir -m 775 -p "$CROUTONLOCKDIR" +exec 3>>"$CROUTONLOCKDIR/xbindkeys" +if ! flock -n 3; then + echo "Another instance of ${0##*/} running, waiting..." + flock 3 +fi + +# Reset event variables to handle strange environments +unset `set | grep -o '^event[0-9]*'` 2>/dev/null || true + +# Poll for new event files and dump the output +while :; do + # Clean up old hexdumps and start new ones + for event in `set | grep -o '^event[0-9]*'` /dev/input/event*; do + # Check if the event file is already monitored + eval "pid=\"\${${event##*/}:-0}\"" + if [ "$pid" != 0 ]; then + # Check if it's still running + if kill -0 "$pid" 2>/dev/null; then + continue + fi + wait "$pid" || true + fi + # Clean up old variables + if [ "${event#/}" = "$event" ]; then + unset "$event" + else + # Read in the event files and split into input_event fields + stdbuf -oL hexdump -e '4/4 "%u " " " 2/2 "%u " " " 1/4 "%u " "\n"' \ + "$event" & + eval "${event##*/}='$!'" + fi + done + # Avoid picking up the event variable + unset event + # Kill all event daemons + pids="`set | sed -n 's/^event[0-9]*=.\(.*\).$/\1/p' | tr '\n' ' '`" + settrap "kill $pids 2>/dev/null;" + # Wait for next poll + sleep "$EVENT_DEV_POLL" +done | unbuffered_awk " + function update() { + if (!cmd && c && s && a && p) { + cmd = \"p\" + } else if (!cmd && c && s && a && n) { + cmd = \"n\" + } else if (cmd && !c && !s && !a && !p && !n) { + system(\"/usr/local/bin/croutoncycle \" cmd) + cmd = \"\" + } + } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTCTRL { c = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTSHIFT { s = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTALT { a = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F1 { p = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F2 { n = $STATE; update() } +" diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 9b1d3db44..88cc4c1c6 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -78,40 +78,8 @@ if [ "$XMETHOD" = 'xephyr' ]; then croutonwheel $CROUTON_WHEEL_PARAMS & fi -# Launch system-wide triggerhappy if it isn't running -mkdir -m 775 -p /tmp/crouton-lock -{ - # Only let one instance *really* run at a time - flock 3 - cmdprev='/usr/local/bin/croutoncycle p' - cmdnext='/usr/local/bin/croutoncycle n' - echo "# -# Ctrl+Alt+Shift+F1 = previous. Require full key release to avoid stuck keys. -KEY_F1+KEY_LEFTSHIFT+KEY_LEFTCTRL+KEY_LEFTALT@ 1 @p -KEY_F1@p 0 $cmdprev -KEY_LEFTSHIFT@p 0 $cmdprev -KEY_LEFTCTRL@p 0 $cmdprev -KEY_LEFTALT@p 0 $cmdprev -KEY_F1@p 0 @ -KEY_LEFTSHIFT@p 0 @ -KEY_LEFTCTRL@p 0 @ -KEY_LEFTALT@p 0 @ -# -# Ctrl+Alt+Shift+F2 = next Require full key release to avoid stuck keys. -KEY_F2+KEY_LEFTSHIFT+KEY_LEFTCTRL+KEY_LEFTALT@ 1 @n -KEY_F2@n 0 $cmdnext -KEY_LEFTSHIFT@n 0 $cmdnext -KEY_LEFTCTRL@n 0 $cmdnext -KEY_LEFTALT@n 0 $cmdnext -KEY_F2@n 0 @ -KEY_LEFTSHIFT@n 0 @ -KEY_LEFTCTRL@n 0 @ -KEY_LEFTALT@n 0 @ -#" | /usr/sbin/thd --triggers /dev/stdin \ - --socket /tmp/crouton-lock/thd.socket /dev/input/event* >/dev/null & - trap "kill '$!' 2>/dev/null" HUP INT TERM - wait "$!" || true -} 3>/tmp/crouton-lock/xbindkeys & +# Launch system-wide trigger daemon +croutontriggerd & # Input-related stuff is not needed for kiwi if [ "$XMETHOD" != "xiwi" ]; then @@ -120,7 +88,7 @@ if [ "$XMETHOD" != "xiwi" ]; then setxkbmap -model chromebook fi - # Launch key binding daemon + # Launch X-server-local key binding daemon xbindkeys -fg /etc/crouton/xbindkeysrc.scm # Launch touchegg if it is requested. diff --git a/targets/x11-common b/targets/x11-common index ee0098ce7..b05b4193f 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -20,8 +20,8 @@ install --minimal dbus xdg-utils ln -sf croutonpowerd /usr/local/bin/gnome-screensaver-command ln -sf croutonpowerd /usr/local/bin/xscreensaver-command -# Install triggerhappy, xbindkeys and xautomation for shortcuts; kbd for chvt -install --minimal triggerhappy xbindkeys xautomation kbd +# Install xbindkeys and xautomation for shortcuts; kbd for chvt +install --minimal xbindkeys xautomation kbd # Allow users to run sudo chvt without password, so we don't need to run # croutoncycle as root echo '%sudo ALL = NOPASSWD:/bin/chvt' > /etc/sudoers.d/chvt diff --git a/targets/xephyr b/targets/xephyr index 3a8b18af6..3fbfbf87b 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -8,7 +8,7 @@ fi REQUIRES='audio' PROVIDES='x11' DESCRIPTION='Nested X11 backend. Improves compatibility but lacks GPU accel.' -CHROOTBIN='croutoncycle croutonwheel croutonxinitrc-wrapper xinit' +CHROOTBIN='croutoncycle croutontriggerd croutonwheel croutonxinitrc-wrapper xinit' CHROOTETC='xbindkeysrc.scm xserverrc xserverrc-xephyr xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xiwi b/targets/xiwi index 8dbc61e67..ea250fa22 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -5,7 +5,7 @@ REQUIRES='audio extension' PROVIDES='x11' DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' -CHROOTBIN='croutoncycle croutonfindnacl croutonxinitrc-wrapper setres xinit' +CHROOTBIN='croutoncycle croutonfindnacl croutontriggerd croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" diff --git a/targets/xorg b/targets/xorg index e902e85e0..d9d169b95 100644 --- a/targets/xorg +++ b/targets/xorg @@ -10,7 +10,7 @@ fi REQUIRES='audio' PROVIDES='x11' DESCRIPTION='X.Org X11 backend. Enables GPU acceleration on supported platforms.' -CHROOTBIN='croutoncycle croutonxinitrc-wrapper setres xinit' +CHROOTBIN='croutoncycle croutontriggerd croutonxinitrc-wrapper setres xinit' CHROOTETC='xbindkeysrc.scm xorg-intel-sna.conf xserverrc xserverrc-xorg xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" From 5559e432db980f6d6ae4fa7bbdf12442af67c9f9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 19 Feb 2015 18:53:56 -0800 Subject: [PATCH 264/334] Style improvements. --- chroot-bin/croutoncycle | 2 +- host-bin/enter-chroot | 2 +- installer/prepare.sh | 4 +++- src/freon.c | 18 ++++++++++++------ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 18847ea99..d8b100e51 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -98,7 +98,7 @@ done freonowner='' if [ ! -f "/sys/class/tty/tty0/active" ]; then if [ -f "/tmp/crouton-lock/display" ]; then - read freonowner < "/tmp/crouton-lock/display" + read -r freonowner < "/tmp/crouton-lock/display" fi freonowner="${freonowner:-0}" winlist="aura*" diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index d16419ff1..da26b45a0 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -657,7 +657,7 @@ if [ ! "$NOLOGIN" = 1 ] && grep -q '^root:' "$passwd" 2>/dev/null; then chmod 622 "$CROUTONLOCKDIR/frecon" fi chrootcmd "(exec >/dev/null 2>&1 Date: Sat, 21 Feb 2015 01:01:53 -0800 Subject: [PATCH 265/334] Test xorg on K1 as well --- test/tests/35-xorg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tests/35-xorg b/test/tests/35-xorg index 33afbe7c0..3d52ace5a 100644 --- a/test/tests/35-xorg +++ b/test/tests/35-xorg @@ -5,8 +5,8 @@ # All supported releases should be able to launch an xorg server if [ -z "$release" ]; then - if uname -m | grep -q 'arm'; then - log "xorg is not available on ARM. Skipping test." + if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo; then + log "xorg is not available on Samsung ARM. Skipping test." else echo "all" fi From 3d324109d5cbc4491a914c3e992e2f39bc9911a8 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 22 Feb 2015 21:12:28 -0800 Subject: [PATCH 266/334] Need bsdmainutils for hexdump --- targets/x11-common | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/x11-common b/targets/x11-common index b05b4193f..aa3b4aed7 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -20,8 +20,8 @@ install --minimal dbus xdg-utils ln -sf croutonpowerd /usr/local/bin/gnome-screensaver-command ln -sf croutonpowerd /usr/local/bin/xscreensaver-command -# Install xbindkeys and xautomation for shortcuts; kbd for chvt -install --minimal xbindkeys xautomation kbd +# Install bsdmainutils, xbindkeys and xautomation for shortcuts; kbd for chvt +install --minimal bsdmainutils xbindkeys xautomation kbd # Allow users to run sudo chvt without password, so we don't need to run # croutoncycle as root echo '%sudo ALL = NOPASSWD:/bin/chvt' > /etc/sudoers.d/chvt From 0a03d84e908271553b79bc32b7678dd441b8a30d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 22 Feb 2015 21:36:05 -0800 Subject: [PATCH 267/334] Fix croutontriggerd on 32-bit systems --- chroot-bin/croutontriggerd | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chroot-bin/croutontriggerd b/chroot-bin/croutontriggerd index 811f7092b..bdd424acd 100755 --- a/chroot-bin/croutontriggerd +++ b/chroot-bin/croutontriggerd @@ -8,6 +8,11 @@ . "`dirname "$0"`/../installer/functions" # hexdump output format variables +if getconf LONG_BIT | grep -q 32; then + HEXDUMP_FMT='2/4 "%u 0 " " " 2/2 "%u " " " 1/4 "%u " "\n"' +else + HEXDUMP_FMT='4/4 "%u " " " 2/2 "%u " " " 1/4 "%u " "\n"' +fi SECONDS_LO='$1' SECONDS_HI='$2' USECONDS_LO='$3' @@ -57,8 +62,7 @@ while :; do unset "$event" else # Read in the event files and split into input_event fields - stdbuf -oL hexdump -e '4/4 "%u " " " 2/2 "%u " " " 1/4 "%u " "\n"' \ - "$event" & + stdbuf -oL hexdump -e "$HEXDUMP_FMT" "$event" & eval "${event##*/}='$!'" fi done From 295d91f125a68de6523884cdd536a7e58a355155 Mon Sep 17 00:00:00 2001 From: DennisL Date: Mon, 23 Feb 2015 20:49:11 -0500 Subject: [PATCH 268/334] log invalid signatures Log 'Invalid signature...' output instead of throwing errors. --- src/fbserver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fbserver.c b/src/fbserver.c index 8f5b4615b..cd0898af1 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -243,7 +243,7 @@ struct cache_entry* find_shm(uint64_t paddr, uint64_t sig, size_t length) { if (*((uint64_t*)entry->map) == sig) return entry; - error("Invalid signature, fetching new shm!"); + log(1, "Invalid signature, fetching new shm!"); close_mmap(entry); } From 230a1afa6b27d13f35764974822f27f3558a7438 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 18:12:35 -0800 Subject: [PATCH 269/334] Fix X11 backend selection --- targets/x11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/x11 b/targets/x11 index da76368da..5951d67cb 100644 --- a/targets/x11 +++ b/targets/x11 @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ - ([ "${ARCH#arm}" = "$ARCH" ] && \ + ([ "${ARCH#arm}" != "$ARCH" ] && \ awk -F= '/_RELEASE_VERSION=/ { exit int($2) < 6689 }' \ '/etc/lsb-release'); then # Xorg won't work on Samsung ARM devices or K1 on release less than 6689 From 1a7e5f815cfc329557ab806f003ff7868f519ad1 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 17:54:16 -0800 Subject: [PATCH 270/334] Prevent X11 from touching the vgem device, which is broken in stock Xorg Fixes #1443 for xorg. More needs to be done for xiwi. Why Xorg thought it was a good idea to probe devices regardless of settings (including ones that explicitly say not to probe) is beyond me. --- chroot-etc/xorg-dummy.conf | 1 + src/freon.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/chroot-etc/xorg-dummy.conf b/chroot-etc/xorg-dummy.conf index d3199324f..83faefc5b 100644 --- a/chroot-etc/xorg-dummy.conf +++ b/chroot-etc/xorg-dummy.conf @@ -33,6 +33,7 @@ EndSection Section "ServerFlags" Option "AutoAddDevices" "false" + Option "AutoAddGPU" "false" Option "DontVTSwitch" "true" Option "AllowMouseOpenFail" "true" Option "PciForceNone" "true" diff --git a/src/freon.c b/src/freon.c index f60a5897a..f985b8bf9 100644 --- a/src/freon.c +++ b/src/freon.c @@ -38,11 +38,13 @@ static int lockfd = -1; static int (*orig_ioctl)(int d, int request, void* data); static int (*orig_open)(const char *pathname, int flags, mode_t mode); static int (*orig_close)(int fd); +static int (*orig_udev_sysname)(void *udev_enumerate, const char *sysname); static void preload_init() { orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); orig_open = dlsym(RTLD_NEXT, "open"); orig_close = dlsym(RTLD_NEXT, "close"); + orig_udev_sysname = dlsym(RTLD_NEXT, "udev_enumerate_add_match_sysname"); } /* Grabs the system-wide lockfile that arbitrates which chroot is using the GPU. @@ -190,3 +192,11 @@ int close(int fd) { } return orig_close(fd); } + +int udev_enumerate_add_match_sysname(void *udev_enum, const char *sysname) { + if (!orig_udev_sysname) preload_init(); + if (sysname && !strcmp(sysname, "card[0-9]*")) { + sysname = "card0"; + } + return orig_udev_sysname(udev_enum, sysname); +} From 24fa73b344ad99a58360d738875621b0d1d824fd Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 17:55:22 -0800 Subject: [PATCH 271/334] Use the display number in the log filename for freon. --- chroot-etc/xserverrc-xorg | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index 36acb66d6..4aeb99a7b 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -30,7 +30,14 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then fi # Freon necessitates the preload hack for X to coexist X=/usr/bin/Xorg - XARGS="-logfile /tmp/Xorg.$$.log $XARGS" + logfile="/tmp/Xorg.crouton.$$.log" + for arg in "$@"; do + disp="`echo "$arg" | sed -n 's/^\:\([0-9]*\)$/\1/p'`" + if [ -n "$disp" ]; then + logfile="/tmp/Xorg.crouton.$disp.log" + fi + done + XARGS="-logfile $logfile" export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" fi From b207e733b9672200a476fa987d43b9f5ff60df48 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 18:50:35 -0800 Subject: [PATCH 272/334] Use croutonfreon.so for xiwi too. Fixes #1443 and shouldn't have any ill side-effects. --- chroot-etc/xserverrc-xiwi | 5 +++++ targets/xiwi | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index 5bdb64c17..fc3f4ca45 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -17,4 +17,9 @@ if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi +# Handle Freon systems +if [ ! -f "/sys/class/tty/tty0/active" ]; then + export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" +fi + exec /usr/bin/Xorg $XARGS "$@" diff --git a/targets/xiwi b/targets/xiwi index ea250fa22..c9a1f5ab5 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -12,6 +12,11 @@ CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-lo ### Append to prepare.sh: XMETHOD="${XMETHOD:-xiwi}" +# On Freon, we need crazy xorg hacks +if [ ! -f "/sys/class/tty/tty0/active" ]; then + compile freon '-ldl' so +fi + # Unhold xserver-xorg-video-dummy to make sure deps are pulled in if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then apt-mark unhold xserver-xorg-video-dummy || true 2>/dev/null From 302d2ef11bf4c5e9c3c53e6ede688c3ac9e7da8e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 19:20:44 -0800 Subject: [PATCH 273/334] Fix xorg+xiwi on precise. Fixes #1294 --- targets/xiwi | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/targets/xiwi b/targets/xiwi index c9a1f5ab5..d30dc815e 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -17,12 +17,22 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then compile freon '-ldl' so fi +ltspackages='' +# On non-ARM precise, install lts-trusty xorg server for compatibility with xorg +# if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) +if [ "${ARCH#arm}" = "$ARCH" ] && release -eq precise \ + && ! uname -r | grep -q "^3.4."; then + # We still install xorg later to pull in its dependencies + ltspackages='-lts-trusty' + install --minimal "xserver-xorg$ltspackages" "libgl1-mesa-glx$ltspackages" +fi + # Unhold xserver-xorg-video-dummy to make sure deps are pulled in if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then - apt-mark unhold xserver-xorg-video-dummy || true 2>/dev/null + apt-mark unhold xserver-xorg-video-dummy$ltspackages || true 2>/dev/null fi -install xorg xserver-xorg-video-dummy +install xorg xserver-xorg-video-dummy$ltspackages # Compile croutonfbserver compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ @@ -45,7 +55,7 @@ echo "Download Xorg dummy driver..." 1>&2 wget -O "$DUMMYBUILDTMP/dummy.tar.gz" "$urlbase/xf86-video-dummy-0.3.7.tar.gz" install --minimal --asdeps patch gcc libc-dev pkg-config \ - xserver-xorg-dev x11proto-xf86dga-dev + xserver-xorg-dev$ltspackages x11proto-xf86dga-dev ( cd "$DUMMYBUILDTMP" @@ -388,7 +398,7 @@ EOF if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then # Hold xserver-xorg-video-dummy to make sure the driver does not get erased - apt-mark hold xserver-xorg-video-dummy + apt-mark hold xserver-xorg-video-dummy$ltspackages fi TIPS="$TIPS"' From 841ff620ccd45ff57c7ca723007874d14fdc8930 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Feb 2015 19:46:56 -0800 Subject: [PATCH 274/334] Install dmz-cursor-theme in x11-common --- targets/x11-common | 3 +++ targets/xephyr | 2 +- targets/xorg | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/targets/x11-common b/targets/x11-common index aa3b4aed7..3e625faf5 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -20,6 +20,9 @@ install --minimal dbus xdg-utils ln -sf croutonpowerd /usr/local/bin/gnome-screensaver-command ln -sf croutonpowerd /usr/local/bin/xscreensaver-command +# Install nicer cursors +install --minimal dmz-cursor-theme + # Install bsdmainutils, xbindkeys and xautomation for shortcuts; kbd for chvt install --minimal bsdmainutils xbindkeys xautomation kbd # Allow users to run sudo chvt without password, so we don't need to run diff --git a/targets/xephyr b/targets/xephyr index 3fbfbf87b..aaaa0c5d4 100644 --- a/targets/xephyr +++ b/targets/xephyr @@ -32,7 +32,7 @@ fi # xserver-xephyr won't auto replace the manually-downloaded version. install --minimal \ - xserver-xephyr xinit dmz-cursor-theme libgl1-mesa-dri \ + xserver-xephyr xinit libgl1-mesa-dri \ x11-utils x11-xserver-utils xinput xterm # Compile croutoncursor diff --git a/targets/xorg b/targets/xorg index d9d169b95..7df83c6e9 100644 --- a/targets/xorg +++ b/targets/xorg @@ -104,7 +104,7 @@ END apt-get -y --force-yes --no-install-recommends dist-upgrade -f fi -install xorg dmz-cursor-theme xserver-xorg-video-fbdev$ltspackages +install xorg xserver-xorg-video-fbdev$ltspackages if [ "${ARCH#arm}" = "$ARCH" ]; then install xserver-xorg-video-intel$ltspackages fi From 2331c99d1b420281fcc8705785e3ca8a837023ef Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 27 Feb 2015 14:27:43 -0800 Subject: [PATCH 275/334] Move vgem workaround to its own .so and use on all platforms Fixes #1450 --- Makefile | 3 +++ chroot-etc/xserverrc-xiwi | 2 ++ chroot-etc/xserverrc-xorg | 2 ++ src/freon.c | 10 ---------- src/xorg.c | 28 ++++++++++++++++++++++++++++ targets/xiwi | 3 +++ targets/xorg | 3 +++ 7 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 src/xorg.c diff --git a/Makefile b/Makefile index 65e954941..db13ce369 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,9 @@ $(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile croutonfreon.so: src/freon.c Makefile gcc $(CFLAGS) -shared -fPIC src/freon.c -ldl -o croutonfreon.so +croutonxorg.so: src/xorg.c Makefile + gcc $(CFLAGS) -shared -fPIC src/xorg.c -ldl -o croutonxorg.so + extension: $(EXTTARGET) $(CONTRIBUTORS): $(GITHEAD) $(CONTRIBUTORSSED) diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index fc3f4ca45..90b8645c5 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -22,4 +22,6 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" fi +export LD_PRELOAD="/usr/local/lib/croutonxorg.so:$LD_PRELOAD" + exec /usr/bin/Xorg $XARGS "$@" diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index 4aeb99a7b..d2e2c4e00 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -41,4 +41,6 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" fi +export LD_PRELOAD="/usr/local/lib/croutonxorg.so:$LD_PRELOAD" + exec "$X" $XARGS "$@" diff --git a/src/freon.c b/src/freon.c index f985b8bf9..f60a5897a 100644 --- a/src/freon.c +++ b/src/freon.c @@ -38,13 +38,11 @@ static int lockfd = -1; static int (*orig_ioctl)(int d, int request, void* data); static int (*orig_open)(const char *pathname, int flags, mode_t mode); static int (*orig_close)(int fd); -static int (*orig_udev_sysname)(void *udev_enumerate, const char *sysname); static void preload_init() { orig_ioctl = dlsym(RTLD_NEXT, "ioctl"); orig_open = dlsym(RTLD_NEXT, "open"); orig_close = dlsym(RTLD_NEXT, "close"); - orig_udev_sysname = dlsym(RTLD_NEXT, "udev_enumerate_add_match_sysname"); } /* Grabs the system-wide lockfile that arbitrates which chroot is using the GPU. @@ -192,11 +190,3 @@ int close(int fd) { } return orig_close(fd); } - -int udev_enumerate_add_match_sysname(void *udev_enum, const char *sysname) { - if (!orig_udev_sysname) preload_init(); - if (sysname && !strcmp(sysname, "card[0-9]*")) { - sysname = "card0"; - } - return orig_udev_sysname(udev_enum, sysname); -} diff --git a/src/xorg.c b/src/xorg.c new file mode 100644 index 000000000..ab4b12793 --- /dev/null +++ b/src/xorg.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2015 The crouton Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * LD_PRELOAD hack to make Xorg happy in a system with the vgem device enabled. + * gcc -shared -fPIC -ldl -Wall -O2 xorg.c -o croutonxorg.so + */ + +#define _GNU_SOURCE +#include +#include + +#define TRACE(...) /* fprintf(stderr, __VA_ARGS__) */ + +static int (*orig_udev_sysname)(void *udev_enumerate, const char *sysname); + +static void preload_init() { + orig_udev_sysname = dlsym(RTLD_NEXT, "udev_enumerate_add_match_sysname"); +} + +int udev_enumerate_add_match_sysname(void *udev_enum, const char *sysname) { + if (!orig_udev_sysname) preload_init(); + TRACE("udev_enumerate_add_match_sysname '%s'\n", sysname); + if (sysname && !strcmp(sysname, "card[0-9]*")) { + sysname = "card0"; + } + return orig_udev_sysname(udev_enum, sysname); +} diff --git a/targets/xiwi b/targets/xiwi index d30dc815e..e9d2acd42 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -17,6 +17,9 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then compile freon '-ldl' so fi +# We need xorg workarounds on all platforms +compile xorg '-ldl' so + ltspackages='' # On non-ARM precise, install lts-trusty xorg server for compatibility with xorg # if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) diff --git a/targets/xorg b/targets/xorg index 7df83c6e9..371565b52 100644 --- a/targets/xorg +++ b/targets/xorg @@ -25,6 +25,9 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then compile freon '-ldl' so fi +# We need xorg workarounds on all platforms +compile xorg '-ldl' so + ltspackages='' # On non-ARM precise, install lts-trusty xorg server to support newer hardware # if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) From 3811e6a59d9f7ff387b2d4ab1c5357d646cea49b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 28 Feb 2015 22:21:50 -0800 Subject: [PATCH 276/334] Fix edit-chroot move filesystem check. Fixes 1441 --- host-bin/edit-chroot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-bin/edit-chroot b/host-bin/edit-chroot index 86fbf99ac..dbeebdc55 100755 --- a/host-bin/edit-chroot +++ b/host-bin/edit-chroot @@ -163,7 +163,7 @@ elif [ $# -gt 1 -a -f "$TARBALL" ]; then fi # Don't allow moving to non-ext filesystems (but don't check if just renaming) -if [ -n "$MOVE" -a "${MOVE#*/}" = "$MOVE" ] && \ +if [ -n "$MOVE" -a "${MOVE#*/}" != "$MOVE" ] && \ df -T "`getmountpoint "$MOVE"`" | awk '$2~"^ext"{exit 1}'; then error 2 "Chroots can only be moved to ext filesystems." fi From 942fcc6cd30a3d82e7ca9f4a46c1da759c8f7811 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 28 Feb 2015 22:42:07 -0800 Subject: [PATCH 277/334] Print out all of the pids stopping unmount-chroot, not just one --- host-bin/unmount-chroot | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/host-bin/unmount-chroot b/host-bin/unmount-chroot index 424e48e3e..b2fc4dd1a 100755 --- a/host-bin/unmount-chroot +++ b/host-bin/unmount-chroot @@ -86,7 +86,7 @@ checkusage() { if [ -n "$FORCE" ]; then return 0 fi - local b="${1%/}/" pid ppid proot prootdir root rootdir + local b="${1%/}/" pid ppid proot prootdir root rootdir pids='' for root in /proc/*/root; do if [ ! -r "$root" ]; then continue @@ -110,10 +110,15 @@ checkusage() { fi fi if [ -n "$PRINT" ]; then - ps -p "$pid" -o pid= -o cmd= || true + pids="$pids $pid" + continue fi return 1 done + if [ -n "$PRINT" -a -n "$pids" ]; then + ps -p "${pids# }" -o pid= -o cmd= || true + return 1 + fi return 0 } From f551e0a6f99db551729823de57e038b6c286e41d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sat, 28 Feb 2015 23:08:53 -0800 Subject: [PATCH 278/334] Ignore processes that session_manager has inherited. Fixes #1456 --- host-bin/unmount-chroot | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/host-bin/unmount-chroot b/host-bin/unmount-chroot index b2fc4dd1a..c123592c8 100755 --- a/host-bin/unmount-chroot +++ b/host-bin/unmount-chroot @@ -78,8 +78,8 @@ fi # appropriate commands by checking if the command's parent root is not equal # to the pid's root. This avoids not unmounting due to a lazy-quitting # background application within the chroot. We also don't consider processes -# that have a parent PID of 1 (which would mean an orphaned process in this -# case), as enter-chroot never orphans its children. +# that have a parent PID of 1 or that of session_manager's (which would mean an +# orphaned process in this case), as enter-chroot never orphans its children. # $1: $base; the canonicalized base path of the chroot # Returns: non-zero if the chroot is in use. checkusage() { @@ -87,6 +87,7 @@ checkusage() { return 0 fi local b="${1%/}/" pid ppid proot prootdir root rootdir pids='' + local smgrpid="`pgrep -o -u 0 -x session_manager || echo 1`" for root in /proc/*/root; do if [ ! -r "$root" ]; then continue @@ -99,7 +100,7 @@ checkusage() { pid="${root#/proc/}" pid="${pid%/root}" ppid="`ps -p "$pid" -o ppid= 2>/dev/null | sed 's/ //g'`" - if [ -z "$ppid" ] || [ "$ppid" -eq 1 ]; then + if [ -z "$ppid" ] || [ "$ppid" -eq 1 -o "$ppid" -eq "$smgrpid" ]; then continue fi proot="/proc/$ppid/root" From 500c8825717008712ded2276c995c7c95af85469 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 25 Nov 2014 17:50:25 -0800 Subject: [PATCH 279/334] Initial hack of a "xiwi" application that runs an app in its own cros window --- chroot-bin/xiwi | 24 ++++++++++++++++++++++++ targets/xiwi | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100755 chroot-bin/xiwi diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi new file mode 100755 index 000000000..97509ea9d --- /dev/null +++ b/chroot-bin/xiwi @@ -0,0 +1,24 @@ +#!/bin/sh -e +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Runs the specified X11 application in its own X server in Chromium OS. + +if [ "$#" = 0 ]; then + echo "Usage: $0 cmd params" 1>&2 + exit 2 +elif [ "$1" = '/' ]; then + shift 1 + "$@" & + exec /usr/bin/ratpoison -f /dev/stdin </dev/null fi -install xorg xserver-xorg-video-dummy$ltspackages +install xorg xserver-xorg-video-dummy$ltspackages ratpoison # Compile croutonfbserver compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ From b794af1214eaa03256a4e168721d862798291c5a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 08:43:14 -0800 Subject: [PATCH 280/334] It's a window, not a tab. --- chroot-bin/croutonxinitrc-wrapper | 2 +- host-ext/crouton/background.js | 4 ++-- host-ext/crouton/window.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 88cc4c1c6..6cdcc4193 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -146,7 +146,7 @@ and enabled, and up to date. (download from http://goo.gl/OVQOEt)" 1>&2 try="$((try+1))" done if [ "$ret" -eq 0 ]; then - echo "Connected to extension, launched crouton in a tab window." 1>&2 + echo "Connected to extension, launched crouton in a window." 1>&2 fi fi diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 126ad72fe..010649179 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -486,7 +486,7 @@ function websocketMessage(evt) { } refreshUI(); break; - case 'X': /* Ask to open a crouton-in-a-tab window */ + case 'X': /* Ask to open a crouton window */ display = payload match = display.match(/^:([0-9]+)$/) displaynum = match ? match[1] : null @@ -530,7 +530,7 @@ function websocketMessage(evt) { kiwi_win_[display].window = null; win = windows_.filter(function(x){ return x.display == display })[0] - name = win ? win.name : "crouton in a tab"; + name = win ? win.name : "crouton in a window"; chrome.windows.create({ 'url': "window.html?display=" + displaynum + "&debug=" + (debug_ ? 1 : 0) + diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index d97790ad9..8489b5f93 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -16,7 +16,7 @@ var errordiv_ = null; /* error div */ var debug_ = 0; /* Debuging level, passed to NaCl module */ var hidpi_ = 0; /* HiDPI mode */ var display_ = null; /* Display number to use */ -var title_ = "crouton in a tab"; /* window title */ +var title_ = "crouton in a window"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ var error_ = false; /* An error has occured */ @@ -95,7 +95,7 @@ function setHiDPI(hidpi) { } function setTitle(title) { - document.title = "crouton in a tab: " + title + " (" + display_ + ")"; + document.title = "crouton (" + display_ + "): " + title; } /* Set status message */ From 10128f2fbf23b360c7ed1eb8a63146db9abf2a62 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 09:52:37 -0800 Subject: [PATCH 281/334] Generate kiwi window title based off of active window --- chroot-bin/xiwi | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 97509ea9d..32ff2203e 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -10,14 +10,20 @@ if [ "$#" = 0 ]; then exit 2 elif [ "$1" = '/' ]; then shift 1 + genwindowname="`cat /etc/crouton/name`/$1: \`\ + ratpoison -c 'windows %s%t' | sed -n '/^\*/s/^.//p'\`" "$@" & exec /usr/bin/ratpoison -f /dev/stdin < Date: Tue, 17 Feb 2015 14:31:47 -0800 Subject: [PATCH 282/334] extension: Always send confirmation when receiving a windowlist Old websockets will harmlessly print out the croutoncycle usage message. New websockets can push windowlist updates and get an OK. --- host-ext/crouton/background.js | 3 +++ src/websocket.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 010649179..16263d777 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -483,6 +483,9 @@ function websocketMessage(evt) { kiwi_win_[k.display].window.setTitle(k.name); } }) + + lastwindowlistupdate_ = new Date().getTime(); + websocket_.send("COK"); } refreshUI(); break; diff --git a/src/websocket.c b/src/websocket.c index a113313d3..7ff241b40 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -378,6 +378,9 @@ static int socket_client_handle_unrequested(const char* buffer, return -1; } replylength += n; + } else if (param[0] == 'O') { + /* Extra OK response from a C back-and-forth. Disregard. */ + break; } else { /* Launch command in background (this is necessary as croutoncycle may send a websocket command, leaving us From 232e557ec7622c3b3cdeb52646540af58fc029d7 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 14:34:00 -0800 Subject: [PATCH 283/334] Push windowlist updates on window switch TODO: Need to push updates on titlebar changes too. --- chroot-bin/xiwi | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 32ff2203e..39b5ef996 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -5,26 +5,35 @@ # Runs the specified X11 application in its own X server in Chromium OS. +. "`dirname "$0"`/../installer/functions" + if [ "$#" = 0 ]; then echo "Usage: $0 cmd params" 1>&2 exit 2 elif [ "$1" = '/' ]; then shift 1 - genwindowname="`cat /etc/crouton/name`/$1: \`\ - ratpoison -c 'windows %s%t' | sed -n '/^\*/s/^.//p'\`" "$@" & - exec /usr/bin/ratpoison -f /dev/stdin < Date: Tue, 17 Feb 2015 15:27:44 -0800 Subject: [PATCH 284/334] Update xiwi on title change --- chroot-bin/xiwi | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 39b5ef996..4e378d8a6 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -6,6 +6,7 @@ # Runs the specified X11 application in its own X server in Chromium OS. . "`dirname "$0"`/../installer/functions" +xiwicmd="`readlink -f "$0"`" if [ "$#" = 0 ]; then echo "Usage: $0 cmd params" 1>&2 @@ -13,8 +14,7 @@ if [ "$#" = 0 ]; then elif [ "$1" = '/' ]; then shift 1 "$@" & - xiwicmd="`readlink -f "$0"`" - exec ratpoison -f /dev/stdin </dev/null </dev/null \ + | while read _; do + "$xiwicmd" // "$2" + done + } 3>> "/tmp/crouton-lock/xiwi-$DISPLAY-$id" & else export XMETHOD=xiwi exec /usr/local/bin/xinit "`readlink -f "$0"`" / "$@" From ab9d15d3ea7d5dac3b107be75e1810f7db041c74 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 15:30:35 -0800 Subject: [PATCH 285/334] Improve app close responsiveness/correctness. --- chroot-bin/xiwi | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 4e378d8a6..e559bd25d 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -13,17 +13,14 @@ if [ "$#" = 0 ]; then exit 2 elif [ "$1" = '/' ]; then shift 1 - "$@" & - exec ratpoison -f /dev/stdin >/dev/null </dev/null <> "/tmp/crouton-lock/xiwi-$DISPLAY-$id" & else export XMETHOD=xiwi - exec /usr/local/bin/xinit "`readlink -f "$0"`" / "$@" + exec /usr/local/bin/xinit "$xiwicmd" / "$@" fi From 4c3b82a3ad5044db8ffcc1e92dcefaaebb69c974 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 17:41:55 -0800 Subject: [PATCH 286/334] Set the root cursor --- chroot-bin/xiwi | 1 + 1 file changed, 1 insertion(+) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index e559bd25d..2f379665f 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -13,6 +13,7 @@ if [ "$#" = 0 ]; then exit 2 elif [ "$1" = '/' ]; then shift 1 + xsetroot -cursor_name left_ptr ratpoison -f /dev/stdin >/dev/null < Date: Tue, 17 Feb 2015 16:32:38 -0800 Subject: [PATCH 287/334] Prevent crouton from segfaulting on bad cursors. Not sure when this happens, so just adding logging code for now. --- src/fbserver.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fbserver.c b/src/fbserver.c index cd0898af1..62ee7c791 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -438,6 +438,10 @@ int write_image(const struct screen* screen) { /* Writes cursor image to websocket */ int write_cursor() { XFixesCursorImage *img = XFixesGetCursorImage(dpy); + if (!img) { + error("XFixesGetCursorImage returned NULL"); + return -1; + } int size = img->width*img->height; const int replylength = sizeof(struct cursor_reply) + size*sizeof(uint32_t); char reply_raw[FRAMEMAXHEADERSIZE + replylength]; From e325260732436b98282a545dab29639e7267d4b3 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 17:38:30 -0800 Subject: [PATCH 288/334] kiwi: fall back on the default cursor when data is unavailable --- host-ext/nacl_src/kiwi.cc | 34 +++++++++++++++++++++++----------- src/fbserver.c | 29 +++++++++++++++-------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index 7df041122..dc41fb409 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -211,6 +211,7 @@ class KiwiInstance : public pp::Instance { } cursor_cache_.clear(); + last_cursor_request_ = 0; SocketReceive(); @@ -311,11 +312,14 @@ class KiwiInstance : public pp::Instance { if (it == cursor_cache_.end()) { /* No cache entry, ask for data. */ SocketSend(pp::Var("P"), false); + last_cursor_request_ = reply->cursor_serial; } else { LogMessage(2) << "Cursor use cache for " << reply->cursor_serial; - pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, - it->second.img, it->second.hot); + pp::MouseCursor::SetCursor(this, + it->second.img.is_null() ? PP_MOUSECURSOR_TYPE_POINTER + : PP_MOUSECURSOR_TYPE_CUSTOM, + it->second.img, it->second.hot); } } return true; @@ -348,21 +352,28 @@ class KiwiInstance : public pp::Instance { int w = cursor->width/scale; int h = cursor->height/scale; - pp::ImageData img(this, pp::ImageData::GetNativeImageDataFormat(), - pp::Size(w, h), true); - uint32_t* imgdata = (uint32_t*)img.data(); - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - /* Nearest neighbour is least ugly */ - imgdata[y*w + x] = cursor->pixels[scale*y*scale*w + scale*x]; + pp::ImageData img; + if (cursor->cursor_serial) { + img = pp::ImageData(this, pp::ImageData::GetNativeImageDataFormat(), + pp::Size(w, h), true); + uint32_t* imgdata = (uint32_t*)img.data(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + /* Nearest neighbour is least ugly */ + imgdata[y*w + x] = cursor->pixels[scale*y*scale*w + scale*x]; + } } + } else { + cursor->cursor_serial = last_cursor_request_; } pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); cursor_cache_[cursor->cursor_serial].img = img; cursor_cache_[cursor->cursor_serial].hot = hot; - pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, - img, hot); + pp::MouseCursor::SetCursor(this, + img.is_null() ? PP_MOUSECURSOR_TYPE_POINTER + : PP_MOUSECURSOR_TYPE_CUSTOM, + img, hot); return true; } @@ -1088,6 +1099,7 @@ class KiwiInstance : public pp::Instance { pp::Point hot; }; std::unordered_map cursor_cache_; + uint32_t last_cursor_request_; /* Display to connect to */ int display_ = -1; diff --git a/src/fbserver.c b/src/fbserver.c index 62ee7c791..86fe0fd19 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -438,11 +438,10 @@ int write_image(const struct screen* screen) { /* Writes cursor image to websocket */ int write_cursor() { XFixesCursorImage *img = XFixesGetCursorImage(dpy); - if (!img) { - error("XFixesGetCursorImage returned NULL"); - return -1; + int size = 0; + if (img) { + size = img->width*img->height; } - int size = img->width*img->height; const int replylength = sizeof(struct cursor_reply) + size*sizeof(uint32_t); char reply_raw[FRAMEMAXHEADERSIZE + replylength]; struct cursor_reply* reply = @@ -451,18 +450,20 @@ int write_cursor() { memset(reply_raw, 0, sizeof(*reply_raw)); reply->type = 'P'; - reply->width = img->width; - reply->height = img->height; - reply->xhot = img->xhot; - reply->yhot = img->yhot; - reply->cursor_serial = img->cursor_serial; - /* This casts long[] to uint32_t[] */ - int i; - for (i = 0; i < size; i++) - reply->pixels[i] = img->pixels[i]; + if (img) { + reply->width = img->width; + reply->height = img->height; + reply->xhot = img->xhot; + reply->yhot = img->yhot; + reply->cursor_serial = img->cursor_serial; + /* This casts long[] to uint32_t[] */ + int i; + for (i = 0; i < size; i++) + reply->pixels[i] = img->pixels[i]; + XFree(img); + } socket_client_write_frame(reply_raw, replylength, WS_OPCODE_BINARY, 1); - XFree(img); return 0; } From 4483c655d66fbf6c69c2bfe643c6b6dfada93d3e Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 17:48:34 -0800 Subject: [PATCH 289/334] Revert "kiwi: fall back on the default cursor when data is unavailable" This reverts commit e325260732436b98282a545dab29639e7267d4b3. It doesn't appear to be necessary, but keeping the code here for reference. --- host-ext/nacl_src/kiwi.cc | 34 +++++++++++----------------------- src/fbserver.c | 29 ++++++++++++++--------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/host-ext/nacl_src/kiwi.cc b/host-ext/nacl_src/kiwi.cc index dc41fb409..7df041122 100644 --- a/host-ext/nacl_src/kiwi.cc +++ b/host-ext/nacl_src/kiwi.cc @@ -211,7 +211,6 @@ class KiwiInstance : public pp::Instance { } cursor_cache_.clear(); - last_cursor_request_ = 0; SocketReceive(); @@ -312,14 +311,11 @@ class KiwiInstance : public pp::Instance { if (it == cursor_cache_.end()) { /* No cache entry, ask for data. */ SocketSend(pp::Var("P"), false); - last_cursor_request_ = reply->cursor_serial; } else { LogMessage(2) << "Cursor use cache for " << reply->cursor_serial; - pp::MouseCursor::SetCursor(this, - it->second.img.is_null() ? PP_MOUSECURSOR_TYPE_POINTER - : PP_MOUSECURSOR_TYPE_CUSTOM, - it->second.img, it->second.hot); + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + it->second.img, it->second.hot); } } return true; @@ -352,28 +348,21 @@ class KiwiInstance : public pp::Instance { int w = cursor->width/scale; int h = cursor->height/scale; - pp::ImageData img; - if (cursor->cursor_serial) { - img = pp::ImageData(this, pp::ImageData::GetNativeImageDataFormat(), - pp::Size(w, h), true); - uint32_t* imgdata = (uint32_t*)img.data(); - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - /* Nearest neighbour is least ugly */ - imgdata[y*w + x] = cursor->pixels[scale*y*scale*w + scale*x]; - } + pp::ImageData img(this, pp::ImageData::GetNativeImageDataFormat(), + pp::Size(w, h), true); + uint32_t* imgdata = (uint32_t*)img.data(); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + /* Nearest neighbour is least ugly */ + imgdata[y*w + x] = cursor->pixels[scale*y*scale*w + scale*x]; } - } else { - cursor->cursor_serial = last_cursor_request_; } pp::Point hot(cursor->xhot/scale, cursor->yhot/scale); cursor_cache_[cursor->cursor_serial].img = img; cursor_cache_[cursor->cursor_serial].hot = hot; - pp::MouseCursor::SetCursor(this, - img.is_null() ? PP_MOUSECURSOR_TYPE_POINTER - : PP_MOUSECURSOR_TYPE_CUSTOM, - img, hot); + pp::MouseCursor::SetCursor(this, PP_MOUSECURSOR_TYPE_CUSTOM, + img, hot); return true; } @@ -1099,7 +1088,6 @@ class KiwiInstance : public pp::Instance { pp::Point hot; }; std::unordered_map cursor_cache_; - uint32_t last_cursor_request_; /* Display to connect to */ int display_ = -1; diff --git a/src/fbserver.c b/src/fbserver.c index 86fe0fd19..62ee7c791 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -438,10 +438,11 @@ int write_image(const struct screen* screen) { /* Writes cursor image to websocket */ int write_cursor() { XFixesCursorImage *img = XFixesGetCursorImage(dpy); - int size = 0; - if (img) { - size = img->width*img->height; + if (!img) { + error("XFixesGetCursorImage returned NULL"); + return -1; } + int size = img->width*img->height; const int replylength = sizeof(struct cursor_reply) + size*sizeof(uint32_t); char reply_raw[FRAMEMAXHEADERSIZE + replylength]; struct cursor_reply* reply = @@ -450,20 +451,18 @@ int write_cursor() { memset(reply_raw, 0, sizeof(*reply_raw)); reply->type = 'P'; - if (img) { - reply->width = img->width; - reply->height = img->height; - reply->xhot = img->xhot; - reply->yhot = img->yhot; - reply->cursor_serial = img->cursor_serial; - /* This casts long[] to uint32_t[] */ - int i; - for (i = 0; i < size; i++) - reply->pixels[i] = img->pixels[i]; - XFree(img); - } + reply->width = img->width; + reply->height = img->height; + reply->xhot = img->xhot; + reply->yhot = img->yhot; + reply->cursor_serial = img->cursor_serial; + /* This casts long[] to uint32_t[] */ + int i; + for (i = 0; i < size; i++) + reply->pixels[i] = img->pixels[i]; socket_client_write_frame(reply_raw, replylength, WS_OPCODE_BINARY, 1); + XFree(img); return 0; } From 77df2b3fad7394011cd4134fd4ce78f9a5b51b0f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 17 Feb 2015 18:30:05 -0800 Subject: [PATCH 290/334] Add nicer help and tip text for the xiwi command. --- chroot-bin/xiwi | 3 ++- targets/xiwi | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 2f379665f..e6ca7c207 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -9,7 +9,8 @@ xiwicmd="`readlink -f "$0"`" if [ "$#" = 0 ]; then - echo "Usage: $0 cmd params" 1>&2 + echo "Usage: ${0##*/} application [parameters ...] +Launches a windowed session in Chromium OS for any graphical application." 1>&2 exit 2 elif [ "$1" = '/' ]; then shift 1 diff --git a/targets/xiwi b/targets/xiwi index f2ad945e3..c84a72477 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -408,6 +408,11 @@ TIPS="$TIPS"' You can open your running chroot desktops by clicking on the extension icon. Once in a crouton window, press fullscreen or the "switch window" key to switch back to Chromium OS. + +You can launch individual apps in crouton windows by using the "xiwi" command +in the chroot shell. Wrap that command with enter-chroot to launch directly +from the host shell. Use the enter-chroot parameter -b to run in the background. +Example: sudo enter-chroot -b xiwi xterm ' ### append x11-common From f041008b26371e72dd5fc85421ebff0186b520ab Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 20 Feb 2015 18:16:49 -0800 Subject: [PATCH 291/334] Add in-app window cycling shortcuts. Ctrl+Alt+Tab = next window Ctrl+Alt+Shift+Tab = previous window Ctrl+Alt+Shift+Escape+Escape = close window --- chroot-bin/xiwi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index e6ca7c207..ead99d946 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -17,8 +17,12 @@ elif [ "$1" = '/' ]; then xsetroot -cursor_name left_ptr ratpoison -f /dev/stdin >/dev/null < Date: Sun, 22 Feb 2015 01:08:32 -0800 Subject: [PATCH 292/334] Bump extension version and remove unneeded tabs permission. --- host-ext/crouton/manifest.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 300874770..4a560c928 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.2.0", + "version": "2.3.0", "icons": { "48": "icon-48.png", "128": "icon-128.png" @@ -25,7 +25,6 @@ "permissions": [ "clipboardRead", "clipboardWrite", - "notifications", - "tabs" + "notifications" ] } From a3ec7c31d0dc70e1b2b6610c90bdca2048935c9b Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 22 Feb 2015 21:10:02 -0800 Subject: [PATCH 293/334] Handle commands like "xiwi startxfce4" correctly. It's just a heuristic though. The "xiwi" command isn't really intended for launching DEs. --- chroot-bin/xiwi | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index ead99d946..c6566d95c 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -9,23 +9,32 @@ xiwicmd="`readlink -f "$0"`" if [ "$#" = 0 ]; then - echo "Usage: ${0##*/} application [parameters ...] -Launches a windowed session in Chromium OS for any graphical application." 1>&2 + echo "Usage: ${0##*/} APPLICATION [PARAMETERS ...] +Launches a windowed session in Chromium OS for any graphical application. +All parameters are passed to the specified application. + +A default window manager will full-screen all windows, unless APPLICATION begins +with 'start'. You can cycle through multiple windows inside the application via +Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape-Escape. +If APPLICATION begins with 'start' but you still want to use the default window +manager, specify the full path of the application." 1>&2 exit 2 elif [ "$1" = '/' ]; then shift 1 xsetroot -cursor_name left_ptr - ratpoison -f /dev/stdin >/dev/null </dev/null < Date: Sun, 1 Mar 2015 13:29:06 -0800 Subject: [PATCH 294/334] Xiwi doesn't need croutonfreon.so anymore --- chroot-etc/xserverrc-xiwi | 6 +----- targets/xiwi | 5 ----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index 90b8645c5..e8c1950ab 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -17,11 +17,7 @@ if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi -# Handle Freon systems -if [ ! -f "/sys/class/tty/tty0/active" ]; then - export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" -fi - +# Xorg compatibility hacks export LD_PRELOAD="/usr/local/lib/croutonxorg.so:$LD_PRELOAD" exec /usr/bin/Xorg $XARGS "$@" diff --git a/targets/xiwi b/targets/xiwi index c84a72477..b7ea11de1 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -12,11 +12,6 @@ CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-lo ### Append to prepare.sh: XMETHOD="${XMETHOD:-xiwi}" -# On Freon, we need crazy xorg hacks -if [ ! -f "/sys/class/tty/tty0/active" ]; then - compile freon '-ldl' so -fi - # We need xorg workarounds on all platforms compile xorg '-ldl' so From 550afdb32c048403844dc4ef8cd74e917ff9ee85 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 4 Mar 2015 14:54:26 -0800 Subject: [PATCH 295/334] Patch Xorg to avoid udev probing. A terrible, terrible hack that definitely hopefully maybe fixes #1450 finally. Remove the other hack that didn't completely fix the issue. So I guess that's net zero hacks for this commit, which is nice. --- chroot-etc/xserverrc-xiwi | 3 --- chroot-etc/xserverrc-xorg | 2 -- src/xorg.c | 28 ---------------------------- targets/x11-common | 7 +++++++ targets/xiwi | 3 --- targets/xorg | 3 --- 6 files changed, 7 insertions(+), 39 deletions(-) delete mode 100644 src/xorg.c diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index e8c1950ab..5bdb64c17 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -17,7 +17,4 @@ if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local fi -# Xorg compatibility hacks -export LD_PRELOAD="/usr/local/lib/croutonxorg.so:$LD_PRELOAD" - exec /usr/bin/Xorg $XARGS "$@" diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index d2e2c4e00..4aeb99a7b 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -41,6 +41,4 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then export LD_PRELOAD="/usr/local/lib/croutonfreon.so:$LD_PRELOAD" fi -export LD_PRELOAD="/usr/local/lib/croutonxorg.so:$LD_PRELOAD" - exec "$X" $XARGS "$@" diff --git a/src/xorg.c b/src/xorg.c deleted file mode 100644 index ab4b12793..000000000 --- a/src/xorg.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (c) 2015 The crouton Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * LD_PRELOAD hack to make Xorg happy in a system with the vgem device enabled. - * gcc -shared -fPIC -ldl -Wall -O2 xorg.c -o croutonxorg.so - */ - -#define _GNU_SOURCE -#include -#include - -#define TRACE(...) /* fprintf(stderr, __VA_ARGS__) */ - -static int (*orig_udev_sysname)(void *udev_enumerate, const char *sysname); - -static void preload_init() { - orig_udev_sysname = dlsym(RTLD_NEXT, "udev_enumerate_add_match_sysname"); -} - -int udev_enumerate_add_match_sysname(void *udev_enum, const char *sysname) { - if (!orig_udev_sysname) preload_init(); - TRACE("udev_enumerate_add_match_sysname '%s'\n", sysname); - if (sysname && !strcmp(sysname, "card[0-9]*")) { - sysname = "card0"; - } - return orig_udev_sysname(udev_enum, sysname); -} diff --git a/targets/x11-common b/targets/x11-common index 3e625faf5..0d3eea5db 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -11,6 +11,13 @@ ln -sfT '/etc/crouton/xserverrc' '/etc/X11/xinit/xserverrc' echo "$XMETHOD" > '/etc/crouton/xmethod' +# Modify the Xorg executable to *not* poll udev for cards +offset="`grep -F -m 1 -boa 'card[0-9]*' /usr/bin/Xorg 2>/dev/null || true`" +if [ -n "$offset" ]; then + echo -n 'croutonhax' | dd seek="${offset%:*}" bs=1 of=/usr/bin/Xorg \ + conv=notrunc,nocreat 2>/dev/null +fi + # Install utility for croutoncycle compile wmtools '-lX11' libx11-dev diff --git a/targets/xiwi b/targets/xiwi index b7ea11de1..e246c38c1 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -12,9 +12,6 @@ CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-lo ### Append to prepare.sh: XMETHOD="${XMETHOD:-xiwi}" -# We need xorg workarounds on all platforms -compile xorg '-ldl' so - ltspackages='' # On non-ARM precise, install lts-trusty xorg server for compatibility with xorg # if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) diff --git a/targets/xorg b/targets/xorg index 371565b52..7df83c6e9 100644 --- a/targets/xorg +++ b/targets/xorg @@ -25,9 +25,6 @@ if [ ! -f "/sys/class/tty/tty0/active" ]; then compile freon '-ldl' so fi -# We need xorg workarounds on all platforms -compile xorg '-ldl' so - ltspackages='' # On non-ARM precise, install lts-trusty xorg server to support newer hardware # if kernel version != 3.4 (lts-trusty mesa requires version >=3.6) From 9a13a2f33fb444c7424cf7e7e61e57b079865de0 Mon Sep 17 00:00:00 2001 From: Stephen Barber Date: Mon, 16 Mar 2015 12:55:10 -0700 Subject: [PATCH 296/334] selectively apply vgem hack Only apply the vgem hack/workaround if the vgem device is present. Revert the hack if it's not present. --- targets/x11-common | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/targets/x11-common b/targets/x11-common index 0d3eea5db..4219b561a 100644 --- a/targets/x11-common +++ b/targets/x11-common @@ -11,11 +11,21 @@ ln -sfT '/etc/crouton/xserverrc' '/etc/X11/xinit/xserverrc' echo "$XMETHOD" > '/etc/crouton/xmethod' -# Modify the Xorg executable to *not* poll udev for cards -offset="`grep -F -m 1 -boa 'card[0-9]*' /usr/bin/Xorg 2>/dev/null || true`" -if [ -n "$offset" ]; then - echo -n 'croutonhax' | dd seek="${offset%:*}" bs=1 of=/usr/bin/Xorg \ - conv=notrunc,nocreat 2>/dev/null +# Only apply hack if vgem card exists +if [ -c '/dev/dri/card1' ]; then + # Modify the Xorg executable to *not* poll udev for cards + offset="`grep -F -m 1 -boa 'card[0-9]*' /usr/bin/Xorg 2>/dev/null || true`" + if [ -n "$offset" ]; then + echo -n 'croutonhax' | dd seek="${offset%:*}" bs=1 of=/usr/bin/Xorg \ + conv=notrunc,nocreat 2>/dev/null + fi +elif grep -q 'croutonhax' '/usr/bin/Xorg'; then + # Undo hack since there's no vgem module + offset="`grep -F -m 1 -boa 'croutonhax' /usr/bin/Xorg 2>/dev/null || true`" + if [ -n "$offset" ]; then + echo -n 'card[0-9]*' | dd seek="${offset%:*}" bs=1 of=/usr/bin/Xorg \ + conv=notrunc,nocreat 2>/dev/null + fi fi # Install utility for croutoncycle From 9678b5a590be4e9c1ec3d043b28f5c9f33e3ec6e Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 23 Mar 2015 14:44:20 +0700 Subject: [PATCH 297/334] croutonversion: Add kernel version and freon status to croutonversion So we don't have to keep asking people if /dev/tty0 exists... --- chroot-bin/croutonversion | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chroot-bin/croutonversion b/chroot-bin/croutonversion index 169f31664..08ce30a70 100755 --- a/chroot-bin/croutonversion +++ b/chroot-bin/croutonversion @@ -85,6 +85,12 @@ if [ -z "$CHANGES$DOWNLOAD$UPDATES" ]; then host="`awk -F= '/_RELEASE_DESCRIPTION=/{print $2}' "$hostrel"`" fi echo "host: version ${host:-unknown}" + echo "kernel: $(uname -a)" + freon="yes" + if [ -f /sys/class/tty/tty0/active ]; then + freon="no" + fi + echo "freon: $freon" exit 0 fi From b916dad0508c6854eaf44a7b8d4744dde0af8a47 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 23 Mar 2015 00:45:58 -0700 Subject: [PATCH 298/334] Glory to bots. May they rule the world in their infinite wisdom. please call off the drones... (#1471) From 75f252cceb8eb763d5d60740348c87eea169dd05 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 23 Mar 2015 15:56:53 +0700 Subject: [PATCH 299/334] croutoncycle: Fix croutoncycle display/list with Xephyr Active window was not displayed correctly (disp gets renamed and disp = curdisp comparison fails...). Fixes #1498. --- chroot-bin/croutoncycle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index d8b100e51..a16f3059c 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -168,6 +168,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then fi ) | sort | while read -r line; do disp="${line##* }" + display="${disp%"*"}" line="${line% *}" number='0' active=' ' @@ -177,7 +178,7 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then if [ "${number#[0-9]}" = "$number" ]; then number='0' else - disp=":$number" + display=":$number" line="`getname "$number"`" fi fi @@ -195,11 +196,11 @@ if [ "$cmd" = 'l' -o "$cmd" = 'd' ]; then fi if [ "$line" = 'aura_root_0' ]; then line="$chromiumos" - disp="cros" + display="cros" window='' fi if [ "$cmd" = 'l' ]; then - echo "${disp%"*"}$active $line" + echo "$display$active $line" fi done for disp in $displist; do From 98ba02a018e0402e02150ee8b1d8d298db8c16d3 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Mon, 23 Mar 2015 16:25:45 +0700 Subject: [PATCH 300/334] extension: Test for view.document.readyState instead of document. `document` is the background page, `view.document` is the popup. Should fix #1510. --- host-ext/crouton/background.js | 2 +- host-ext/crouton/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 16263d777..df2170113 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -146,7 +146,7 @@ function refreshUI() { for (var i = 0; i < views.length; views++) { var view = views[i]; /* Make sure page is ready */ - if (document.readyState === "complete") { + if (view.document.readyState === "complete") { /* Update "help" link */ helplink = view.document.getElementById("help"); helplink.onclick = showHelp; diff --git a/host-ext/crouton/manifest.json b/host-ext/crouton/manifest.json index 4a560c928..1c524ba0a 100644 --- a/host-ext/crouton/manifest.json +++ b/host-ext/crouton/manifest.json @@ -4,7 +4,7 @@ "name": "crouton integration", "short_name": "crouton", "description": "Improves integration with crouton chroots.", - "version": "2.3.0", + "version": "2.3.1", "icons": { "48": "icon-48.png", "128": "icon-128.png" From 9e9469f13310854340d20d4b7e34a45de6a33bb2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 25 Mar 2015 17:18:04 -0700 Subject: [PATCH 301/334] Fix X11 auto-selection for Tegra K1. --- targets/x11 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/x11 b/targets/x11 index 5951d67cb..8a3e7a6f0 100644 --- a/targets/x11 +++ b/targets/x11 @@ -4,7 +4,7 @@ # found in the LICENSE file. if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ ([ "${ARCH#arm}" != "$ARCH" ] && \ - awk -F= '/_RELEASE_VERSION=/ { exit int($2) < 6689 }' \ + awk -F= '/_RELEASE_VERSION=/ { exit !(int($2) < 6689) }' \ '/etc/lsb-release'); then # Xorg won't work on Samsung ARM devices or K1 on release less than 6689 REQUIRES='xephyr' From d1087278e9367b2f06ba2207378de700f4ae3eba Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 25 Mar 2015 17:20:03 -0700 Subject: [PATCH 302/334] Handle the eventual move of Samsung Exynos to Freon --- targets/x11 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/targets/x11 b/targets/x11 index 8a3e7a6f0..229342669 100644 --- a/targets/x11 +++ b/targets/x11 @@ -7,7 +7,12 @@ if grep -q 'SAMSUNG EXYNOS' /proc/cpuinfo || \ awk -F= '/_RELEASE_VERSION=/ { exit !(int($2) < 6689) }' \ '/etc/lsb-release'); then # Xorg won't work on Samsung ARM devices or K1 on release less than 6689 - REQUIRES='xephyr' + # But if we're on Freon, we can't use xephyr either, so xiwi's all we have + if [ -f /sbin/frecon ]; then + REQUIRES='xiwi' + else + REQUIRES='xephyr' + fi else REQUIRES='xorg' fi From e8d61eebb63cdc88c42dee9165a04d4c859cb938 Mon Sep 17 00:00:00 2001 From: DennisL Date: Thu, 26 Mar 2015 01:35:46 -0400 Subject: [PATCH 303/334] Add croutonversion Add croutonversion for troubleshooting. --- chroot-bin/croutonxinitrc-wrapper | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 6cdcc4193..1be0a6047 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -43,6 +43,10 @@ else fi # Run crouton-specific commands: + +# Show chroot specifics for troubleshooting +croutonversion + if [ -z "$XMETHOD" ]; then if [ -f '/etc/crouton/xmethod' ]; then read -r XMETHOD _ < /etc/crouton/xmethod From 5bd432f024e6a4c345e6d52be5b764c1ac9db68a Mon Sep 17 00:00:00 2001 From: Mike Kasick Date: Wed, 25 Mar 2015 23:54:26 -0400 Subject: [PATCH 304/334] croutontriggerd: Support use of right-{ctrl,shift,alt} in trigger. --- chroot-bin/croutontriggerd | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/chroot-bin/croutontriggerd b/chroot-bin/croutontriggerd index bdd424acd..0db94a8a5 100755 --- a/chroot-bin/croutontriggerd +++ b/chroot-bin/croutontriggerd @@ -28,6 +28,9 @@ STATE_UP=0 KEY_LEFTCTRL=29 KEY_LEFTALT=56 KEY_LEFTSHIFT=42 +KEY_RIGHTCTRL=97 +KEY_RIGHTALT=100 +KEY_RIGHTSHIFT=54 KEY_F1=59 KEY_F2=60 @@ -75,6 +78,7 @@ while :; do sleep "$EVENT_DEV_POLL" done | unbuffered_awk " function update() { + c = lc || rc; s = ls || rs; a = la || ra if (!cmd && c && s && a && p) { cmd = \"p\" } else if (!cmd && c && s && a && n) { @@ -84,9 +88,12 @@ done | unbuffered_awk " cmd = \"\" } } - $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTCTRL { c = $STATE; update() } - $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTSHIFT { s = $STATE; update() } - $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTALT { a = $STATE; update() } - $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F1 { p = $STATE; update() } - $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F2 { n = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTCTRL { lc = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTSHIFT { ls = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_LEFTALT { la = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTCTRL { rc = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTSHIFT { rs = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_RIGHTALT { ra = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F1 { p = $STATE; update() } + $TYPE == $TYPE_EV_KEY && $KEY == $KEY_F2 { n = $STATE; update() } " From cf2aacba07e65d75c5b1c5484eae235588db1965 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 27 Mar 2015 19:51:06 -0700 Subject: [PATCH 305/334] Update CONTRIBUTORS --- CONTRIBUTORS | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index a7a6169ec..7469fffab 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -12,6 +12,7 @@ John Tantalo Maurice van Kruchten Micah Lee Michael Orr +Mike Kasick Mikito Takada Nevada Romsdahl Nicolas Boichat From 07facb0d8f42d82ed444f038274136687760257f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Mar 2015 15:13:10 -0700 Subject: [PATCH 306/334] xiwi-app: use i3 instead of ratpoison Improves floating window behavior and compatibility with software that requires a reparenting window manager. Window title monitoring is also more efficient. Fixes #1483 --- chroot-bin/xiwi | 60 ++++++++++++++++++++++---------------------- chroot-etc/xiwi.conf | 27 ++++++++++++++++++++ targets/xiwi | 5 ++-- 3 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 chroot-etc/xiwi.conf diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index c6566d95c..cafd6b162 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -15,7 +15,7 @@ All parameters are passed to the specified application. A default window manager will full-screen all windows, unless APPLICATION begins with 'start'. You can cycle through multiple windows inside the application via -Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape-Escape. +Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape. If APPLICATION begins with 'start' but you still want to use the default window manager, specify the full path of the application." 1>&2 exit 2 @@ -23,37 +23,37 @@ elif [ "$1" = '/' ]; then shift 1 xsetroot -cursor_name left_ptr if [ "${1#start}" = "$1" ]; then - ratpoison -f /dev/stdin >/dev/null </dev/null | { + monpid='' + monwid='' + while read _ wid; do + if [ "$wid" = "$monwid" ]; then + continue + fi + if [ -n "$monpid" ]; then + kill "$monpid" 2>/dev/null + fi + monwid="$wid" + xprop -spy -notype -id "$wid" 'WM_NAME' 2>/dev/null | { + while read _ title; do + title="${title%\"}" + title="`cat /etc/crouton/name`/$1: ${title#*\"}" + xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME "$title" + ret="`{ echo -n 'C'; croutoncycle l; } | websocketcommand`" + done + } & + monpid="$!" + done + if [ -n "$monpid" ]; then + kill "$monpid" 2>/dev/null + fi + } & fi exec "$@" -elif [ "$1" = '//' ]; then - # Titlebar update. $2 has the original command name - wininfo="`ratpoison -c 'windows %s%i %t' | sed -n '/^\*/s/^.//p'`" - id="${wininfo%% *}" - title="`cat /etc/crouton/name`/$2: ${wininfo#* }" - xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME "$title" - ret="`{ echo -n 'C'; croutoncycle l; } | websocketcommand`" - if [ "$ret" != 'COK' ]; then - error 1 "${ret#?}" - fi - # Launch titlebar monitoring daemon (only need one per window per display) - { - flock -n 3 || exit 0 - xprop -spy -notype -id "$id" '_NET_WM_NAME' 2>/dev/null \ - | while read _; do - "$xiwicmd" // "$2" - done - } 3>> "/tmp/crouton-lock/xiwi-$DISPLAY-$id" & else export XMETHOD=xiwi exec /usr/local/bin/xinit "$xiwicmd" / "$@" diff --git a/chroot-etc/xiwi.conf b/chroot-etc/xiwi.conf new file mode 100644 index 000000000..6adba9040 --- /dev/null +++ b/chroot-etc/xiwi.conf @@ -0,0 +1,27 @@ +# i3 config file (v4) +# Copyright (c) 2014 The crouton Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Style +new_window none +new_float normal +workspace_layout tabbed +font pango:Sans 8 + +# Colors border backgr. text indicator +client.focused #8E8E8F #EAEAEB #000000 #8E8E8F +client.focused_inactive #8E8E8F #CACACB #525252 #8E8E8F +client.unfocused #8E8E8F #CACACB #525252 #8E8E8F +client.urgent #FF8E8E #FFCACB #520000 #FF8E8E +client.background #C3C3C4 + +# Interaction +focus_follows_mouse no +bindsym Mod1+Shift+Control+Escape kill +floating_modifier Mod1 +bindsym Mod1+Tab focus right +bindsym Mod1+Shift+Tab focus left +bindsym Mod1+Control+Tab focus right +bindsym Mod1+Shift+Control+Tab focus left +bindsym --release button2 kill diff --git a/targets/xiwi b/targets/xiwi index e246c38c1..fae487c1d 100644 --- a/targets/xiwi +++ b/targets/xiwi @@ -6,7 +6,7 @@ REQUIRES='audio extension' PROVIDES='x11' DESCRIPTION='X.org X11 backend running unaccelerated in a Chromium OS window.' CHROOTBIN='croutoncycle croutonfindnacl croutontriggerd croutonxinitrc-wrapper setres xinit xiwi' -CHROOTETC='xbindkeysrc.scm xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-local.example' +CHROOTETC='xbindkeysrc.scm xiwi.conf xorg-dummy.conf xserverrc xserverrc-xiwi xserverrc-local.example' . "${TARGETSDIR:="$PWD"}/common" ### Append to prepare.sh: @@ -27,7 +27,8 @@ if [ "${DISTROAKA:-"$DISTRO"}" = 'debian' ]; then apt-mark unhold xserver-xorg-video-dummy$ltspackages || true 2>/dev/null fi -install xorg xserver-xorg-video-dummy$ltspackages ratpoison +install xorg xserver-xorg-video-dummy$ltspackages +install --minimal i3 # Compile croutonfbserver compile fbserver '-lX11 -lXfixes -lXdamage -lXext -lXtst' \ From 8c200a9f828c6fa853ecabe4fe8a2a2c8d8fc3e1 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 26 Mar 2015 15:33:55 -0700 Subject: [PATCH 307/334] xiwi: Minor cleanup --- chroot-bin/xiwi | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index cafd6b162..59ec595e6 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -28,6 +28,7 @@ elif [ "$1" = '/' ]; then xprop -spy -root | grep -q _NET_ACTIVE_WINDOW # Launch the window title monitoring daemon xprop -spy -notype -root 0i ' $0\n' '_NET_ACTIVE_WINDOW' 2>/dev/null | { + name="`cat /etc/crouton/name`" monpid='' monwid='' while read _ wid; do @@ -41,9 +42,12 @@ elif [ "$1" = '/' ]; then xprop -spy -notype -id "$wid" 'WM_NAME' 2>/dev/null | { while read _ title; do title="${title%\"}" - title="`cat /etc/crouton/name`/$1: ${title#*\"}" - xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME "$title" - ret="`{ echo -n 'C'; croutoncycle l; } | websocketcommand`" + xprop -root -f CROUTON_NAME 8s -set CROUTON_NAME \ + "$name/$1: ${title#*\"}" + { + echo -n 'C' + croutoncycle l + } | websocketcommand >/dev/null done } & monpid="$!" From e844948d7b4524c7e5856c5f557da911fa415949 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 27 Mar 2015 20:05:14 -0700 Subject: [PATCH 308/334] Allow for user-specific init scripts for xiwi apps. Useful for setting things like keyboard layouts. --- chroot-bin/xiwi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 59ec595e6..2152616b5 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -56,6 +56,10 @@ elif [ "$1" = '/' ]; then kill "$monpid" 2>/dev/null fi } & + # Launch user init scripts + if [ -f "$HOME/.xiwirc" ]; then + /bin/sh "$HOME/.xiwirc" || true + fi fi exec "$@" else From f94a27436e2d758421f2f6e43fda6fb41bf6930d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Fri, 27 Mar 2015 20:52:52 -0700 Subject: [PATCH 309/334] Add -f to xiwi to force xiwi to remain open If your gui app *must* fork, you can use this. You'll get a better experience if you can tell the app not to fork though... Fixes #1538 --- chroot-bin/xiwi | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 2152616b5..900d71f82 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -9,10 +9,16 @@ xiwicmd="`readlink -f "$0"`" if [ "$#" = 0 ]; then - echo "Usage: ${0##*/} APPLICATION [PARAMETERS ...] + echo "Usage: ${0##*/} [-f] APPLICATION [PARAMETERS ...] Launches a windowed session in Chromium OS for any graphical application. All parameters are passed to the specified application. +xiwi will normally close when the application returns. Some gui applications +fork before or during normal operation, which can confuse xiwi and cause it to +quit prematurely. If your application does not have a parameter that prevents +it from forking, you can use -f to prevent xiwi from quitting automatically. +You will need to Ctrl-C xiwi in the terminal to make it quit. + A default window manager will full-screen all windows, unless APPLICATION begins with 'start'. You can cycle through multiple windows inside the application via Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape. @@ -21,6 +27,11 @@ manager, specify the full path of the application." 1>&2 exit 2 elif [ "$1" = '/' ]; then shift 1 + forever='0' + if [ "$1" = '-f' ]; then + forever='infinity' + shift 1 + fi xsetroot -cursor_name left_ptr if [ "${1#start}" = "$1" ]; then i3 -c "/etc/crouton/xiwi.conf" & @@ -61,7 +72,8 @@ elif [ "$1" = '/' ]; then /bin/sh "$HOME/.xiwirc" || true fi fi - exec "$@" + "$@" + exec sleep "$forever" else export XMETHOD=xiwi exec /usr/local/bin/xinit "$xiwicmd" / "$@" From a7433319afa8339e58be8480e497b2b2b604a46d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 30 Mar 2015 21:48:06 -0700 Subject: [PATCH 310/334] Improve error logging in fbserver.c --- src/fbserver.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/fbserver.c b/src/fbserver.c index 62ee7c791..baf5551c6 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -99,6 +99,14 @@ void kb_release_all() { /* X11-related functions */ static int xerror_handler(Display *dpy, XErrorEvent *e) { + if (verbose < 1) + return 0; + char msg[64] = {0}; + char op[32] = {0}; + sprintf(msg, "%d", e->request_code); + XGetErrorDatabaseText(dpy, "XRequest", msg, "", op, sizeof(op)); + XGetErrorText(dpy, e->error_code, msg, sizeof(msg)); + error("%s (%s)", msg, op); return 0; } @@ -378,9 +386,11 @@ int write_image(const struct screen* screen) { reply->cursor_updated = 0; while (XCheckTypedEvent(dpy, fixesEvent + XFixesCursorNotify, &ev)) { XFixesCursorNotifyEvent* curev = (XFixesCursorNotifyEvent*)&ev; - char* name = XGetAtomName(dpy, curev->cursor_name); - log(2, "cursor! %ld %s", curev->cursor_serial, name); - XFree(name); + if (verbose >= 2) { + char* name = XGetAtomName(dpy, curev->cursor_name); + log(2, "cursor! %ld %s", curev->cursor_serial, name); + XFree(name); + } reply->cursor_updated = 1; reply->cursor_serial = curev->cursor_serial; } From 0cdb49af55d078da84646ee66402f78ef2f734ea Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 30 Mar 2015 21:48:31 -0700 Subject: [PATCH 311/334] Add CROUTON_CONNECTED property for xiwi displays --- src/fbserver.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/fbserver.c b/src/fbserver.c index baf5551c6..c555c036f 100644 --- a/src/fbserver.c +++ b/src/fbserver.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,19 @@ static int xerror_handler(Display *dpy, XErrorEvent *e) { return 0; } +/* Sets the CROUTON_CONNECTED property for the root window */ +static void set_connected(Display *dpy, uint8_t connected) { + Window root = DefaultRootWindow(dpy); + Atom prop = XInternAtom(dpy, "CROUTON_CONNECTED", False); + if (prop == None) { + error("Unable to get atom"); + return; + } + XChangeProperty(dpy, root, prop, XA_INTEGER, 8, PropModeReplace, + &connected, 1); + XFlush(dpy); +} + /* Registers XDamage events for a given Window. */ static void register_damage(Display *dpy, Window win) { XWindowAttributes attrib; @@ -524,7 +538,9 @@ int main(int argc, char** argv) { int length; while (1) { + set_connected(dpy, False); socket_server_accept(VERSION); + set_connected(dpy, True); while (1) { length = socket_client_read_frame((char*)buffer, sizeof(buffer)); if (length < 0) { From 21bf27cf2fd03401c263094cfe7276a738def3f6 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 30 Mar 2015 22:24:39 -0700 Subject: [PATCH 312/334] xiwi -f: close when the window is closed if nothing is shown --- chroot-bin/xiwi | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 900d71f82..5653c6193 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -17,7 +17,7 @@ xiwi will normally close when the application returns. Some gui applications fork before or during normal operation, which can confuse xiwi and cause it to quit prematurely. If your application does not have a parameter that prevents it from forking, you can use -f to prevent xiwi from quitting automatically. -You will need to Ctrl-C xiwi in the terminal to make it quit. +xiwi will quit if you close the Chromium OS window when nothing is displayed. A default window manager will full-screen all windows, unless APPLICATION begins with 'start'. You can cycle through multiple windows inside the application via @@ -27,9 +27,9 @@ manager, specify the full path of the application." 1>&2 exit 2 elif [ "$1" = '/' ]; then shift 1 - forever='0' + foreground='' if [ "$1" = '-f' ]; then - forever='infinity' + foreground="y" shift 1 fi xsetroot -cursor_name left_ptr @@ -38,6 +38,8 @@ elif [ "$1" = '/' ]; then # Wait for i3 to launch xprop -spy -root | grep -q _NET_ACTIVE_WINDOW # Launch the window title monitoring daemon + # _NET_ACTIVE_WINDOW is more reliable than _NET_CLIENT_LIST_STACKING for + # keeping track of the topmost window. xprop -spy -notype -root 0i ' $0\n' '_NET_ACTIVE_WINDOW' 2>/dev/null | { name="`cat /etc/crouton/name`" monpid='' @@ -73,7 +75,21 @@ elif [ "$1" = '/' ]; then fi fi "$@" - exec sleep "$forever" + if [ -n "$foreground" ]; then + xprop -spy -notype -root 0i ' $0\n' 'CROUTON_CONNECTED' \ + | while read _ connected; do + if [ "$connected" != 0 ]; then + continue + fi + # _NET_CLIENT_LIST_STACKING is more reliable than + # _NET_ACTIVE_WINDOW for detecting when no windows exist + if ! xprop -notype -root '_NET_CLIENT_LIST_STACKING' \ + | grep -q '0x'; then + kill "$$" + break + fi + done + fi else export XMETHOD=xiwi exec /usr/local/bin/xinit "$xiwicmd" / "$@" From 8ee31495a1451830c5899bf57eecfa8366a11deb Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 11:47:37 -0700 Subject: [PATCH 313/334] Recursively autobuild the extension pnacl --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index db13ce369..7dab818c6 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ SCRIPTS := \ $(wildcard installer/*/*) \ $(wildcard src/*) \ $(wildcard targets/*) +EXTPEXE = host-ext/crouton/kiwi.pexe +EXTPEXESOURCES = $(wildcard host-ext/nacl_src/*.h) \ + $(wildcard host-ext/nacl_src/*.cc) EXTSOURCES = $(wildcard host-ext/crouton/*) GENVERSION = build/genversion.sh CONTRIBUTORSSED = build/CONTRIBUTORS.sed @@ -56,6 +59,9 @@ $(TARGET): $(WRAPPER) $(SCRIPTS) $(GENVERSION) $(GITHEAD) Makefile $(EXTTARGET): $(EXTSOURCES) Makefile rm -f $(EXTTARGET) && zip -q --junk-paths $(EXTTARGET) $(EXTSOURCES) +$(EXTPEXE): $(EXTPEXESOURCES) + $(MAKE) -C host-ext/nacl_src + $(SRCTARGETS): src/$(patsubst crouton%,src/%.c,$@) $($@_DEPS) Makefile gcc $(CFLAGS) $(patsubst crouton%,src/%.c,$@) $($@_LIBS) -o $@ From a9f5bfd017a2ca4e2062d10a5b3d798a9e069264 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 13:11:49 -0700 Subject: [PATCH 314/334] Support parameters in XMETHOD Syntax is xiwi-flag-var=val --- chroot-bin/croutoncycle | 4 ++-- chroot-bin/croutonxinitrc-wrapper | 8 +++++--- chroot-bin/setres | 2 +- chroot-etc/xserverrc | 2 +- chroot-etc/xserverrc-xiwi | 4 +++- chroot-etc/xserverrc-xorg | 4 +++- host-bin/enter-chroot | 4 ++-- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index a16f3059c..420a9f563 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -88,7 +88,7 @@ for disp in /tmp/.X*-lock; do | grep -q 'INTEGER'; then displist="$displist $disp" elif DISPLAY="$disp" xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ - | grep -q '= "xiwi"$'; then + | grep -q '= "xiwi'; then displist="$displist $disp" xiwiactive='y' fi @@ -304,7 +304,7 @@ if [ "${destdisp#:}" = "$destdisp" ]; then fi else export DISPLAY="$destdisp" - if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiwi"$'; then + if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiwi'; then if [ -z "$freonowner" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 diff --git a/chroot-bin/croutonxinitrc-wrapper b/chroot-bin/croutonxinitrc-wrapper index 1be0a6047..7f5d77a1f 100755 --- a/chroot-bin/croutonxinitrc-wrapper +++ b/chroot-bin/croutonxinitrc-wrapper @@ -56,6 +56,8 @@ if [ -z "$XMETHOD" ]; then exit 1 fi fi +xmethodtype="${XMETHOD%%-*}" +xmethodargs="${XMETHOD#*-}" # Record the name of the chroot in the root window properties if [ -f '/etc/crouton/name' ] && hash xprop 2>/dev/null; then @@ -74,7 +76,7 @@ if hash croutonclip 2>/dev/null; then fi # Pass through the host cursor and correct mousewheels on xephyr -if [ "$XMETHOD" = 'xephyr' ]; then +if [ "$xmethodtype" = 'xephyr' ]; then host-x11 croutoncursor "$DISPLAY" & if [ -z "$CROUTON_WHEEL_PARAMS" -a -r "$HOME/.croutonwheel" ]; then CROUTON_WHEEL_PARAMS="`head -n1 "$HOME/.croutonwheel"`" @@ -86,7 +88,7 @@ fi croutontriggerd & # Input-related stuff is not needed for kiwi -if [ "$XMETHOD" != "xiwi" ]; then +if [ "$xmethodtype" != "xiwi" ]; then # Apply the Chromebook keyboard map if installed. if [ -f '/usr/share/X11/xkb/compat/chromebook' ]; then setxkbmap -model chromebook @@ -125,7 +127,7 @@ if [ "$XMETHOD" != "xiwi" ]; then fi # Crouton-in-a-tab: Start fbserver and launch display -if [ "$XMETHOD" = 'xiwi' ]; then +if [ "$xmethodtype" = 'xiwi' ]; then # The extension sends evdev key codes: fix the keyboard mapping rules setxkbmap -rules evdev # Reapply xkb map: This fixes autorepeat mask in "xset q" diff --git a/chroot-bin/setres b/chroot-bin/setres index bfcb3688d..ce100ee07 100755 --- a/chroot-bin/setres +++ b/chroot-bin/setres @@ -27,7 +27,7 @@ fi xmethod="`xprop -root 'CROUTON_XMETHOD' | sed -n 's/^.*\"\(.*\)\"/\1/p'`" -if [ "$xmethod" != "xiwi" ]; then +if [ "${xmethod%%-*}" != "xiwi" ]; then cvt "$x" "$y" "$r" | { read -r _ read -r _ mode data diff --git a/chroot-etc/xserverrc b/chroot-etc/xserverrc index 545ddfbe1..eb3e1eed5 100644 --- a/chroot-etc/xserverrc +++ b/chroot-etc/xserverrc @@ -12,7 +12,7 @@ if [ -z "$XMETHOD" ]; then fi fi -xserverrc="/etc/crouton/xserverrc-$XMETHOD" +xserverrc="/etc/crouton/xserverrc-${XMETHOD%%-*}" if [ "${XMETHOD##*/}" != "$XMETHOD" -o ! -f "$xserverrc" ]; then echo "Invalid X11 backend '$XMETHOD'" 1>&2 exit 2 diff --git a/chroot-etc/xserverrc-xiwi b/chroot-etc/xserverrc-xiwi index 5bdb64c17..8932d9192 100644 --- a/chroot-etc/xserverrc-xiwi +++ b/chroot-etc/xserverrc-xiwi @@ -11,7 +11,9 @@ for arg in "$@"; do fi done -export XMETHOD='xiwi' +if [ "${XMETHOD%%-*}" != 'xiwi' ]; then + export XMETHOD='xiwi' +fi XARGS="-nolisten tcp -config xorg-dummy.conf -logfile $logfile" if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/chroot-etc/xserverrc-xorg b/chroot-etc/xserverrc-xorg index 4aeb99a7b..c0fc926ae 100644 --- a/chroot-etc/xserverrc-xorg +++ b/chroot-etc/xserverrc-xorg @@ -3,7 +3,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -export XMETHOD='xorg' +if [ "${XMETHOD%%-*}" != 'xorg' ]; then + export XMETHOD='xorg' +fi XARGS='-nolisten tcp' if [ -f /etc/crouton/xserverrc-local ]; then . /etc/crouton/xserverrc-local diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index da26b45a0..e279f09e7 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -138,8 +138,8 @@ fi # Check to ensure that the XMETHOD requested has been installed if [ -n "$TMPXMETHOD" ]; then - if ! grep -q "^$TMPXMETHOD$" "$CHROOTS/$NAME/.crouton-targets" 2>/dev/null; then - error 1 "$CHROOTS/$NAME does not contain XMETHOD '$TMPXMETHOD'" + if ! grep -q "^$TMPXMETHOD\(-\|\$\)" "$CHROOTS/$NAME/.crouton-targets"; then + error 1 "$CHROOTS/$NAME does not contain XMETHOD '${TMPXMETHOD%%-*}'" fi fi From 3517fd806c1eaceb4308bf158f299d653c275f03 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 1 Mar 2015 00:53:12 -0800 Subject: [PATCH 315/334] Support extra parameters when launching xiwi windows. Currently, full-screen/window is supported. --- host-ext/crouton/background.js | 15 ++++++++++++--- host-ext/crouton/window.js | 6 ++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index df2170113..e36117835 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -491,8 +491,16 @@ function websocketMessage(evt) { break; case 'X': /* Ask to open a crouton window */ display = payload - match = display.match(/^:([0-9]+)$/) + match = display.match(/^:([0-9]+)([- ][^- ]*)*$/) displaynum = match ? match[1] : null + if (displaynum) { + display = ":" + displaynum; + } + mode = (match.length > 2 && match[2].length >= 2) ? match[2][1] : 'f' + if (mode != 'f' && mode != 'w') { + console.log('invalid xiwi mode: ' + mode); + mode = 'f'; + } if (!displaynum) { /* Minimize all kiwi windows */ var disps = Object.keys(kiwi_win_); @@ -520,7 +528,7 @@ function websocketMessage(evt) { !kiwi_win_[display].window.closing)) { /* focus/full screen an existing window */ var winid = kiwi_win_[display].id; - chrome.windows.update(winid, {focused: true}); + chrome.windows.update(winid, {'focused': true}); chrome.windows.get(winid, function(win) { if (win.state == "maximized") chrome.windows.update(winid, {'state': 'fullscreen'}, @@ -538,7 +546,8 @@ function websocketMessage(evt) { chrome.windows.create({ 'url': "window.html?display=" + displaynum + "&debug=" + (debug_ ? 1 : 0) + "&hidpi=" + (hidpi_ ? 1 : 0) + - "&title=" + encodeURIComponent(name), + "&title=" + encodeURIComponent(name) + + "&mode=" + mode, 'type': "popup" }, function(newwin) { kiwi_win_[display].id = newwin.id; diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 8489b5f93..48811e47b 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -262,8 +262,10 @@ function handleFocusBlur(evt) { } /* Start in full screen */ -chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': "fullscreen"}, function(win) {}) +if (location.search.search(/[&?]mode=f(&|$)/i) != -1) { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {'state': "fullscreen"}, function(win) {}) +} document.addEventListener('DOMContentLoaded', function() { listener_ = document.getElementById('listener'); From 66f4a55a9dbbf68b5e080fc27635d062f6d433f1 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 13:28:46 -0700 Subject: [PATCH 316/334] Pass xiwi args to the extension --- chroot-bin/croutoncycle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chroot-bin/croutoncycle b/chroot-bin/croutoncycle index 420a9f563..50a0576bf 100755 --- a/chroot-bin/croutoncycle +++ b/chroot-bin/croutoncycle @@ -304,7 +304,9 @@ if [ "${destdisp#:}" = "$destdisp" ]; then fi else export DISPLAY="$destdisp" - if xprop -root 'CROUTON_XMETHOD' 2>/dev/null | grep -q '= "xiwi'; then + xmethod="`xprop -root 'CROUTON_XMETHOD' 2>/dev/null \ + | sed -n 's/^.*\"\(.*\)\"/\1/p'`" + if [ "${xmethod%%-*}" = 'xiwi' ]; then if [ -z "$freonowner" -a "$tty" != 'tty1' ]; then sudo -n chvt 1 sleep .1 @@ -314,7 +316,7 @@ else if [ -z "$freonowner" ]; then host-x11 croutonwmtools raise "$aurawin" fi - STATUS="`echo -n "X${destdisp}" | websocketcommand`" + STATUS="`echo -n "X${destdisp} ${xmethod#*-}" | websocketcommand`" if [ "$STATUS" != 'XOK' ]; then error 1 "${STATUS#?}" fi From 82d1ed1ef67353c238e3bf835a5984e91c9629f6 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 14:20:34 -0700 Subject: [PATCH 317/334] xiwi: start in windowed mode unless -F is supplied. Add -T although it doesn't work yet. --- chroot-bin/xiwi | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/chroot-bin/xiwi b/chroot-bin/xiwi index 5653c6193..573d565b3 100755 --- a/chroot-bin/xiwi +++ b/chroot-bin/xiwi @@ -5,14 +5,13 @@ # Runs the specified X11 application in its own X server in Chromium OS. -. "`dirname "$0"`/../installer/functions" -xiwicmd="`readlink -f "$0"`" - -if [ "$#" = 0 ]; then - echo "Usage: ${0##*/} [-f] APPLICATION [PARAMETERS ...] +USAGE="Usage: ${0##*/} [-f] [-F|-T] APPLICATION [PARAMETERS ...] Launches a windowed session in Chromium OS for any graphical application. All parameters are passed to the specified application. +By default, the app is launched in a window. Passing -F will launch the app +full-screen, and passing -T will launch the app in a tab. + xiwi will normally close when the application returns. Some gui applications fork before or during normal operation, which can confuse xiwi and cause it to quit prematurely. If your application does not have a parameter that prevents @@ -20,20 +19,30 @@ it from forking, you can use -f to prevent xiwi from quitting automatically. xiwi will quit if you close the Chromium OS window when nothing is displayed. A default window manager will full-screen all windows, unless APPLICATION begins -with 'start'. You can cycle through multiple windows inside the application via -Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via Ctrl-Alt-Shift-Escape. -If APPLICATION begins with 'start' but you still want to use the default window -manager, specify the full path of the application." 1>&2 - exit 2 +with 'start' or is 'xinit'. You can cycle through multiple windows inside the +application via Ctrl-Alt-Tab/Ctrl-Alt-Shift-Tab, or close them via +Ctrl-Alt-Shift-Escape. If APPLICATION begins with 'start' but you still want to +use the default window manager, specify the full path of the application." + +. "`dirname "$0"`/../installer/functions" +xiwicmd="`readlink -f "$0"`" +OPTSTRING='FfTt' + +if [ "$#" = 0 ]; then + error 2 "$USAGE" elif [ "$1" = '/' ]; then shift 1 foreground='' - if [ "$1" = '-f' ]; then - foreground="y" - shift 1 - fi + while getopts "$OPTSTRING" f; do + case "$f" in + f) foreground='y';; + t|T|F) :;; + \?) error 2 "$USAGE";; + esac + done + shift "$((OPTIND-1))" xsetroot -cursor_name left_ptr - if [ "${1#start}" = "$1" ]; then + if [ "$1" != 'xinit' -a "${1#start}" = "$1" ]; then i3 -c "/etc/crouton/xiwi.conf" & # Wait for i3 to launch xprop -spy -root | grep -q _NET_ACTIVE_WINDOW @@ -91,6 +100,14 @@ elif [ "$1" = '/' ]; then done fi else - export XMETHOD=xiwi + export XMETHOD='xiwi-window' + while getopts "$OPTSTRING" f; do + case "$f" in + f) :;; + F) export XMETHOD='xiwi-fullscreen';; + t|T) export XMETHOD='xiwi-tab';; + \?) error 2 "$USAGE";; + esac + done exec /usr/local/bin/xinit "$xiwicmd" / "$@" fi From 8421b0a48d963f120f8620f835b63647bc4cb404 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 18:30:08 -0700 Subject: [PATCH 318/334] Remove unnecessary quotes on object definitions. --- host-ext/crouton/background.js | 14 +++++++------- host-ext/crouton/window.js | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index e36117835..bcb4c9e86 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -138,7 +138,7 @@ function refreshUI() { icon = "disconnected"; chrome.browserAction.setIcon( - {path: {'19': icon + '-19.png', '38': icon + '-38.png'}} + {path: {19: icon + '-19.png', 38: icon + '-38.png'}} ); chrome.browserAction.setTitle({title: 'crouton: ' + icon}); @@ -510,14 +510,14 @@ function websocketMessage(evt) { minimize = function(win) { chrome.windows.update(winid, - {'state': 'minimized'}, function(win) {})} + {state: 'minimized'}, function(win) {})} chrome.windows.get(winid, function(win) { /* To make restore nicer, first exit full screen, * then minimize */ if (win.state == "fullscreen") { chrome.windows.update(winid, - {'state': 'maximized'}, minimize) + {state: 'maximized'}, minimize) } else { minimize() } @@ -528,10 +528,10 @@ function websocketMessage(evt) { !kiwi_win_[display].window.closing)) { /* focus/full screen an existing window */ var winid = kiwi_win_[display].id; - chrome.windows.update(winid, {'focused': true}); + chrome.windows.update(winid, {focused: true}); chrome.windows.get(winid, function(win) { if (win.state == "maximized") - chrome.windows.update(winid, {'state': 'fullscreen'}, + chrome.windows.update(winid, {state: 'fullscreen'}, function(win) {}) }) } else { @@ -543,12 +543,12 @@ function websocketMessage(evt) { win = windows_.filter(function(x){ return x.display == display })[0] name = win ? win.name : "crouton in a window"; - chrome.windows.create({ 'url': "window.html?display=" + displaynum + + chrome.windows.create({ url: "window.html?display=" + displaynum + "&debug=" + (debug_ ? 1 : 0) + "&hidpi=" + (hidpi_ ? 1 : 0) + "&title=" + encodeURIComponent(name) + "&mode=" + mode, - 'type': "popup" }, + type: "popup" }, function(newwin) { kiwi_win_[display].id = newwin.id; focus_win_ = display; diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 48811e47b..1134b8089 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -180,18 +180,18 @@ function handleMessage(message) { newstate = "fullscreen"; } chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': newstate}, function(win) {}) + {state: newstate}, function(win) {}) }) } else if (type == "state" && payload == "hide") { /* Hide window */ chrome.windows.getCurrent(function(win) { minimize = function(win) { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': 'minimized'}, function(win) {})} + {state: 'minimized'}, function(win) {})} /* To make restore nicer, first exit full screen, then minimize */ if (win.state == "fullscreen") { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': 'maximized'}, minimize) + {state: 'maximized'}, minimize) } else { minimize() } @@ -264,7 +264,7 @@ function handleFocusBlur(evt) { /* Start in full screen */ if (location.search.search(/[&?]mode=f(&|$)/i) != -1) { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {'state': "fullscreen"}, function(win) {}) + {state: "fullscreen"}, function(win) {}) } document.addEventListener('DOMContentLoaded', function() { From a684c8c3fc6fa1cda6032f4a912f65d10022d02a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 31 Mar 2015 20:08:48 -0700 Subject: [PATCH 319/334] Tab support! --- host-ext/crouton/background.js | 140 ++++++++++++++++++++++----------- host-ext/crouton/window.js | 10 ++- 2 files changed, 103 insertions(+), 47 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index bcb4c9e86..695ebf94f 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -42,7 +42,7 @@ var logger_ = []; /* Array of status messages: [LogLevel, time, message] */ var windows_ = []; /* Array of windows. (.display, .name) */ var kiwi_win_ = {}; /* Map of kiwi windows. Key is display, value is object - (.id, .window: window element) */ + (.id, .isTab, .window: window element) */ var focus_win_ = -1; /* Focused kiwi window. -1 if no kiwi window focused. */ var notifications_ = {}; /* Map of notification id to function to be called when @@ -181,8 +181,15 @@ function refreshUI() { refreshUI(); var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (kiwi_win_[disps[i]].window) - kiwi_win_[disps[i]].window.setDebug(debug_?1:0); + var win = kiwi_win_[disps[i]]; + if (win.window) { + if (win.isTab) { + chrome.tabs.sendMessage(win.id, + {func: 'setDebug', param: debug_?1:0}); + } else { + win.window.setDebug(debug_?1:0); + } + } } } debugcheck.checked = debug_; @@ -195,8 +202,15 @@ function refreshUI() { refreshUI(); var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (kiwi_win_[disps[i]].window) - kiwi_win_[disps[i]].window.setHiDPI(hidpi_?1:0); + var win = kiwi_win_[disps[i]]; + if (win.window) { + if (win.isTab) { + chrome.tabs.sendMessage(win.id, + {func: 'setHiDPI', param: hidpi_?1:0}); + } else { + win.window.setHiDPI(hidpi_?1:0); + } + } } } hidpicheck.disabled = false; @@ -274,9 +288,15 @@ function clipboardStart() { LogLevel.INFO); setStatus("Started...", false); - /* Monitor window focus changes/removals and report to croutonclip */ - chrome.windows.onFocusChanged.addListener(windowFocusChanged) - chrome.windows.onRemoved.addListener(windowRemoved) + /* Monitor window/tab focus changes/removals and report to croutonclip */ + chrome.windows.onFocusChanged.addListener( + function(id) { onFocusChanged(id, false); }); + chrome.windows.onRemoved.addListener( + function(id) { onRemoved(id, false); }); + chrome.tabs.onActivated.addListener( + function(data) { onFocusChanged(data.tabId, true); }); + chrome.tabs.onRemoved.addListener( + function(id, data) { onRemoved(id, true); }); clipboardholder_ = document.getElementById("clipboardholder"); @@ -479,8 +499,14 @@ function websocketMessage(evt) { ).filter( function(x) { return !!x; } ) windows_.forEach(function(k) { - if (kiwi_win_[k.display] && kiwi_win_[k.display].window) { - kiwi_win_[k.display].window.setTitle(k.name); + var win = kiwi_win_[k.display]; + if (win && win.window) { + if (win.isTab) { + chrome.tabs.sendMessage(win.id, + {func: 'setTitle', param: k.name}); + } else { + win.window.setTitle(k.name); + } } }) @@ -492,34 +518,37 @@ function websocketMessage(evt) { case 'X': /* Ask to open a crouton window */ display = payload match = display.match(/^:([0-9]+)([- ][^- ]*)*$/) - displaynum = match ? match[1] : null + displaynum = match ? match[1] : null; + mode = null; if (displaynum) { display = ":" + displaynum; - } - mode = (match.length > 2 && match[2].length >= 2) ? match[2][1] : 'f' - if (mode != 'f' && mode != 'w') { - console.log('invalid xiwi mode: ' + mode); - mode = 'f'; + mode = match[2].length >= 2 ? match[2].charAt(1) : 'f'; + if ('fwt'.indexOf(mode) == -1) { + console.log('invalid xiwi mode: ' + mode); + mode = 'f'; + } } if (!displaynum) { /* Minimize all kiwi windows */ var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { + if (kiwi_win_[disps[i]].isTab) { + continue; + } var winid = kiwi_win_[disps[i]].id; chrome.windows.update(winid, {focused: false}); minimize = function(win) { - chrome.windows.update(winid, - {state: 'minimized'}, function(win) {})} + chrome.windows.update(winid, {state: 'minimized'}); }; chrome.windows.get(winid, function(win) { /* To make restore nicer, first exit full screen, * then minimize */ if (win.state == "fullscreen") { - chrome.windows.update(winid, - {state: 'maximized'}, minimize) + chrome.windows.update(winid, {state: 'maximized'}, + minimize); } else { - minimize() + minimize(); } }) } @@ -528,33 +557,49 @@ function websocketMessage(evt) { !kiwi_win_[display].window.closing)) { /* focus/full screen an existing window */ var winid = kiwi_win_[display].id; - chrome.windows.update(winid, {focused: true}); - chrome.windows.get(winid, function(win) { - if (win.state == "maximized") - chrome.windows.update(winid, {state: 'fullscreen'}, - function(win) {}) - }) + if (kiwi_win_[display].isTab) { + chrome.tabs.update(winid, {active: true}); + chrome.tabs.get(winid, function(tab) { + chrome.windows.update(tab.windowId, {focused: true}); + }); + } else { + chrome.windows.update(winid, {focused: true}); + chrome.windows.get(winid, function(win) { + if (win.state == "maximized") + chrome.windows.update(winid, {state: 'fullscreen'}); + }); + } } else { /* Open a new window */ kiwi_win_[display] = new Object(); kiwi_win_[display].id = -1; + kiwi_win_[display].isTab = mode == 't'; kiwi_win_[display].window = null; - win = windows_.filter(function(x){ return x.display == display })[0] + win = windows_.filter(function(x){return x.display == display})[0]; name = win ? win.name : "crouton in a window"; + create = chrome.windows.create; + data = {}; + + if (kiwi_win_[display].isTab) { + name = win ? win.name : "crouton in a tab"; + create = chrome.tabs.create; + } else { + data['type'] = "popup"; + } - chrome.windows.create({ url: "window.html?display=" + displaynum + - "&debug=" + (debug_ ? 1 : 0) + - "&hidpi=" + (hidpi_ ? 1 : 0) + - "&title=" + encodeURIComponent(name) + - "&mode=" + mode, - type: "popup" }, - function(newwin) { - kiwi_win_[display].id = newwin.id; - focus_win_ = display; - if (active_ && sversion_ >= 2) - websocket_.send("Cs" + focus_win_); - }); + data['url'] = "window.html?display=" + displaynum + + "&debug=" + (debug_ ? 1 : 0) + + "&hidpi=" + (hidpi_ ? 1 : 0) + + "&title=" + encodeURIComponent(name) + + "&mode=" + mode; + + create(data, function(newwin) { + kiwi_win_[display].id = newwin.id; + focus_win_ = display; + if (active_ && sversion_ >= 2) + websocket_.send("Cs" + focus_win_); + }); } websocket_.send("XOK"); closePopup(); @@ -598,13 +643,14 @@ function websocketClose() { checkUpdate(false); } -/* Called when window in focus changes: feeback to the extension so the +/* Called when window/tab in focus changes: feeback to the extension so the * clipboard can be transfered. */ -function windowFocusChanged(windowid) { +function onFocusChanged(id, isTab) { var disps = Object.keys(kiwi_win_); nextfocus_win_ = "cros"; for (var i = 0; i < disps.length; i++) { - if (kiwi_win_[disps[i]].id == windowid) { + if (kiwi_win_[disps[i]].isTab == isTab + && kiwi_win_[disps[i]].id == id) { nextfocus_win_ = disps[i]; break; } @@ -617,12 +663,14 @@ function windowFocusChanged(windowid) { } } -/* Called when a window is removed, so we can delete its reference. */ -function windowRemoved(windowid) { +/* Called when a window/tab is removed, so we can delete its reference. */ +function onRemoved(id, isTab) { var disps = Object.keys(kiwi_win_); for (var i = 0; i < disps.length; i++) { - if (kiwi_win_[disps[i]].id == windowid) { + if (kiwi_win_[disps[i]].isTab == isTab + && kiwi_win_[disps[i]].id == id) { kiwi_win_[disps[i]].id = -1; + kiwi_win_[disps[i]].isTab = false; kiwi_win_[disps[i]].window = null; printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); } diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 1134b8089..3a129d57c 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -16,7 +16,7 @@ var errordiv_ = null; /* error div */ var debug_ = 0; /* Debuging level, passed to NaCl module */ var hidpi_ = 0; /* HiDPI mode */ var display_ = null; /* Display number to use */ -var title_ = "crouton in a window"; /* window title */ +var title_ = "crouton"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ var error_ = false; /* An error has occured */ @@ -69,6 +69,13 @@ function handleCrash(event) { registerWindow(false); } +/* Handle requests from the background page (for tabs) */ +function handleRequest(message, sender, sendResponse) { + if (typeof(window[message.func]) == "function") { + window[message.func](message.param); + } +}; + /* Change debugging level */ function setDebug(debug) { debug_ = (debug > 0) ? DEBUG_LEVEL : 0; @@ -278,6 +285,7 @@ document.addEventListener('DOMContentLoaded', function() { window.addEventListener('focus', handleFocusBlur); window.addEventListener('blur', handleFocusBlur); document.addEventListener('visibilitychange', handleFocusBlur); + chrome.runtime.onMessage.addListener(handleRequest); infodiv_ = document.getElementById('info'); statusdiv_ = document.getElementById('status'); From 1cd26661c9b233b7a0b7d76264296e8e8f74028c Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 1 Apr 2015 21:03:59 -0700 Subject: [PATCH 320/334] Various review fixes. --- host-bin/enter-chroot | 2 +- host-ext/crouton/background.js | 4 +-- host-ext/crouton/window.js | 46 ++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/host-bin/enter-chroot b/host-bin/enter-chroot index e279f09e7..7b4d319dc 100755 --- a/host-bin/enter-chroot +++ b/host-bin/enter-chroot @@ -138,7 +138,7 @@ fi # Check to ensure that the XMETHOD requested has been installed if [ -n "$TMPXMETHOD" ]; then - if ! grep -q "^$TMPXMETHOD\(-\|\$\)" "$CHROOTS/$NAME/.crouton-targets"; then + if ! grep -q "^${TMPXMETHOD%%-*}$" "$CHROOTS/$NAME/.crouton-targets"; then error 1 "$CHROOTS/$NAME does not contain XMETHOD '${TMPXMETHOD%%-*}'" fi fi diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index 695ebf94f..b2e5c4000 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -573,7 +573,7 @@ function websocketMessage(evt) { /* Open a new window */ kiwi_win_[display] = new Object(); kiwi_win_[display].id = -1; - kiwi_win_[display].isTab = mode == 't'; + kiwi_win_[display].isTab = (mode == 't'); kiwi_win_[display].window = null; win = windows_.filter(function(x){return x.display == display})[0]; @@ -643,7 +643,7 @@ function websocketClose() { checkUpdate(false); } -/* Called when window/tab in focus changes: feeback to the extension so the +/* Called when window/tab in focus changes: feedback to the extension so the * clipboard can be transfered. */ function onFocusChanged(id, isTab) { var disps = Object.keys(kiwi_win_); diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 3a129d57c..ca8021906 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -15,7 +15,7 @@ var errordiv_ = null; /* error div */ var debug_ = 0; /* Debuging level, passed to NaCl module */ var hidpi_ = 0; /* HiDPI mode */ -var display_ = null; /* Display number to use */ +var display_ = -1; /* Display number to use */ var title_ = "crouton"; /* window title */ var connected_ = false; var closing_ = false; /* Disconnected, and waiting for the window to close */ @@ -268,11 +268,27 @@ function handleFocusBlur(evt) { KiwiModule_.focus(); } -/* Start in full screen */ -if (location.search.search(/[&?]mode=f(&|$)/i) != -1) { - chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {state: "fullscreen"}, function(win) {}) -} +/* Parse arguments */ +(function() { + var args = location.search.substring(1).split('&'); + for (var i = 0; i < args.length; i++) { + var keyval = args[i].split('='); + if (keyval[0] == "display") { + display_ = keyval[1]; + } else if (keyval[0] == "title") { + title_ = decodeURIComponent(keyval[1]); + } else if (keyval[0] == "debug") { + debug_ = keyval[1]; + } else if (keyval[0] == "hidpi") { + hidpi_ = keyval[1]; + } else if (keyval[0] == "mode") { + if (keyval[1] == 'f') { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {state: "fullscreen"}); + } + } + } +}()); document.addEventListener('DOMContentLoaded', function() { listener_ = document.getElementById('listener'); @@ -298,22 +314,8 @@ document.addEventListener('DOMContentLoaded', function() { warningdiv_.style.display = 'block'; errordiv_.style.display = 'block'; - /* Parse arguments */ - var args = location.search.substring(1).split('&'); - display_ = -1; - debug_ = 0; - for (var i = 0; i < args.length; i++) { - var keyval = args[i].split('=') - if (keyval[0] == "display") - display_ = keyval[1]; - else if (keyval[0] == "title") - title_ = decodeURIComponent(keyval[1]); - else if (keyval[0] == "debug") - setDebug(keyval[1]); - else if (keyval[0] == "hidpi") - setHiDPI(keyval[1]); - } - + setDebug(debug_); + setHiDPI(hidpi_); setTitle(title_); registerWindow(true); From f95420f8a1a93f926f4bdd60da8921d2b5a7ff5f Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 1 Apr 2015 21:28:49 -0700 Subject: [PATCH 321/334] Enable javascript strict mode --- host-ext/crouton/background.js | 78 +++++++++++++++++----------------- host-ext/crouton/popup.js | 2 + host-ext/crouton/window.js | 15 ++++--- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index b2e5c4000..ea13a04d0 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -1,6 +1,7 @@ // Copyright (c) 2014 The crouton Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +'use strict'; /* Constants */ var URL = "ws://localhost:30001/"; @@ -12,11 +13,11 @@ var WINDOW_UPDATE_INTERVAL = 15; /* Update window list every 15" at most */ /* String to copy to the clipboard if it should be empty */ var DUMMY_EMPTYSTRING = "%"; -LogLevel = { +var LogLevel = Object.freeze({ ERROR : "error", - INFO : "info", + INFO : "info", DEBUG : "debug" -} +}); /* Global variables */ var clipboardholder_; /* textarea used to hold clipboard content */ @@ -110,7 +111,7 @@ function updateWindowList(force) { /* Called from kiwi (window.js), so we can directly access each window */ function registerKiwi(displaynum, window) { - display = ":" + displaynum + var display = ":" + displaynum; if (kiwi_win_[display] && kiwi_win_[display].id >= 0) { kiwi_win_[display].window = window; } @@ -128,14 +129,13 @@ function closePopup() { function refreshUI() { updateWindowList(false); + var icon = "disconnected"; if (error_) - icon = "error" + icon = "error"; else if (!enabled_) - icon = "disabled" + icon = "disabled"; else if (active_) icon = "connected"; - else - icon = "disconnected"; chrome.browserAction.setIcon( {path: {19: icon + '-19.png', 38: icon + '-38.png'}} @@ -148,10 +148,10 @@ function refreshUI() { /* Make sure page is ready */ if (view.document.readyState === "complete") { /* Update "help" link */ - helplink = view.document.getElementById("help"); + var helplink = view.document.getElementById("help"); helplink.onclick = showHelp; /* Update enable/disable link. */ - enablelink = view.document.getElementById("enable"); + var enablelink = view.document.getElementById("enable"); if (enabled_) { enablelink.textContent = "Disable"; enablelink.onclick = function() { @@ -175,7 +175,7 @@ function refreshUI() { } /* Update debug mode according to checkbox state. */ - debugcheck = view.document.getElementById("debugcheck"); + var debugcheck = view.document.getElementById("debugcheck"); debugcheck.onclick = function() { debug_ = debugcheck.checked; refreshUI(); @@ -195,7 +195,7 @@ function refreshUI() { debugcheck.checked = debug_; /* Update hidpi mode according to checkbox state. */ - hidpicheck = view.document.getElementById("hidpicheck"); + var hidpicheck = view.document.getElementById("hidpicheck"); if (window.devicePixelRatio > 1) { hidpicheck.onclick = function() { hidpi_ = hidpicheck.checked; @@ -224,7 +224,7 @@ function refreshUI() { /* Update window table */ /* FIXME: Improve UI */ - windowlist = view.document.getElementById("windowlist"); + var windowlist = view.document.getElementById("windowlist"); while (windowlist.rows.length > 0) { windowlist.deleteRow(0); @@ -247,7 +247,7 @@ function refreshUI() { } /* Update logger table */ - loggertable = view.document.getElementById("logger"); + var loggertable = view.document.getElementById("logger"); /* FIXME: only update needed rows */ while (loggertable.rows.length > 0) { @@ -255,7 +255,7 @@ function refreshUI() { } /* Only update if "show log" is enabled */ - logcheck = view.document.getElementById("logcheck"); + var logcheck = view.document.getElementById("logcheck"); logcheck.onclick = function() { showlog_ = logcheck.checked; refreshUI(); @@ -263,7 +263,7 @@ function refreshUI() { logcheck.checked = showlog_; if (showlog_) { for (var i = 0; i < logger_.length; i++) { - value = logger_[i]; + var value = logger_[i]; if (value[0] == LogLevel.DEBUG && !debug_) continue; @@ -483,7 +483,7 @@ function websocketMessage(evt) { if (payload.length > 0) { windows_ = payload.split('\n').map( function(x) { - m = x.match(/^([^ *]*)\*? +(.*)$/) + var m = x.match(/^([^ *]*)\*? +(.*)$/); if (!m) return null; @@ -491,12 +491,12 @@ function websocketMessage(evt) { if (m[1] != "cros" && !m[1].match(/^:([0-9]+)$/)) return null; - k = new Object() + var k = new Object(); k.display = m[1]; k.name = m[2]; return k; } - ).filter( function(x) { return !!x; } ) + ).filter( function(x) { return !!x; } ); windows_.forEach(function(k) { var win = kiwi_win_[k.display]; @@ -508,7 +508,7 @@ function websocketMessage(evt) { win.window.setTitle(k.name); } } - }) + }); lastwindowlistupdate_ = new Date().getTime(); websocket_.send("COK"); @@ -516,10 +516,10 @@ function websocketMessage(evt) { refreshUI(); break; case 'X': /* Ask to open a crouton window */ - display = payload - match = display.match(/^:([0-9]+)([- ][^- ]*)*$/) - displaynum = match ? match[1] : null; - mode = null; + var display = payload; + var match = display.match(/^:([0-9]+)([- ][^- ]*)*$/); + var displaynum = match ? match[1] : null; + var mode = null; if (displaynum) { display = ":" + displaynum; mode = match[2].length >= 2 ? match[2].charAt(1) : 'f'; @@ -538,7 +538,7 @@ function websocketMessage(evt) { var winid = kiwi_win_[disps[i]].id; chrome.windows.update(winid, {focused: false}); - minimize = function(win) { + var minimize = function(win) { chrome.windows.update(winid, {state: 'minimized'}); }; chrome.windows.get(winid, function(win) { @@ -550,7 +550,7 @@ function websocketMessage(evt) { } else { minimize(); } - }) + }); } } else if (kiwi_win_[display] && kiwi_win_[display].id >= 0 && (!kiwi_win_[display].window || @@ -576,10 +576,10 @@ function websocketMessage(evt) { kiwi_win_[display].isTab = (mode == 't'); kiwi_win_[display].window = null; - win = windows_.filter(function(x){return x.display == display})[0]; - name = win ? win.name : "crouton in a window"; - create = chrome.windows.create; - data = {}; + var win = windows_.filter(function(x){return x.display == display})[0]; + var name = win ? win.name : "crouton in a window"; + var create = chrome.windows.create; + var data = {}; if (kiwi_win_[display].isTab) { name = win ? win.name : "crouton in a tab"; @@ -647,16 +647,16 @@ function websocketClose() { * clipboard can be transfered. */ function onFocusChanged(id, isTab) { var disps = Object.keys(kiwi_win_); - nextfocus_win_ = "cros"; + var nextfocus_win = "cros"; for (var i = 0; i < disps.length; i++) { if (kiwi_win_[disps[i]].isTab == isTab && kiwi_win_[disps[i]].id == id) { - nextfocus_win_ = disps[i]; + nextfocus_win = disps[i]; break; } } - if (focus_win_ != nextfocus_win_) { - focus_win_ = nextfocus_win_; + if (focus_win_ != nextfocus_win) { + focus_win_ = nextfocus_win; if (active_ && sversion_ >= 2) websocket_.send("Cs" + focus_win_); printLog("Window " + focus_win_ + " focused", LogLevel.DEBUG); @@ -702,10 +702,10 @@ function padstr0(i) { /* Add a message in the log. */ function printLog(str, level) { - date = new Date; - datestr = padstr0(date.getHours()) + ":" + - padstr0(date.getMinutes()) + ":" + - padstr0(date.getSeconds()); + var date = new Date; + var datestr = padstr0(date.getHours()) + ":" + + padstr0(date.getMinutes()) + ":" + + padstr0(date.getSeconds()); if (str.length > 200) str = str.substring(0, 197) + "..."; @@ -748,7 +748,7 @@ chrome.runtime.onInstalled.addListener(function(details) { chrome.runtime.getPlatformInfo(function(platforminfo) { if (platforminfo.os == 'cros') { /* On error: disconnect WebSocket, then log errors */ - onerror = function(msg, url, line) { + var onerror = function(msg, url, line) { if (websocket_) websocket_.close(); error("Uncaught JS error: " + msg, false); diff --git a/host-ext/crouton/popup.js b/host-ext/crouton/popup.js index b3811cd53..4c4f4401a 100644 --- a/host-ext/crouton/popup.js +++ b/host-ext/crouton/popup.js @@ -2,6 +2,8 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +'use strict'; + document.addEventListener('DOMContentLoaded', function() { chrome.extension.getBackgroundPage().refreshUI(); }); diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index ca8021906..12443d37e 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -1,6 +1,7 @@ // Copyright (c) 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +'use strict'; var CLOSE_TIMEOUT = 2; /* Close window x seconds after disconnect */ var DEBUG_LEVEL = 2; /* If debug is enabled, use this level in NaCl */ @@ -187,22 +188,22 @@ function handleMessage(message) { newstate = "fullscreen"; } chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {state: newstate}, function(win) {}) - }) + {state: newstate}, function(win) {}); + }); } else if (type == "state" && payload == "hide") { /* Hide window */ chrome.windows.getCurrent(function(win) { - minimize = function(win) { + var minimize = function(win) { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, {state: 'minimized'}, function(win) {})} /* To make restore nicer, first exit full screen, then minimize */ if (win.state == "fullscreen") { chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {state: 'maximized'}, minimize) + {state: 'maximized'}, minimize); } else { - minimize() + minimize(); } - }) + }); } else if (type == "resize") { i = payload.indexOf("/"); if (i < 0) return; @@ -319,4 +320,4 @@ document.addEventListener('DOMContentLoaded', function() { setTitle(title_); registerWindow(true); -}) +}); From 4fbc34d669f4d53755f36abf1ed548f5f40542f9 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 1 Apr 2015 21:52:13 -0700 Subject: [PATCH 322/334] Fix race between tab/window creation and registration A bad outcome would result in no titlebar updates. --- host-ext/crouton/background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/host-ext/crouton/background.js b/host-ext/crouton/background.js index ea13a04d0..57c891765 100644 --- a/host-ext/crouton/background.js +++ b/host-ext/crouton/background.js @@ -112,7 +112,7 @@ function updateWindowList(force) { /* Called from kiwi (window.js), so we can directly access each window */ function registerKiwi(displaynum, window) { var display = ":" + displaynum; - if (kiwi_win_[display] && kiwi_win_[display].id >= 0) { + if (kiwi_win_[display] && kiwi_win_[display].id >= -1) { kiwi_win_[display].window = window; } } @@ -669,7 +669,7 @@ function onRemoved(id, isTab) { for (var i = 0; i < disps.length; i++) { if (kiwi_win_[disps[i]].isTab == isTab && kiwi_win_[disps[i]].id == id) { - kiwi_win_[disps[i]].id = -1; + kiwi_win_[disps[i]].id = -2; kiwi_win_[disps[i]].isTab = false; kiwi_win_[disps[i]].window = null; printLog("Window " + disps[i] + " removed", LogLevel.DEBUG); From 67b3ccf4e6f5bee6db86118a760e2b480107de81 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Wed, 1 Apr 2015 21:52:49 -0700 Subject: [PATCH 323/334] Simplify the titlebar. "crouton" is redundant with the icon. The display number is usually unnecessary, and you can always deduce it from the crouton extension popup. Makes the tab title a little bit more useful, although it's still usually taken up by "chrootname/applicationname". --- host-ext/crouton/window.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 12443d37e..619ee9e3c 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -103,7 +103,7 @@ function setHiDPI(hidpi) { } function setTitle(title) { - document.title = "crouton (" + display_ + "): " + title; + document.title = title; } /* Set status message */ From 018b33b25165e25bf2b326318b09987c77702f11 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 2 Apr 2015 09:45:48 -0700 Subject: [PATCH 324/334] Clean up argument processing --- host-ext/crouton/window.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/host-ext/crouton/window.js b/host-ext/crouton/window.js index 619ee9e3c..15ec7c616 100644 --- a/host-ext/crouton/window.js +++ b/host-ext/crouton/window.js @@ -270,26 +270,23 @@ function handleFocusBlur(evt) { } /* Parse arguments */ -(function() { - var args = location.search.substring(1).split('&'); - for (var i = 0; i < args.length; i++) { - var keyval = args[i].split('='); - if (keyval[0] == "display") { - display_ = keyval[1]; - } else if (keyval[0] == "title") { - title_ = decodeURIComponent(keyval[1]); - } else if (keyval[0] == "debug") { - debug_ = keyval[1]; - } else if (keyval[0] == "hidpi") { - hidpi_ = keyval[1]; - } else if (keyval[0] == "mode") { - if (keyval[1] == 'f') { - chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, - {state: "fullscreen"}); - } +location.search.substring(1).split('&').forEach(function(arg) { + var keyval = arg.split('='); + if (keyval[0] == "display") { + display_ = keyval[1]; + } else if (keyval[0] == "title") { + title_ = decodeURIComponent(keyval[1]); + } else if (keyval[0] == "debug") { + debug_ = keyval[1]; + } else if (keyval[0] == "hidpi") { + hidpi_ = keyval[1]; + } else if (keyval[0] == "mode") { + if (keyval[1] == 'f') { + chrome.windows.update(chrome.windows.WINDOW_ID_CURRENT, + {state: "fullscreen"}); } } -}()); +}); document.addEventListener('DOMContentLoaded', function() { listener_ = document.getElementById('listener'); From d35137abd2b3bc38e72791c371ac5325c23af84a Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 2 Apr 2015 09:53:32 -0700 Subject: [PATCH 325/334] Close tags and provide style type --- host-ext/crouton/window.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/host-ext/crouton/window.html b/host-ext/crouton/window.html index 9d381c227..2ba7def7e 100644 --- a/host-ext/crouton/window.html +++ b/host-ext/crouton/window.html @@ -6,11 +6,11 @@ - - + + Crouton in a tab -