sebadorn http://www.sebadorn.de/themes/standard/img/favicon.png sebadorn http://www.sebadorn.de/ Tue, 25 Sep 2018 23:21:07 +0200 http://www.sebadorn.de/feed/ js13kGames: Tricks applied in Risky Nav Sat, 15 Sep 2018 19:19:08 +0200 http://www.sebadorn.de/2018/09/15/js13kgames-tricks-applied-in-risky-nav http://www.sebadorn.de/2018/09/15/js13kgames-tricks-applied-in-risky-nav Seba Informatik Game Dev <p class="illu"><img src="https://sebadorn.de/media/2018/09/risky_nav.png" alt="Risky Nav" /></p> <p>From the 13th August to the 13th September I participated in the <a href="https://js13kgames.com/">js13kGames</a> competition. My entry <a href="https://2018.js13kgames.com/entries/risky-nav">Risky Nav can be seen here</a> and the <a href="https://github.com/sebadorn/js13k-2018-offline">source code is on GitHub here</a>. In this blog post I will explain some of the tricks and techniques I used in making my game.</p> <p>The game is tile based, so everything – player, monsters, goal – is always positioned at a (x, y) position on a 2D map.</p> <h3>About the background</h3> <p>The background is a single image which is created once right at the beginning. It is drawn on a canvas and each tile is 1px big. In the rendering loop it is then up-scaled to the desired tile size. To avoid a blurry image, it is necessary to disable anti-aliasing.</p> <pre class="brush: javascript">context.imageSmoothingEnabled = false; let w = bgCanvas.width * tileWidth; let h = bgCanvas.height * tileHeight; function renderLoop() { context.drawImage( bgCanvas, 0, 0, w, h ); }</pre> <h3>About the fog/shadow</h3> <p>The fog/shadow around the player is done in a similar way as the background. The image is pre-rendered with each tile being 1px and then up-scaled in the main loop. But it moves with the player. The darkness is determined by the euclidean distance from the player.</p> <pre class="brush: javascript">for( let y = 0; y < fogMapHeight; y++ ) { for( let x = 0; x < fogMapWidth; x++ ) { // Euclidean distance from origin. let de = Math.sqrt( x * x + y * y ); // Darkness only starts 2 tiles away from the player. // f has to be a value between 0 and 1. let f = ( de < 2 ) ? 0 : Math.min( 1.15 - Math.min( 3 / de, 1 ), 1 ); fogCtx.fillStyle = `rgba(0,0,0,${f})`; fogCtx.fillRect( x, y, 1, 1 ); } }</pre> <!--more--> <h3>Path finding</h3> <p>The path finding for the navigation in the game can be done in a rather cheap fashion, thanks to a lot of restrictions. First, we only have a 2D map in which no diagonal movement is possible. Second, the goal and all obstacles have a static position so the map never changes. The only changing variable is the player position.</p> <p>This allows to pre-calculate all the possible paths before the game starts. In the game we then only have to find the shortest path out of those already known. The algorithm to create a path map is:</p> <ol> <li>Initialize all fields with the value <code>0</code>.</li> <li>Give the field of the goal the value <code>1</code>. This is our step counter.</li> <li>Mark every accessible field around the goal (up, down, left, right) as one step higher – with the value <code>2</code>.</li> <li>Repeat this for every field. So fields around the second one have the step value <code>3</code> and so on.</li> </ol> <p>Here is an example. For better readability the unaccessible fields ("stones" in the game) are marked with a <code>-</code> instead.</p> <pre class="brush: plain"> [ ][ ][ ][ ][ ][-] [ ][-][ ][ ][ ][ ] [ ][-][ ][ ][1][ ] [ ][ ][ ][ ][-][ ] [ ][ ][ ][ ][ ][ ] After the first marking: [ ][ ][ ][ ][ ][-] [ ][-][ ][ ][2][ ] [ ][-][ ][2][1][2] [ ][ ][ ][ ][-][ ] [ ][ ][ ][ ][ ][ ] And after that: [ ][ ][ ][ ][3][-] [ ][-][ ][3][2][3] [ ][-][3][2][1][2] [ ][ ][ ][3][-][3] [ ][ ][ ][ ][ ][ ] Final form: [7][6][5][4][3][-] [8][-][4][3][2][3] [7][-][3][2][1][2] [6][5][4][3][-][3] [7][6][5][4][5][4] </pre> <p>When searching for a shortest path while the game is already running, we can now look up the player position in this map. We then instantly know how many steps it will take from our current position to the goal. To gather the concrete path, we check every field around the current one and select the one with a step value of 1 smaller. So assuming we stand on a field of value <code>5</code>, we then select a field around us with value <code>4</code>. Repeat and move to a field of value <code>3</code> and so on until we are at field <code>1</code> – which is the goal.</p> <h3>Better arrow key control</h3> <p>Just using the <code>keydown/up</code> events is not the best way to handle the controls. The <code>keydown</code> event fires repeatedly, but there is quite a break between the first event and the following repeated ones. Also the speed the events are fired at is most likely different from the speed of the game loop.</p> <p>So instead you use the <code>keydown/up</code> event to keep track of the key states. In the game loop you then query the current state.</p> <ul> <li>For every <code>keydown</code> you save as information the key and the current timestamp.</li> <li>For every <code>keyup</code> you reset that timestamp to <code>0</code> or <code>null</code> or delete it.</li> </ul> <pre class="brush: javascript"> let state = {}; document.body.onkeydown = ( ev ) => { state[ev.which] = Date.now(); }; document.body.onkeyup = ( ev ) => { state[ev.which] = 0; };</pre> <p>In the game loop you can then do the following:</p> <pre class="brush: javascript">if( state[40] ) { player.moveDown(); } else if( state[37] ) { player.moveLeft(); } else if( state[39] ) { player.moveRight(); } else if( state[38] ) { player.moveUp(); }</pre> <p>However, this can still be improved. Suppose you press multiple arrow keys at once. Currently it is a fixed order: Down is priorized over left is priorized over right is priorized over up. This can be confusing or downright annoying because it is not intuitive. The last pressed arrow key should always be the one being used. Let's add a function:</p> <pre class="brush: javascript">function getLastArrowKey() { let down = state[40] || 0; let left = state[37] || 0; let right = state[39] || 0; let up = state[38] || 0; let values = [down, left, right, up]; values.sort( ( a, b ) => b - a ); let max = values[0]; if( !max ) { return null; } if( max === down ) { return 40; } if( max === left ) { return 37; } if( max === right ) { return 39; } if( max === up ) { return 38; } return null; }</pre> <p>This will return the arrow key which has the most current timestamp. Now adjust the handling in the game loop:</p> <pre class="brush: javascript">let arrow = getLastArrowKey(); if( arrow === 40 ) { player.moveDown(); } else if( arrow === 37 ) { player.moveLeft(); } else if( arrow === 39 ) { player.moveRight(); } else if( arrow === 38 ) { player.moveUp(); }</pre> <p>Applying those two techniques, the player will now move 1) at a steady rate the moment a key is first pressed down, and 2) even if multiple arrow keys are pressed, the last one is used. Even if the last one is released, then the one which has been pressed before that (and has not been released yet) will be used.</p> <h3>The little things</h3> <p>Some other, maybe not so obvious gameplay improvements:</p> <ul> <li>Player and goal have a minimum distance at game start. Otherwise it would be too easy.</li> <li>The player always starts near a map border, but not the bottom one. If the player started at the bottom, he most likely would walk up and the character face would not be visible.</li> <li>Monsters are randomly placed, but always a minimum distance from the player. Avoid that the player gets killed right away in the first seconds.</li> <li>It is <em>Game Over</em> on the second hit. After the first hit, the player is invincible for 2 seconds to give him a chance to get away.</li> <li>Using path finding, check if there is a guaranteed path between player and goal before the game starts.</li> <li>The monsters are dump and try to take the most direct way to the player. This means they could get stuck on stones. To prevent this, they take a random step if the next tile is not an accessible one.</li> </ul> Dead Cells: PS4 controller support on Linux Sat, 11 Aug 2018 01:28:00 +0200 http://www.sebadorn.de/2018/08/11/dead-cells-ps4-controller-support-on-linux http://www.sebadorn.de/2018/08/11/dead-cells-ps4-controller-support-on-linux Seba Informatik <p><strong><a href="https://dead-cells.com/">Dead Cells</a></strong> is a game, it is really good, and it is available for Linux. However right after installation (version 1.0 from GOG) it did not recognize my PS4 controller. It could not be a problem with the controller itself or Linux in general, because the DS4 worked with other applications – for example it showed up perfectly fine in <a href="https://jstest-gtk.gitlab.io/">jstest-gtk</a> (0.1.0).</p> <p>After some research I came across <a href="https://www.reddit.com/r/linux_gaming/comments/6rxhj8/what_happened_with_the_dualshock_4_and_the_new/">this reddit post</a>. Dead Cells uses the <a href="https://www.libsdl.org/">SDL library</a>, so maybe that's it. I followed the instructions and built and ran <a href="https://gitlab.com/sdl-jstest/sdl-jstest">sdl2-jstest</a>. The output should contain an entry like this for the DS4 (2nd gen):</p> <pre class="brush: plain">Joystick Name: 'Sony Interactive Entertainment Wireless Controller' Joystick GUID: 030000004c050000cc09000011810000 Joystick Number: 0 Number of Axes: 6 Number of Buttons: 13 Number of Hats: 1 Number of Balls: 0 GameControllerConfig: Name: 'PS4 Controller' Mapping: '030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,' </pre> <p>… or like this for the DS4 (1st gen):</p> <pre class="brush: plain">Joystick Name: 'Sony Computer Entertainment Wireless Controller' Joystick GUID: 030000004c050000c405000011810000 Joystick Number: 0 Number of Axes: 6 Number of Buttons: 13 Number of Hats: 1 Number of Balls: 0 GameControllerConfig: Name: 'PS4 Controller' Mapping: '030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,'</pre> <br /> <p>Take the value behind <em>Mapping</em> and add a line in your <code>/etc/environment</code> file like this:</p> <pre class="brush: shell">SDL_GAMECONTROLLERCONFIG='030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,'</pre> <br /> <p>After the next reboot everything should be working. Or if you want to test it right away without reboot, then you can just add it to the start script of the game. Assuming you used the standard installation path from the GOG installer, the file is located at <code>~/GOG Games/Dead Cells/start.sh</code>. Change the file so it now begins with:</p> <pre class="brush: shell">#!/bin/bash # GOG.com (www.gog.com) # Game export SDL_GAMECONTROLLERCONFIG='030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,'</pre> <br /> <p>That's what worked for me. If it still doesn't for you, try adding some udev rules as described in my article <a href="https://sebadorn.de/2017/12/07/using-nw-js-to-communicate-with-a-ds4-controller">Using NW.js to communicate with a DS4 controller</a>.</p> JavaScript source protection with NW.js Sat, 26 May 2018 19:21:53 +0200 http://www.sebadorn.de/2018/05/26/javascript-source-protection-with-nw-js http://www.sebadorn.de/2018/05/26/javascript-source-protection-with-nw-js Seba Informatik <p>You can minify and uglify JavaScript files, but technically the source code of your distributed <a href="https://nwjs.io/">NW.js</a> application is still readable. But NW.js also provides the means to <a href="https://nwjs.readthedocs.io/en/latest/For%20Users/Advanced/Protect%20JavaScript%20Source%20Code/">compile JavaScript to a binary file</a> and then load it as part of the application. The command line tool <code>nwjc</code> to create the binary file is included in the SDK version.</p> <p>Assuming you have a JavaScript file <code>js/private.js</code>:</p> <pre class="js">'use strict'; function secretFunction( foo ) { return foo * 4; };</pre> <p>Then you can compile it like this to a file <code>js/private.bin</code>:</p> <pre class="shell">$ ./nwjs-sdk-v0.30.5-linux-x64/nwjc js/private.js js/private.bin</pre> <p>Internally the tool uses the <a href="https://v8project.blogspot.de/2015/09/custom-startup-snapshots.html">V8 snapshot feature</a>, which means the versions have to match. A binary file created with NW.js 0.30 can only be loaded by 0.30. Binary files also <strong>do not work cross-platform</strong>. For each platform it is necessary to compile its own binary file with the SDK for the same platform.</p> <p>To then load the binary file in your application, it works like this:</p> <pre class="js">let win = nw.Window.get(); win.evalNWBin( null, 'js/private.bin' ); let value = secretFunction( 4 ); // returns 16</pre> <p>Note however that the loading is per window. If you open another window in your application, the file has to be loaded there again.</p> <p>Using the DevTools you can of course find the functions and variables which have been loaded from the file. The function implementation however is protected:</p> <pre class="plain">&gt; String( secretFunction ) &lt; "function secretFunction() { [native code] }"</pre> <h3>DevTools issues</h3> <p>There is an issue with loading binary files and the DevTools. Basically you cannot have the DevTools open and then load the file. There will be no error, but the contents will not be available. <a href="https://github.com/nwjs/nw.js/issues/5225">This is a known issue.</a></p> <p>My temporary solution is to just close the DevTools. But just closing them right before is not enough, you also have to use a timeout before loading the file:</p> <pre class="js">let win = nw.Window.get(); // Function is only available in the SDK build. if( typeof win.closeDevTools === 'function' ) { win.closeDevTools(); } setTimeout( () => { win.evalNWBin( null, 'js/private.bin' ); }, 500 );</pre> <p>But why not check first if the DevTools are open? Then you could open them again afterwards. <a href="https://nwjs.readthedocs.io/en/latest/References/Window/#winisdevtoolsopen">According to the API documentation</a> there is <code>win.isDevToolsOpen()</code>. But it exists only in the documentation. Using the SDK build there is de facto no such function. This too is <a href="https://github.com/nwjs/nw.js/issues/4487">a known issue</a>.</p> <h3>Wine for Windows</h3> <p>I successfully used <a href="https://www.winehq.org/">Wine</a> 3 to compile a binary file for the Windows version of a NW.js application and then load it there. So if you are on Linux or macOS you will not need Windows for your build process. You should of course still test your application to make sure it works on all targeted platforms.</p> Using NW.js to communicate with a DS4 controller Thu, 07 Dec 2017 19:55:00 +0100 http://www.sebadorn.de/2017/12/07/using-nw-js-to-communicate-with-a-ds4-controller http://www.sebadorn.de/2017/12/07/using-nw-js-to-communicate-with-a-ds4-controller Seba Informatik <p class="illu"><img src="https://sebadorn.de/media/2017/12/ds4_green.jpg" style="width: 600px; height: 314px;" alt="DS4 green light" /></p> <p><a href="https://nwjs.io/">NW.js</a> still provides the <a href="https://developer.chrome.com/apps/api_index">Chrome Apps API</a> which has been removed from Chrome, but not ChromeOS. This will allow us to access in a platform-independant manner devices which are connected with the PC per USB.</p> <p>Without this API, a 3rd party Node.js module like <a href="https://github.com/node-hid/node-hid">node-hid</a> could be used. This will however come with platform-dependant libraries and will have to be updated or rebuild each time the Node.js version changes.</p> <p>This article concentrates on sending data to the controller. However it is also possible to retrieve data like pressed buttons using the established connection. Aside from using chrome.hid there is also the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API">Gamepad API</a> for read-only access.</p> <h3>Identifying the controller</h3> <p>First we need a way to identify the DS4. Devices come with a vendor Id and product Id. According to the <a href="https://wiki.gentoo.org/wiki/Sony_DualShock">Gentoo Wiki</a> they are as follows:</p> <table> <tr> <th>Device</th> <th>Vendor Id</th> <th>Product Id</th> </tr> <tr> <td>DS4 (1st gen)</td> <td>hex <code>054C</code> / dec <code>1356</code></td> <td>hex <code>05C4</code> / dec <code>1476</code></td> </tr> <tr> <td>DS4 (2nd gen)</td> <td>hex <code>054C</code> / dec <code>1356</code></td> <td>hex <code>09CC</code> / dec <code>2508</code></td> </tr> </table> <p>Having tested with both devices, I can also confirm the Ids.</p> <h3>Get the device</h3> <p>For all communication with the device, we will use the <a href="https://developer.chrome.com/apps/hid">chrome.hid API</a>. First we define a filter using the vendor and product Id, and then query the available devices:</p> <pre class="brush: javascript">var filter = { filter: [ { vendorId: 1356, productId: 1476 }, { vendorId: 1356, productId: 2508 } ] }; chrome.hid.getDevices( filter, ( devices ) => { // Error handling. if( chrome.runtime.lastError ) { console.error( chrome.runtime.lastError ); return; } if( !devices ) { return; } var device = devices[0]; // Next: Connect to the device. };</pre> <!--more--> <h4>Linux</h4> <p>On Linux you have to add some udev rules to be able to access the devices as non-root. Create a file <code>/etc/udev/rules.d/61-dualshock.rules</code> with the following content:</p> <pre class="brush: plain">SUBSYSTEM=="input", GROUP="input", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE:="666", GROUP="plugdev" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev" SUBSYSTEM=="input", GROUP="input", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="09cc", MODE:="666", GROUP="plugdev" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"</pre> <p>Then reload the udev rules with the following command. If your DS4 was plugged in, you will have to disconnect it and then connect it again afterwards.</p> <pre class="brush: bash">sudo udevadm control --reload-rules</pre> <p>Source: <a href="https://www.npmjs.com/package/dualshock-controller">https://npmjs.com/package/dualshock-controller</a></p> <h3>Connect</h3> <p>In order to connect with the device the device Id is necessary. On success the callback will be passed a connection object containing a connection Id we will need.</p> <pre class="brush: javascript">chrome.hid.connect( device.deviceId, ( connection ) => { // Error handling. if( chrome.runtime.lastError ) { console.error( chrome.runtime.lastError ); return; } if( !connection ) { return; } var connectionId = connection.connectionId; // Next: Send a command. } );</pre> <h3>Send the command</h3> <p>The command will be passed as a buffer of length 10 of type <code>uint8</code> so the values are between <code>0</code> and <code>255</code>. The report Id has to be <code>5</code>.</p> <pre class="brush: javascript">const reportId = 5; var data = new Uint8Array( 10 ); var i = 0; // Unchanging beginning. data[i++] = 0xFF; data[i++] = 0x04; data[i++] = 0x00; // Options we can use. data[i++] = 0x00; // rumble right data[i++] = 0x00; // rumble left data[i++] = 0x00; // red LED brightness data[i++] = 0xFF; // green LED brightness data[i++] = 0x00; // blue LED brightness data[i++] = 0x00; // flash on duration data[i++] = 0x00; // flash off duration chrome.hid.send( connectionId, reportId, data.buffer, () => { // Error handling. if( chrome.runtime.lastError ) { console.error( chrome.runtime.lastError ); } } );</pre> <p>Setting all RGB values to <code>0</code> will turn off the light. The rumble values let the controller side rumble stronger the higher the value. Sending a <code>0</code> while it still rumbles – it stops after a few seconds on its own – will stop it immediately.</p> <p>Source for the format: <a href="https://github.com/ehd/node-ds4">https://github.com/ehd/node-ds4</a></p> <h3>Disconnect</h3> <p>When you don't need the device anymore, disconnect it.</p> <pre class="brush: javascript">chrome.hid.disconnect( connectionId, () => { // Error handling. if( chrome.runtime.lastError ) { console.error( chrome.runtime.lastError ); } } );</pre> Android file transfer over MTP is a nightmare Fri, 02 Sep 2016 23:00:00 +0200 http://www.sebadorn.de/2016/09/02/android-file-transfer-over-mtp-is-a-nightmare http://www.sebadorn.de/2016/09/02/android-file-transfer-over-mtp-is-a-nightmare Seba Informatik <p><strong>Update, 30.09.2017:</strong> After trying it again today, I found none of the problems listed below anymore. It's fast and painless. So that's good!</p> <hr /> <p>It was bad before, but after the upgrade to Ubuntu 16.04 – which may or may not be related, it probably is – it went from bad to downright painful. Connecting device and PC works fine. What does not is …</p> <ul> <li>It is slow. Opening the SD card directory with my music takes several seconds.</li> <li>After deleting files the amount of available space is not updated. I free space for new files but get a warning telling me there isn't enough free space. At least I have the option to try and copy the files anyway, ignoring the warning.</li> <li><strong>The new problem:</strong> After just one transfer, I cannot access the device per MTP anymore. No content is listed for the music directory anymore. Seemingly trying to load the contents it ultimately fails to do so. Unplugging does not help. Restarting the PC does not help. Restarting the device does not help. Using a different PC with macOS does not help. What helps is … using the Android file manager and deleting a file. Something is seriously broken here.</li> </ul> <p>In early Android versions the device was mounted as USB mass storage. Those were the good, old days. <a href="http://www.howtogeek.com/192732/android-usb-connections-explained-mtp-ptp-and-usb-mass-storage/">At least they had understandable reasons for replacing it.</a></p> <p class="illu"><img src="//sebadorn.de/media/2016/09/nautilus-ftp.png" alt="Nautilus FTP" /></p> <p><strong>An alternative to using MTP is FTP.</strong> I used the app <a href="https://play.google.com/store/apps/details?id=lutey.FTPServer">FTPServer</a> to start an FTP server on my device while connected to my home WiFi. In the FTPServer settings I chose <code>wlan0</code> as standard interface and pointed the server root to the directory <code>/mnt/extSdCard</code>. Now I can use FileZilla or Nautilus (see image above) to connect to the server from my PC. The transfer speed is of course a little limited, but the file transfer itself? – works just fine.</p> <p>While I'm happy to have found a good alternative to the nightmare that is MTP, I find this whole situation quite ridiculous. At some point I'd like to upgrade from my Galaxy S3. While I'm not considering iOS or Windows Phone, I'm also disappointed of Android. Hopefully the <a href="http://www.ubuntu.com/phone">Ubuntu phones</a> take of.</p> Improvements after Mozilla’s Observatory results Sat, 27 Aug 2016 16:15:00 +0200 http://www.sebadorn.de/2016/08/27/improvements-after-mozillas-observatory-results http://www.sebadorn.de/2016/08/27/improvements-after-mozillas-observatory-results Seba Informatik <p>Mozilla made their <a href="https://observatory.mozilla.org/">Observatory service</a> public, which lets you check the security of sites. A first run resulted in an <code>F</code> for <code>sebadorn.de</code>. Following some of the sug&shy;gestions I could improve that to a <code>B-</code>.</p> <h3>1. Redirect HTTP to HTTPS</h3> <p>Thanks to <a href="https://letsencrypt.org/">Let’s Encrypt</a> I already offered HTTPS, but I didn't enforce it. Now visitors to <code>http://sebadorn.de</code> are redirected to <code>http<strong>s</strong>://sebadorn.de</code>. I did so by adding the following rule to my <code>.htaccess</code> file:</p> <pre class="brush: apache">&lt;IfModule mod_rewrite.c&gt; RewriteEngine On RewriteCond %{HTTP_HOST} ^sebadorn\.de [NC] RewriteCond %{SERVER_PORT} 80 RewriteRule ^(.*)$ https://sebadorn.de/$1 [R,L] &lt;/IfModule&gt;</pre> <h3>2. Add some more headers</h3> <pre class="brush: apache">&lt;IfModule mod_headers.c&gt; Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure" Header set Content-Security-Policy "frame-ancestors 'self'" Header set X-Content-Type-Options "nosniff" Header set X-Frame-Options "SAMEORIGIN" Header set X-XSS-Protection "1; mode=block" &lt;/IfModule&gt;</pre> <dl> <dt>Set-Cookie</dt> <dd>Cookies about to be set received additional directives: <em>HttpOnly</em> and <em>Secure</em>. <em>HttpOnly</em> disallows cookies being read by JavaScript and <em>Secure</em> enforces an HTTPS connection. (<a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#Cookies">Source</a>)</dd> <dt>X-Content-Type-Options</dt> <dd>Setting this header to <em>nosniff</em> tells browsers not to try and guess the MIME type of contents, which potentially prevents XSS attacks. (<a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#X-Content-Type-Options">Source</a>)</dd> <dt>X-Frame-Options</dt> <dd>Setting this header to <em>SAMEORIGIN</em> or <em>DENY</em> prevents other pages from displaying the site in a frame which prevents clickjacking. (<a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#X-Frame-Options">Source</a>)</dd> <dt>X-XSS-Protection</dt> <dd>Setting this header to <em>1; mode=block</em> tells browsers to try and detect XSS attacks and in this case stop loading the page. (<a href="https://wiki.mozilla.org/Security/Guidelines/Web_Security#X-XSS-Protection">Source</a>)</dd> </dl> Remove intro/outro from an MP3 without re-encoding Thu, 03 Dec 2015 21:44:41 +0100 http://www.sebadorn.de/2015/12/03/remove-intro-outro-from-an-mp3-without-re-encoding http://www.sebadorn.de/2015/12/03/remove-intro-outro-from-an-mp3-without-re-encoding Seba Informatik Musik <p>I have this podcast I listen to repeatedly. But most episodes have an intro and outro part before the actual episode, which gets quite annoying. One possibility is, of course, to just use an audio editor like Audacity, cut the unwanted parts, and save the file. But this would result in the MP3 being re-encoded and losing in quality. This shouldn't be necessary since I only want to cut off some data, right?</p> <p>The tool for the job is <code>mp3splt</code>. I wrote this little bash script:</p> <pre class="brush: bash">#!/usr/bin/env bash F_IN=$1 F_OUT=${F_IN%.mp3} START=$2 END=EOF-$3 OUT_DIR=./nointro # Split at "-". FILE_SPLIT=(${F_IN//-/ }) # Remove "_" and " " characters. TRACK=${FILE_SPLIT[0]//_/} TRACK=${TRACK// /} mp3splt -f -d "$OUT_DIR" "$F_IN" "$START" "$END" -o "$F_OUT" eyeD3 --track=$TRACK "$OUT_DIR/$F_IN"</pre> <p>Example: <code>./shorten.sh 26-FacelessOldWoman.mp3 1.18 1.15.5</code></p> <p>First, the track number will be extracted from the file name. In my case, the number is always at the beginning and separated by a minus from the title. So we split the string and remove some unwanted characters (whitespace, underscore).</p> <p>In the example, the first 1min 18sec will be removed and the last 1min 15.5sec. The resulting MP3 will be saved with the same name in the directory set in <code>OUT_DIR</code>.</p> <p>Most id3 tags will be kept – including the embedded cover –, but the track number will be overwritten by mp3splt. That is why I extracted the track number from the file name before. Now I can set it again with <code>eyeD3</code>.</p> <p>Done.</p> Stackless BVH traversal Fri, 13 Mar 2015 23:42:00 +0100 http://www.sebadorn.de/2015/03/13/stackless-bvh-traversal http://www.sebadorn.de/2015/03/13/stackless-bvh-traversal Seba Informatik <p class="abstract">Im Path Tracing verwendet man spezielle Datenstrukturen für die Geometrie, um diese schneller gegen die Strahlen testen zu können. Eine der üblichsten ist dabei die Bounding Volume Hierarchy (BVH) – ein Binärbaum, der die Szene immer weiter unterteilt. Einen solchen Baum würde man normalerweise rekursiv durchlaufen. Auf der GPU mit OpenCL steht jedoch keine Rekursion zur Verfügung.</p> <p class="abstract">Was man daher macht, ist, selbst einen kleinen Stack zu verwalten, in dem man sich den nächsten zu besuchenden Knoten merkt. Dieser Stack benötigt jedoch zusätzlichen privaten Speicher, welcher knapp bemessen ist, und dadurch die Anwendung ausbremst. Wünschens&shy;wert ist daher ein Verfahren, das ohne Stack auskommt.</p> <h3>Aufbau und Ablauf</h3> <p class="illu"><img src="//sebadorn.de/media/2015/03/bvh-stackless-nextNode.png" alt="BVH-Baum." style="width: 600px; height: 400px" /></p> <ul> <li>Jeder Knoten hat entweder genau zwei oder keine Kindknoten.</li> <li>Jeder Knoten hat zudem ein Attribut <em>nextNode</em>. <ul> <li>Für den linken Kindknoten zeigt <em>nextNode</em> auf den rechten Geschwisterknoten.</li> <li>Für den rechten Kindknoten zeigt <em>nextNode</em> auf den Elternknoten.</li> </ul> </li> </ul> <!--more--> <p>Vom Wurzelknoten aus wird immer zuerst dem linken Kindknoten gefolgt. Falls der Strahl die Bounding Box nicht trifft, es sich um einen Blatt&shy;knoten handelt oder zuletzt ein rechter Knoten besucht wurde, dann wird als nächstes der Knoten besucht, auf den <em>nextNode</em> zeigt. Ist man wieder beim Wurzel&shy;knoten angelangt, beendet der Algorithmus.</p> <p><strong>Nachteil:</strong> Knoten werden auf dem Weg nach oben erneut besucht. Ein unnötiger Schritt, der nur zusätzliche Zeit in Anspruch nimmt.</p> <h3>Optimierung</h3> <p class="illu"><img src="//sebadorn.de/media/2015/03/bvh-stackless-nextNode-right-optimized.png" alt="BVH – optimierter nextNode" style="width: 600px; height: 400px;" /></p> <p>Die Optimierung ist denkbar einfach. Dafür muss jediglich der <em>nextNode</em> der rechten Knoten angepasst werden. Anstatt auf den Elternknoten, können rechte Kindknoten direkt auf den nächsten zu testenden Knoten zeigen.</p> <p>Dafür setzt man zunächst <em>nextNode</em> immer weiter auf einen höheren Elternknoten, bis es sich bei diesem um einen linken Kindknoten handelt. Danach setzt man <em>nextNode</em> auf den rechten Geschwister&shy;knoten. Auf diese Weise werden alle bereits besuchten Eltern&shy;knoten über&shy;sprungen. Existiert kein rechter Kindknoten, ist man beim Wurzel&shy;knoten angelangt und zeigt auf diesen, was den Endpunkt markiert.</p> <p>Den so erzeugten Baum kann man dann folgendermaßen abwandern:</p> <pre class="brush: cpp">int index = 0; do { bvhNode node = scene->bvh[index]; float tNear = 0.0f; float tFar = INFINITY; bool isNodeHit = ( intersectBox( ray, node, &tNear, &tFar ) && tFar > 0.00001f && ray->t > tNear ); // In case of no hit: Go right or up. index = node.nextNode; if( !isNodeHit ) { continue; } // Not a leaf node, progress further down to the left. if( !isLeaf( node ) ) { index = node.leftChild; } // Node is leaf node. else { intersectFaces( scene, ray, node, tNear, tFar ); } } while( index > 0 );</pre> <h3>Anmerkung: Links/rechts</h3> <p>Welcher der beiden Kindknoten als „linker“ und welcher als „rechter“ gesetzt wird, ist egal. Ich setze als linken Kindknoten denjenigen mit mehr Ober&shy;fläche. Mehr Ober&shy;fläche bedeutet, dass ihn potentiell mehr Strahlen treffen und man so eventuell ein früheres Ende erreicht. Tatsächlich hat diese Vorsortierung die Performance gering&shy;fügig verbessert.</p> <h3>Resultat</h3> <p>Mein Algorithmus kommt wie gewünscht ohne Stack aus. Weiterer Speicher wird nur für eine Variable <em>index</em>, sowie ein neues Knoten-Attribut <em>nextNode</em> benötigt. Beim Abwandern des Baumes wird jeder Knoten höchstens ein einziges Mal besucht.</p> <p>Zumindest in meiner Implemen&shy;tierung <a href="#fn-bvh-impl">[1]</a> verbesserten sich die Render-Zeiten in allen meinen Test-Szenen. Dies kann aber auch an einer schlechten Implemen&shy;tierung des Stack-basierten Algorithmus liegen, mit dem ver&shy;glichen wurde. Ich habe beide Verfahren im Projekt belassen und versuche sie von Zeit zu Zeit weiter zu optimieren und erneut zu vergleichen.</p> <hr /> <p class="annotation" id="fn-bvh-impl">[1] <a href="https://github.com/sebadorn/Physically-based-Rendering/blob/4bbf393a597a1bc1cf3e3536a57abd6f3026c8a7/source/PathTracer.cpp#L261">Vorbereitung der BVH-Datenstruktur</a> und <a href="https://github.com/sebadorn/Physically-based-Rendering/blob/4bbf393a597a1bc1cf3e3536a57abd6f3026c8a7/source/opencl/pt_bvh.cl#L128">BVH traversal</a>.</p> Standalone-Anwendungen mit node-webkit Sun, 19 Oct 2014 16:28:00 +0200 http://www.sebadorn.de/2014/10/19/standalone-anwendungen-mit-node-webkit http://www.sebadorn.de/2014/10/19/standalone-anwendungen-mit-node-webkit Seba Informatik <p class="illu"><img src="//sebadorn.de/media/2014/10/node-webkit-demo.png" style="width: 602px; height: 425px;" alt="node-webkit demo window" /></p> <p>Gestern habe ich ein wenig in <a href="https://github.com/rogerwang/node-webkit">node-webkit</a> reingeschnuppert. Damit sollen sich ganz einfach Desktop-Anwendungen auf Basis von HTML, JavaScript und Node.js erstellen lassen. Die erstellte Anwendung lässt sich dann relativ einfach für verschiedene Betriebs&shy;systeme verpacken. Für nicht allzu rechen-intensive Spiele scheint mir das recht interessant. Tatsächlich verwendet auch das kürzlich erschiene <a href="http://www.wizardslizard.com/">A Wizard's Lizard</a> node-webkit <a class="fn" href="#fn-lizard">[1]</a>.</p> <p>Für eine kleine Demo habe ich das <a href="https://github.com/mrdoob/three.js/">Beispiel von three.js</a> genommen und als Anwendung verpackt. Das HTML und JavaScript lasse ich hier mal aus. Mein <code>package.json</code> für ein Fenster ohne Toolbar sieht wie folgt aus:</p> <pre class="brush: javascript">{ "main": "index.html", "name": "nw-demo", "window": { "frame": true, "height": 600, "kiosk": false, "toolbar": false, "width": 900 } }</pre> <!--more--> <p>Den Inhalt des Projekt-Verzeichnisses verpackt man als nächstes als ZIP-Archiv:</p> <pre class="brush: bash">zip -r demo.nw *</pre> <p>Diese Datei lässt sich mit <code>nw demo.nw</code> ausführen, benötigt aber eben weiterhin node-webkit auf dem System. Im nächsten Schritt wird node-webkit daher in das Archiv integriert:</p> <pre class="brush: bash">cat ~/node-webkit-v0.10.5/nw demo.nw > demo && chmod +x demo</pre> <p>Versucht man nun <code>./demo</code> auszuführen, gibt es eine Fehlermeldung:</p> <blockquote class="code error"> [13445:1019/161018:FATAL:content_main_runner.cc(751)] Check failed: base::i18n::InitializeICU(). </blockquote> <p>Im selben Verzeichnis wie die Standalone-Anwendung müssen auch die Dateien <strong>nw.pak</strong> und <strong>icudtl.dat</strong> liegen. Beide Dateien finden sich im selben Verzeichnis wie <code>nw</code>, hier also <code>~/node-webkit-v0.10.5/</code>. Einfach herüberkopieren und die Anwendung lässt sich starten.</p> <hr /> <p>Meine kleine Demo habe ich <a href="https://github.com/sebadorn/project-templates/tree/master/node-webkit">auf GitHub hochgeladen</a>. Darin befindet sich auch ein Build-Skript für Linux, das sich um alles kümmert. Es muss nur der Pfad zur node-webkit-Installation angepasst werden.</p> <hr /> <p class="annotation" id="fn-lizard">[1] <a href="http://forum.lostdecadegames.com/topic/115/node-webkit-and-steam">forum.lostdecadegames.com/topic/115/node-webkit-and-steam</a></p> Sublime Text 3 portable unter Linux Fri, 05 Sep 2014 15:24:00 +0200 http://www.sebadorn.de/2014/09/05/sublime-text-3-portable-unter-linux http://www.sebadorn.de/2014/09/05/sublime-text-3-portable-unter-linux Seba Informatik <p><a href="https://sublimetext.com/3">Sublime Text</a> ist derzeit der Editor meiner Wahl, zusammen mit einigen Zusatz-Paketen. Installiert habe ich ihn über <a href="http://www.webupd8.org/2013/07/sublime-text-3-ubuntu-ppa-now-available.html">das Web Upd8 PPA</a>. Das ist allerdings hinderlich, wenn man ihn auch als normalen Text-Editor zum spontanen Bearbeiten verwenden möchte. In dem Fall springt immer das zuletzt geöffnete Projekt mit auf. Eine Extra-Installation wäre hier praktisch.</p> <ol> <li>Lade <a href="https://sublimetext.com/3">Sublime Text 3</a> als tarball herunter.</li> <li>Entpacke das Archiv nach <code>~/.st3_portable</code>. (Name egal.)</li> <li>Erstelle in dem Verzeichnis einen Ordner namens <code>Data</code>. (Wichtig!)</li> </ol> <p>Das reicht schon, um auf eine portable Version umzustellen. Für die Verwendung als Gelegenheits-Text-Editor steht aber noch ein wenig mehr an. So soll, wenn eine Datei geöffnet wird, auch das zugehörige Verzeichnis in der Sidebar angezeigt werden. Dafür erstellt man sich ein kleines Bash-Skript <code>~/.st3_portable/st3_p.sh</code>:</p> <pre class="brush:bash">#!/bin/bash cd ~/.st3_portable/ FOLDER=$(dirname "$1") ./sublime_text "$FOLDER" "$1"</pre> <p>Das Argument <code>$1</code> ist die zu öffnende Datei. Im letzten Schritt muss nun noch eine <code>.desktop</code>-Datei angelegt werden. Erstelle eine Datei <code>~/.local/share/applications/st3-portable.desktop</code> mit dem Inhalt:</p> <pre class="brush:plain">[Desktop Entry] Version=1.0 Type=Application Name=Sublime Text 3 (portable) Comment=Sophisticated text editor for code, markup and prose Exec=/home/seba/.st3_portable/st3_p.sh %F Terminal=false MimeType=text/plain; Icon=sublime-text Categories=TextEditor;Development;Utility; StartupNotify=true X-Desktop-File-Install-Version=0.21</pre> <p>Anstatt <code>/home/seba/</code> sollte da natürlich der eigene Benutzername stehen. Relative Pfadangaben bzw. <code>~/</code> funktionieren hier nicht. Ist die Datei gespeichert, sollten sich Dateien nun per <em>Rechtsklick › Öffnen mit › Sublime Text 3 (portable)</em> öffnen lassen.</p> Mit dem Upgrade zu Ubuntu 14.04 hätte ich noch warten sollen Sat, 17 May 2014 13:10:00 +0200 http://www.sebadorn.de/2014/05/17/mit-dem-upgrade-zu-ubuntu-14-04-haette-ich-noch-warten-sollen http://www.sebadorn.de/2014/05/17/mit-dem-upgrade-zu-ubuntu-14-04-haette-ich-noch-warten-sollen Seba Informatik <p>Da meine Masterarbeit abgeschlossen ist, habe ich gestern das Upgrade von Ubuntu 13.10 auf 14.04 gewagt. Zwar gab es keine kritischen Probleme, aber doch einige Ärgernisse – wovon sich die meisten mit Recherche und Herumprobieren beheben ließen.</p> <h3>Konflikt zwischen Wine und NVIDIA-OpenCL</h3> <p>Es macht keinen Sinn, aber scheinbar hat jemand in den Paket-Abhängigkeiten gepfuscht oder sonst irgendetwas. Wollte man <code>wine</code> installieren, musste <code>nvidia-libopencl1-331</code> deinstalliert werden und vice versa. Ich brauche beide Pakete. Wie sich herausstellte, lässt sich Wine 1.7 aber installieren.</p> <p>Dafür fügt man das <a href="https://launchpad.net/~ubuntu-wine/+archive/ppa">Wine Team PPA</a> zu seinen Softwarequellen hinzu und kann dann <code>wine1.7</code> installieren. Daraufhin lässt sich auch <code>playonlinux</code> installieren.</p> <p>Sollte <code>/usr/lib/libOpenCL.so</code> dann immer noch fehlen, muss wahrscheinlich nur ein Symlink nachgetragen werden:</p> <pre class="brush: bash">sudo ln -s /usr/lib/i386-linux-gnu/libnvidia-opencl.so.1 /usr/lib/libOpenCL.so</pre> <h3>Fallout 3 läuft nicht mehr unter Wine</h3> <p><del>Ein leider noch ungelöstes Problem.</del> Vor dem Upgrade lief Fallout 3 unter Wine ziemlich gut – nicht perfekt, aber ziemlich gut. Jetzt öffnet sich nur noch ein inhaltsloses Fenster. Eine Neu-Installation hat nicht geholfen. Mit Debugging bin ich auch noch nicht weit gekommen, da in den Logs kein eindeutiger Fehler zu finden ist. Wer Tipps hat, ich wäre dankbar. Vielleicht tauchen ja demnächst Leidensgenossen auf <a href="http://appdb.winehq.org/objectManager.php?sClass=version&iId=14322">appdb.winehq.org</a> auf.</p> <p><strong>[Update 2014-05-18]:</strong> Wine 1.7.19 ist erschienen und behebt genau dieses Problem (<a href="http://www.winehq.org/announce/1.7.19">siehe Changelog</a>).</p> <!--more--> <h3>In Firefox keine YouTube-HTML5-Videos über 360p</h3> <p>Grund ist mangelnde Unterstützung des H.264-Codecs. Das ist jedoch nichts Neues und bisher gab es <a href="http://askubuntu.com/questions/389437/how-do-i-get-html5-h-264-video-working-on-firefox">dafür einen Workaround mit GStreamer</a>. Der funktioniert nun nicht mehr, da die benötigten Pakete durch neuere ersetzt wurden.</p> <p>Eine Möglichkeit ist sicherlich, ein PPA zu finden, dass die alte Version zur Verfügung stellt, aber ich habe ehrlich gesagt genug davon, andauernd andere PPAs einbinden zu müssen. Was YouTube anbelangt steige ich wieder auf Flash um. Mit Firefox 30 könnte sich das wieder ändern, wenn dann hoffentlich GStreamer 1.0 unterstützt wird.</p> <h3>GNOME-Terminal ohne Transparenz</h3> <p>Wie aus <a href="https://bugzilla.gnome.org/show_bug.cgi?id=698544#c45">dieser Antwort eines GNOME-Entwicklers</a> hervorgeht, wurde Hintergrund-Transparenz im Rahmen größerer Code-Aufräumarbeiten (vorerst?) entfernt. Ich will aber transparente Terminals, also bin ich auf Terminator umgestiegen.</p> <pre class="brush: bash">sudo apt-get install terminator</pre> <h3>Einige GNOME Shell-Erweiterungen sind nicht aktuell</h3> <p class="illu"><img style="width: 687px; height: 623px;" src="//sebadorn.de/media/2014/05/gnome-shell-extensions.png" alt="Installierte GNOME Shell-Erweiterungen." /></p> <p>Ich mag die GNOME Shell. Aber nur durch einige Erweiterungen wird sie wirklich gut, ansonsten wäre sie arg unattraktiv für mich. Die eine oder andere Erweiterung wurde von ihrem Autor leider noch nicht aktualisiert, was zum Glück kein allzu großes Problem darstellt. Diese Erweiterungen sind in JavaScript geschrieben und liegen einfach in einem HOME-Unterverzeichnis (<code>~/.local/share/gnome-shell/extensions/</code>). Die folgenden Anpassungen waren ausreichend für <a href="">Nvidia GPU Temperature Indicator </a> und <a href="https://extensions.gnome.org/extension/82/cpu-temperature-indicator/">Sensors</a>.</p> <ul> <li> In <code>metadata.json</code> die aktuelle Version eintragen: <pre class="brush: js">"shell-version": ["3.10"],</pre> </li> <li> Anstatt <code>PanelMenu.SystemStatusButton</code> verwendet man <code>PanelMenu.Button</code>. <pre class="brush: js">PanelMenu.Button.prototype._init.call(this, 0.0, 'sensorsMenu', false);</pre> </li> <li> Aus der Funktion <code>.addActor()</code> wurde <code>.actor.add()</code>. </li> </ul> <h3>Symbole im Cairo-Dock verschwinden</h3> <p>Zusätzlich zur GNOME Shell habe ich noch am Bildschirmrand ein Cairo-Dock für die offenen Anwendungen. Leider waren viele Symbole plötzlich nicht mehr zu sehen. Die Lösung war erfreulich unkompliziert. Cairo-Dock muss im OpenGL-Modus gestartet werden:</p> <pre class="brush: bash">cairo-dock -o</pre> Bilder aus der Masterthesis: Physik-basiertes Rendering Thu, 10 Apr 2014 02:59:00 +0200 http://www.sebadorn.de/2014/04/10/bilder-aus-der-masterthesis-physikbasiertes-rendering http://www.sebadorn.de/2014/04/10/bilder-aus-der-masterthesis-physikbasiertes-rendering Seba Informatik Studentendasein <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-04.png" alt="Modell: Eichhörnchen" /></p> <p>Das große Eichhörnchen ist aus Glas (Brechungsindex 1,5). Die dunklen Stellen – z.B. an der Schnauze – sollten nicht dunkel sein, aber der Pfad hat hier seine maximale Länge erreicht, weshalb keine Licht- und somit Farbinformationen gewonnen werden konnten. Die ganzen Erklärungen, was ich in diesem Projekt überhaupt gemacht habe, kommen irgendwann in einem anderen Blogeintrag.</p> <!--more--> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-05.png" alt="Modell: Eichhörnchen" /></p> <p>Gleiche Szene wie eben, nur wurde als Licht ein eher dunkles, warmes Orange (CIE A) gewählt. Das kleine Eichhörnchen wurde zudem zur weißen Lichtquelle umdefiniert.</p> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-06.png" alt="Modell: Icosphere" /></p> <p>Ein Ikosaeder in der Mitte und die Wände links und rechts davon spiegeln. Allerdings ist die Spiegelung nicht perfekt, sondern leicht matt gewählt, weshalb das Bild mit zunehmender Spiegelungsanzahl verschwimmt. Die schwarze Wand am Schluss gibt es nicht wirklich, hier haben die Pfade nur wieder ihr Maximum erreicht.</p> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-07.png" alt="Modell: Icosphere" /></p> <p>Die weißen Linien zeichnen die Bounding Boxen ein. Als Datenstruktur wurde eine <em>Bounding Volume Hierarchy</em> verwendet – die Dreiecke wurden in kleine Gruppen eingeteilt.</p> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-03.png" alt="Modell: Blenders Suzanne" /></p> <p>Matter, grüner Boden. Der Affenkopf ist das Suzanne-Modell von <a href="http://www.blender.org/">Blender</a>. Hier ist sehr schön Color Bleeding zu sehen – die Farbe des grünen Bodens strahlt auf die Objekte ab.</p> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-01.png" alt="Modell: Applejack" /></p> <p>Das <a href="http://kp-shadowsquirrel.deviantart.com/art/Pony-Model-Download-Center-215266264">Applejack-Modell von KP-ShadowSquirrel</a>. Die Farben und Material-Eigenschaften habe allerdings ich verbockt. Die richtigen Farben zu finden ist recht schwer, wenn man sie nicht einfach per Color Picker und Schieberegler zusammenklicken kann. Da mein Renderer mit Wellenlängen und Intensitäten zur Farbrepräsentation arbeitet, war viel Ausprobieren und Herantasten nötig.</p> <p class="illu"><img style="width: 800px; height: 600px;" src="//sebadorn.de/media/2014/04/pbr-02.png" alt="Modell: Applejack" /></p> <p>Noch einmal mit anderer Beleuchtung. Alle Bilder wurden für circa 2&nbsp;Minuten bei 10&nbsp;FPS auf der Grafikkarte gerendert.</p> Der kleine Kampf CyanogenMod auf dem Galaxy S1 zu installieren Fri, 16 Aug 2013 19:17:00 +0200 http://www.sebadorn.de/2013/08/16/der-kleine-kampf-cyanogenmod-auf-dem-galaxy-s1-zu-installieren http://www.sebadorn.de/2013/08/16/der-kleine-kampf-cyanogenmod-auf-dem-galaxy-s1-zu-installieren Seba Informatik <p class="illu"><img style="width: 600px; height: 348px;" src="//sebadorn.de/media/2013/08/s1-cyanogenmod.jpg" alt="CyanogenMod wird auf dem Galaxy S1 installiert" /></p> <p>Um das Ende vorwegzunehmen: Die Installation selbst hat reibungslos geklappt und auf meinem alten Samsung Galaxy S1 läuft nun super-flüssig CyanogenMod 10.1 (Android 4.2.2). Auf den Schritten dahin bin ich nur unterwegs ein wenig angeeckt.</p> <p>Ich folgte dabei (so gut es ging) der <a href="https://wiki.cyanogenmod.org/w/Install_CM_for_galaxysmtd">offiziellen Anleitung</a>.</p> <h3>Heimdall für 64-Bit-Linux funktioniert nicht mit 64-Bit-Linux</h3> <p>Heimdall wird benötigt, um eine Recovery-Datei auf das Smartphone zu schieben, während es sich im <em>Download Mode</em> befindet. Heimdall gibt es für Windows, Linux und Mac. Nur funktioniert die 64-Bit-Linux-Version scheinbar nicht bei jedem, worauf in der Anleitung aber auch hingewiesen wird. Die 32-Bit-Version erfüllt aber ihren Zweck. Von daher war das nicht wirklich ein Problem.</p> <!--more--> <h3>Endlose Boot-Schleife</h3> <p>Nun sollte ich also in den <em>ClockworkMod Recovery Mode</em> booten, indem ich zum Einschalten <strong>Volume up + Home + Power</strong> drücke. Dies machte ich, aber das Gerät kam nicht über den Boot-Begrüßungs-Screen (der mit dem Samsung-Logo) hinweg. Ab und zu flackerte dieser mal kurz, ansonsten ging es aber nicht mehr weiter. Ich probierte es mehrmals, manchmal konnte ich es wieder über den Power-Knopf ausschalten, manchmal entfernte ich einfach den Akku.</p> <p>Ein kurzes <a href="https://youtu.be/uQWg-SiV-L8">YouTube-Erklär-Video</a> brachte mich schließlich auf den Trichter, dass mein <strong>Timing</strong> schlecht war. Man darf die Tastenkombination nur so lange drücken, bis das Samsung-Logo erscheint. Entweder habe ich vorher immer zu lange oder zu kurz gehalten, aber diesmal hat es geklappt.</p> <h3>Wie bekomme ich das Image nach /sdcard?</h3> <p>Nun musste also das CyanogenMod-ZIP auf den internen Speicher verfrachtet werden. Die Anleitung empfiehlt dafür <strong>ADB</strong> – ich hingegen rate davon ab, das schont Nerven. Meine aktuelle ADB-Version hat das Smartphone immer als offline aufgeführt und entsprechend konnte ich auch nicht damit interagieren. Ich habe verschiedene USB-Ports ausprobiet, ein anderes Kabel genommen, das Smartphone neugestartet … nope, no dice!</p> <p>Daraufhin hatte ich über das ClockwordMod Recovery-Menü das Gerät als <strong>Mass Storage</strong> gemountet. Dadurch konnte ich über Ubuntu darauf zugreifen und die Datei einfach hinüberkopieren … oder auch nicht. Der Kopiervorgang hat sich jedes Mal kurz vor Ende aufgehangen und die Datei hat es nie auf das Gerät geschafft.</p> <p>Schließlich habe ich meinen Laptop gepackt und das darauf noch installierte, seit Jahren nicht mehr gebootete Vista gestartet. Smartphone angeschlossen und es wurde als Wechseldatenträger erkannt – gut! USB-Stift mit dem ZIP eingesteckt und der Explorer hängt sich auf – schlecht. Abgemeldet, neu angemeldet. Diesmal habe ich zuerst das ZIP hinüberkopiert und dann das Smartphone angeschlossen. Erfolg! Das ZIP war auf dem Gerät!</p> <blockquote> <p><strong>Vorschlag:</strong> Bevor ihr auch nur mit dem ersten Schritt der Anleitung beginnt, ladet das benötigte ZIP herunter und übertragt es ganz normal auf euer Smartphone. (Den internen Speicher, nicht auf eine externe Karte!) Dann müsst ihr weder mit ADB herumhantieren, noch genervt einen Alternativ-Weg suchen.</p> </blockquote> <p>Von da an hat es geklappt. Und ja, der nicht zu leugnende, kleine Linux-Fanboy in mir ist ein wenig angefressen wegen der benötigten Windows-Hilfe.</p> <h3>Wie bekomme ich den Play Store da drauf?</h3> <p>Okay, <a href="http://goo.im/gapps">hier gibt es also ein ZIP zum Download</a>. Aber sind Apps nicht normalerweise APK-Dateien? Was soll also mit dem ZIP … oooh.</p> <p>Das Gerät wieder mit <strong>Volume Up + Home + Power</strong> einschalten und diesmal anstatt dem CyanogenMod-ZIP das Gapps-ZIP installieren.</p> <hr /> <p class="illu"><img style="width: 600px; height: 500px;" src="//sebadorn.de/media/2013/08/s1-systeminfo.jpg" alt="S1 auf dem CyanogenMod läuft" /></p> <p>Unterm Strich ist also nichts Tragisches passiert. Es war nur teils anstrengend und als ich im Boot-Loop steckte, hatte ich schon kurzzeitig das Schlimmste befürchtet.</p> Opera –› Firefox Wed, 17 Jul 2013 20:20:00 +0200 http://www.sebadorn.de/2013/07/17/opera-firefox http://www.sebadorn.de/2013/07/17/opera-firefox Seba Informatik <p>Opera 15 ist erschienen (zumindest für Windows und Mac) und bringt den großen Engine-Sprung von Presto zu Blink mit sich. Eine nachvollziehbare und sinnvolle Umstellung. Allerdings erscheint mir die Veröffentlichung überstürzt und Opera 15 ein halbgares Produkt. Wozu gibt es denn die <a href="http://my.opera.com/desktopteam/blog/opera-features-and-release-cycle">drei Release-Kanäle</a> <em>Opera (Stable)</em>, <em>Next</em> und <em>Developer</em>?</p> <p>Opera 15 hat keine Bookmarks, herrjenocheins! Stattdessen soll dafür das Speed Dial ausreichen, mit dem sich zwar auch Seiten speichern, gruppieren und suchen lassen, aber das eben keine einfache Auflistung bietet. Bin ich ein „Power User“-Randfall, nur weil ich 100+ Bookmarks habe?</p> <p class="illu"><img style="width: 600px; height: 369px;" src="//sebadorn.de/media/2013/07/opera15-speeddial.jpg" alt="Opera 15: Speeddial" /></p> <p>Das generelle Feedback war auch entsprechend negativ und Opera hat verlauten lassen, dass <a href="http://my.opera.com/desktopteam/blog/2013/07/10/ctrl-z-of-ctrl-d">ein paar Prioritäten umverlagert werden</a>. Dann fehlen aber immer noch die Site Preferences. Vermutlich fehlt noch mehr, aber für mich ist der Deal hier schon geplatzt. Nunja, ganz abgesehen davon gibt es ohnehin noch keine Linux-Version.</p> <p>Der neue Blink-Opera wird wohl frühestens in einem Jahr auf einen vernünftigen Stand kommen – vorausgesetzt, er soll überhaupt die Funktionalität der 12er-Version wieder erreichen. Wenn ein Unternehmen ankündigt, es wolle sein Produkt massentauglicher gestalten, überkommt mich ein ungutes Gefühl.</p> <p>Also könnte ich entweder bei Opera 12 bleiben, der noch eine Weile sicherheitstechnisch gepflegt werden wird, oder ich gewöhne mich schon mal um. Seit einiger Zeit mache ich auch die Erfahrung, dass Opera 12 von Firefox und Chrome – gerade was JavaScript-Geschwindigkeit betrifft – abgehängt wird und die eine oder andere Seite ein Problem mit Opera 12 hat, wie z.B. YouTube oder Mega.co.nz.</p> <p>Jetzt bin ich also bei Firefox und versuche mir mit Addons alles so herzurichten, dass es mir an Nichts fehle. Warum Firefox und nicht Chrome? Firefox lässt sich etwas besser bzw. mehr konfigurieren und Mozilla ist mir auch das sympathischere Unternehmen – das meine ich weniger negativ für Google, als vielmehr positiv für Mozilla.</p> <!--more--> <h3>Übernahme der Bookmarks</h3> <p>Die Bookmarks lassen sich in Opera als HTML exportieren. Firefox kann diese HTML-Datei wieder importieren und schon hat man alle Bookmarks inklusive Verzeichnisstruktur. Vorher sollte man in Opera vielleicht noch das Müll-Verzeichnis leeren, da diese sonst zusammen mit den anderen Bookmarks im obersten Verzeichnis landen.</p> <p>Mir ist bewusst, dass es eine <a href="https://addons.opera.com/en/extensions/details/bookmarks-manager/">Opera 15 Extension für Bookmarks</a> gibt. Diese ist hochgradig unnütz. Die Bedienung und Optik ist lachhaft schlecht und der Import hat bei mir erst gar nicht funktioniert – was aber auch daran liegen mag, dass ich aus der Opera-Linux-Version exportierte und in Opera-Windows-in-einer-VM importierte.</p> <h3>Addons</h3> <p><a href="https://addons.mozilla.org/de/firefox/addon/adblock-plus/"><strong>Adblock Plus</strong></a> zusammen mit dem <a href="https://addons.mozilla.org/de/firefox/addon/elemhidehelper/"><strong>Element Hiding Helper</strong></a>. Der Zweck dürfte bekannt sein: Werbung und andere nervige Elemente blockieren.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/add-to-search-bar/"><strong>Add to Search Bar</strong></a> ist praktisch, um sehr einfach neue Suchmaschinen hinzuzufügen: Einfach ein Rechtsklick in das Suchfeld der besuchten Seite und auswählen. Die eingerichteten Suchen können entweder über das Suchfeld genutzt werden oder – wie ich es bevorzuge – per Kürzel über die Adressleiste verwendet werden.</p> <p class="illu"><img style="width: 600px; height: 407px;" src="//sebadorn.de/media/2013/07/firefox-search.png" alt="Firefox Search" /></p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/firegestures"><strong>FireGestures</strong></a> um die Mausgesten aus Opera nachzubilden. Die verschiedenen Maus&shy;gesten lassen sich zudem frei einrichten und so habe ich auch ein paar Bewegungen, die ich von Opera gewohnt war, wieder so eingestellt.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/fxchrome/"><strong>FXChrome</strong></a> bietet einen sehr sauberen, Chrome-ähnlichen Look. Es ist so ein typisch minimalistisches Design, wie ich es liebe.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/greasemonkey/"><strong>Greasemonkey</strong></a> für Userscripts. Insofern man überhaupt Unterstützung für Userscripts braucht. Opera 12 hatte es build-in und so geht es mit Firefox.</p> <hr /> <p><a href="https://www.eff.org/https-everywhere"><strong>HTTPS Everywhere</strong></a>, damit von mehr Seiten oder auch nur bestimmten Elementen auf Seiten (wie z.B. die Gravatars auf meinem Blog) die HTTPS-Variante verwendet wird, insofern vorhanden.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/private-tab/"><strong>Private Tab</strong></a> für eben solche, da Firefox an sich nur Private Windows anbietet. Verstehe ich nicht so recht, aber nun gut, dem ließ sich ja leicht Abhilfe schaffen.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/super-start/?src=search"><strong>Super Start</strong></a>, da das Speed Dial von Firefox so gar nicht meinen Wünschen entspricht. Vor allem nervt mich, dass dort immer die letzten besuchten Seiten auftauchen, es sei denn, ich belege alle Plätze.</p> <p>Die Vorschaubilder der Seiten haben allerdings erst einmal Kartoffel-Aufnahmen-Qualität. In <code>about:config</code> den Wert von <code>extensions.superstart.site.snapshot.width</code> auf <code>512</code> zu erhöhen verbessert dies deutlich.</p> <hr /> <p><a href="https://addons.mozilla.org/de/firefox/addon/yesscript/"><strong>YesScript</strong></a>, um gezielt Seiten die Ausführung von JavaScript zu untersagen. Also ein Blacklist-Verfahren – im Gegensatz zu <a href="https://addons.mozilla.org/de/firefox/addon/noscript/"><em>NoScript</em></a>, das eine Whitelist führt.</p> <h3>Fein-Einstellungen</h3> <p><strong>Letzter Tab soll nicht den ganzen Browser schließen:</strong> In <code>about:config</code> muss der Wert für <code>browser.tabs.closeWindowWithLastTab</code> auf <code>false</code> geändert werden.</p> <p>Hat noch jemand Vorschläge für nützliche Addons oder Einstellungen?</p> PGP Public Key meiner Mail-Adresse Tue, 11 Jun 2013 17:59:00 +0200 http://www.sebadorn.de/2013/06/11/pgp-public-key-fuer-meine-email-adresse http://www.sebadorn.de/2013/06/11/pgp-public-key-fuer-meine-email-adresse Seba Informatik <p>In Anbetracht <a href="https://netzpolitik.org/2013/prism-amerikanischer-geheimdienst-nsa-hat-direkten-zugriff-auf-alle-daten-der-grosen-internet-unternehmen/">aktueller Ereignisse</a> habe ich mir das Thema PGP/Mail-Verschlüsselung noch einmal angeschaut und möchte die Möglichkeit einfach mal all jenen, die meine Mail-Adresse haben, anbieten. Die Einrichtung ging mit dieser <a href="https://support.mozillamessaging.com/en-US/kb/digitally-signing-and-encrypting-messages">Anleitung für Thunderbird</a> recht schnell und unkompliziert vonstatten.</p> <p><strong><a href="http://sebadorn.de/public_keys/seba_dorn@gmx.de.asc">PGP Public Key für meine GMX-Adresse</a></strong> – es bediene sich, wer möchte. Für meine anderen Adressen kann ich das auch noch nachholen, falls Bedarf besteht.</p> <p>Wer es sich auch einrichtet: Bloß nicht so schusselig sein und den <em>Private</em> Key irgendwo in die Cloud (DropBox etc.) legen; sonst kann man sich das Verfahren auch gleich sparen.</p> <p>Ansonsten: Danke und viel Glück, Herr Snowden. Hoffentlich wird das mit dem Asyl in Island was.</p> <hr /> <p><strong>[Update 2013-06-13]</strong> Machen wir das doch so: <a href="http://sebadorn.de/public_keys/">sebadorn.de/public_keys</a></p> Opera Extensions und JavaScript-Whitelisting Sun, 21 Apr 2013 00:02:00 +0200 http://www.sebadorn.de/2013/04/21/opera-extensions-und-javascript-whitelisting http://www.sebadorn.de/2013/04/21/opera-extensions-und-javascript-whitelisting Seba Informatik <h3>JavaScript-Whitelisting</h3> <p>Gemeint ist, wenn man JavaScript erst im Browser deaktiviert und dann nur für jede gewünschte Seite gesondert aktiviert. Man führt also eine Whitelist für Seiten, die JavaScript ausführen dürfen. Warum kann das eine gute Idee sein?</p> <ul> <li><strong>Sicherheit und geschützte Privatsphäre:</strong> Böse Dinge wie dynamisches Nachladen von schädlichen Inhalten von Fremd-Servern wird erschwert. Das allgegenwärtige Tracking durch hauptsächlich Werbefirmen (das beinhaltet auch und gerade Google) wird nahezu unmöglich.</li> <li><strong>Internet im Light-Modus:</strong> Kein Popup, das nach Facebook-Likes oder Twitter-Gefollowere bettelt; keine nervige Bottom-Menubar, die aufklappt, und überhaupt: 99% weniger Werbung.</li> </ul> <h3>Das Problem mit Opera</h3> <p>Deaktiviert man global JavaScript, funktionieren Extensions nicht mehr, selbst wenn die besuchte Seite auf der Whitelist steht. Konkret ist das Problem, dass keine Content-Skripte mehr in die besuchte Seite injiziert werden – ein elementärer Bestandteil der meisten Extensions.</p> <p>Der Witz an der Sache ist, dass Userscripts – diese einzelnen JavaScript-Dateien, die auf .user.js enden – weiterhin funktionieren, denn dafür gibt es sogar eine extra Einstellung. Auch die Opera Developer Toolbar „Dragonfly“ funktioniert weiterhin.</p> <p>Also: Warum nicht auch Extensions?</p> <h3>Workaround</h3> <ol> <li><em>Aktiviere</em> JavaScript global.</li> <li>Füge eine Site Preference hinzu für die Adresse „<code>*</code>“ und <em>deaktiviere</em> für diese JavaScript. Dabei handelt es sich um eine Wildcard, die alle Seiten betrifft – besucht und unbesucht.</li> <li>Füge eine Site Preference hinzu für die Adresse „<code>wuid-*</code>“ und <em>aktiviere</em> für diese JavaScript. Dies ist für die Extensions, damit z.B. das Background-Skript und die Optionen-Seite funktionieren.</li> </ol> <p>Ab sofort laufen auf jeder Seite, die auf der Whitelist steht, auch die Extensions.</p> Browser-Addons: Nachrichten-Broadcast an alle Tabs Mon, 25 Mar 2013 17:25:00 +0100 http://www.sebadorn.de/2013/03/25/browser-addons-nachrichten-broadcast-an-alle-tabs http://www.sebadorn.de/2013/03/25/browser-addons-nachrichten-broadcast-an-alle-tabs Seba Informatik <p class="illu"><img style="width: 600px; height: 400px;" src="//sebadorn.de/media/2013/03/3-browser-broadcast.png" alt="Browser: Chrome, Firefox, Opera" /></p> <h3>Situation</h3> <p>Meine Extension injiziert auf bestimmten Seiten ein Content Script. Man kann dann auf dieser veränderten Seite auch Einstellungen ändern, die im der Extension zugewiesenen Storage gespeichert wird. Dafür wird wie üblich eine Nachricht mit den Änderungen an den Hintergrundsprozess geschickt.</p> <h3>Problem</h3> <p>Angenommen, ich habe zwei oder mehr Tabs offen. Nun verändere ich die Einstellung A in einem dieser Tabs. Die Änderung wird gespeichert, aber alle anderen Tabs zeigen nach wie vor den alten Zustand an. Erst nach einem Neuladen der Seite wird die Änderung sichtbar.</p> <p>Was wir wollen, ist eine Synchronisation der Tabs, was bedeutet, wir müssen alle im Hintergrund eintreffenden Änderungen allen anderen Tabs ebenfalls mitteilen. Und das geht so …</p> <!--more--> <h3>Lösung: Opera</h3> <pre class="brush: js">opera.extension.broadcastMessage( msg );</pre> <p>Eeyup, das war es.</p> <h3>Lösung: Chrome</h3> <p>In Chrome müssen wir uns die IDs der Tabs merken, die unser Content Script beinhalten. Dafür sendet besagtes Content Script zuallererst immer eine Nachricht an den Hintergrund, damit wir uns seine Tab-ID merken können. Wird der Tab geschlossen, verwerfen wir die Tab-ID wieder.</p> <pre class="brush: js">var openTabs = []; chrome.extension.onMessage.addListener( handler ); function handler( e, sender, sendResponse ) { if( e.data.msg == "content script loaded" ) { openTabs.push( sender.tab.id ); chrome.tabs.onRemoved.addListener( onTabRemove ); } ... }; function onTabRemove( tabId, info ) { var idx = openTabs.indexOf( tabId ); if( idx >= 0 ) { openTabs.splice( idx, 1 ); } };</pre> <p>Ein Broadcast an alle relevanten Tabs sieht dann wie folgt aus.</p> <pre class="brush: js">for( var i = 0; i < openTabs.length; i++ ) { chrome.tabs.sendMessage( openTabs[i], msg, myCallbackFunction ); }</pre> <h3>Lösung: Firefox</h3> <p>In Firefox gehen wir ähnlich vor wie in Chrome und merken uns alle Tabs, die unser Content Script inne haben, in der Variable <code>workers</code>.</p> <pre class="brush: js">var self = require( "self" ); var pageMod = require( "page-mod" ); var workers = []; pageMod.PageMod( { include: "*.mozilla.org", attachTo: ["existing", "top"], contentScriptWhen: "ready", contentScriptFile: [self.data.url( "myContentScript.js" )], onAttach: handleOnAttach } ); function handleOnAttach( worker ) { workers.push( worker ); worker.on( "detach", function() { forgetWorker( this ); } ); }; function forgetWorker( worker ) { var idx = workers.indexOf( worker ); if( idx >= 0 ) { workers.splice( idx, 1 ); } };</pre> <p>Unser Broadcast funktioniert dann wie folgt.</p> <pre class="brush: js">for( var i = 0; i < workers.length; i++ ) { workers[i].postMessage( msg ); }</pre> <hr /> <p class="annotation"><a href="http://dev.opera.com/articles/view/opera-extensions-messaging/">Opera extensions: messaging</a></p> <p class="annotation"><a href="http://developer.chrome.com/extensions/tabs.html">chrome extensions: chrome.tabs</a></p> <p class="annotation"><a href="https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/page-mod.html">Firefox SDK: page-mod</a></p> Add-ons für Chrome, Firefox und Opera selbst hosten Wed, 13 Mar 2013 18:14:00 +0100 http://www.sebadorn.de/2013/03/13/addons-fuer-chrome-firefox-und-opera-selbst-hosten http://www.sebadorn.de/2013/03/13/addons-fuer-chrome-firefox-und-opera-selbst-hosten Seba Informatik <p class="illu"><img style="width: 600px; height: 400px;" src="//sebadorn.de/media/2013/02/3-browser.png" alt="3 Browser" /></p> <p>Ich habe eine Browser-Extension für Chrome, Firefox und Opera erstellt. Alle Dateien liegen bei mir lokal, ich habe also z.B. nicht Firefoxs <a href="https://builder.addons.mozilla.org/">Add-on Builder</a> verwendet, sondern nur <a href="https://addons.mozilla.org/en-us/developers/builder">das SDK</a>. Als die Extension fertig war, wollte ich sie auch selbst hosten. Ich meine, wie schwer kann das schon sein? Ja, nun …</p> <p>In diesem Artikel gehen wir davon aus, dass sich die Browser-spezifischen Extension-Dateien in den Unterverzeichnissen <code>Chrome/</code>, <code>Firefox/</code> und <code>Opera/</code> befinden.</p> <!--more--> <p>Der Artikel ist lang geworden. Hier sind ein paar Sprungmarken:</p> <ul> <li><a href="#extension-hosting-opera">Hosting und Updates mit Opera</a></li> <li><a href="#extension-hosting-chrome">Hosting und Updates mit Chrome</a></li> <li><a href="#extension-hosting-firefox">Hosting und Updates mit Firefox</a></li> </ul> <hr /> <h3 id="extension-hosting-opera">Opera</h3> <p>In Operas <code>config.xml</code> muss ein Verweis auf die XML-Datei auf dem hostenden Server stehen, die später nach Updates gefragt wird.</p> <pre class="brush: xml">&lt;widget …&gt; … &lt;update-description href="http://example.org/updates-opera.xml" /&gt; &lt;/widget&gt;</pre> <p>Dann packen wir die Extension ein. Dafür wird einfach alles in ein ZIP-Archiv mit der Datei-Endung OEX gepackt.</p> <pre class="brush: bash">cd Opera/ zip -r MyExtension.oex *</pre> <p>Die XML-Datei für Updates <code>updates-opera.xml</code> beinhaltet lediglich einen Pfad zur Extension, sowie die aktuelle Versionsnummer dieser.</p> <pre class="brush: xml">&lt;update-info xmlns="http://www.w3.org/ns/widgets" src="http://example.org/MyExtension.oex" version="1.1"&gt;&lt;/update-info&gt;</pre> <p>Das war es auch schon. Ein Hindernis ist jedoch, dass die eigene Domain erst von den Benutzern als „Trusted Website“ in Opera eingetragen werden muss – <a href="http://sebadorn.de/mlp/mle/mle_install_opera.png">wie auf diesem Screenshot zu sehen.</a></p> <hr /> <h3 id="extension-hosting-chrome">Chrome</h3> <p>In <code>manifest.json</code> wird für Updates ein zusätzlicher Eintrag benötigt.</p> <pre class="brush: js">{ … "update_url": "http://example.org/updates-chrome.xml" }</pre> <p>Wird die Extension <strong>zum ersten Mal verpackt</strong>, lautet der Befehl:</p> <pre class="brush: bash">google-chrome --pack-extension=Chrome/</pre> <p>Dabei wird ein Private Key erzeugt und hier mit dem Namen <code>Chrome.pem</code> abgelegt. <em>Diese Datei sollte geheim bleiben – also nicht einfach in ein öffentliches Repository hochladen!</em></p> <p>Bei jedem späteren Mal, dass die Extension verpackt wird, lautet der Befehl dann:</p> <pre class="brush: bash">google-chrome --pack-extension=Chrome/ --pack-extension-key=Chrome.pem</pre> <p>In der XML-Datei <code>updates-chrome.xml</code> steht die ID der Extension, der Pfad zur Installations-Datei und die aktuelle Versionsnummer. Die ID erfährt man von der Chrome-Erweiterungs-Seite (<code>chrome://extensions</code>) – dafür muss man ein Häkchen bei „Developer Mode“ setzen. Diese ID ändert sich nicht mehr, auch nicht mit späteren Updates.</p> <pre class="brush: xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;gupdate xmlns="http://www.google.com/update2/response" protocol="2.0"&gt; &lt;app appid="replace-this-with-your-extension-id"&gt; &lt;updatecheck codebase="http://example.org/MyExtension.crx" version="1.1" /&gt; &lt;/app&gt; &lt;/gupdate&gt;</pre> <p>Ein Problem bei der Installation ist, dass dies nicht direkt über die Website erfolgen kann. Die Extension muss vom Benutzer heruntergeladen und dann auf die Chrome-Erweiterungs-Seite (<code>chrome://extensions</code>) <a href="http://sebadorn.de/mlp/mle/mle_install_chrome.png">gezogen werden (Drag&amp;Drop)</a>. Updates funktionieren dann jedoch ganz normal.</p> <hr /> <h3 id="extension-hosting-firefox">Firefox</h3> <p>Ich sage es vorne weg: Firefox hat mich ordentlich angepisst. Mir ist klar, dass alles im Sinne von Sicherheit geschieht und durchaus sinnvoll ist. Aber aus Entwicklersicht war es eine Qual überhaupt erst einmal herauszufinden, wie vorzugehen ist. Erschwerend kam hinzu, dass ich kein SSL verwende. Dafür ist die Firefox-Extension die einzige, die für eine Installation keine größere Mitarbeit seitens des Benutzers erfordert.</p> <p>Zunächst wird die Extension mit dem cfx tool aus <a href="https://addons.mozilla.org/en-us/developers/builder">dem SDK</a> verpackt.</p> <pre class="brush: bash">CFX=~/.firefox-addon-sdk-1.13.2/bin/cfx cd Firefox/ $CFX xpi --update-url http://example.org/updates-firefox.rdf</pre> <p>Nun müssen wir allerdings das für das XPI erstellte <code>install.rdf</code> bearbeiten. Also extrahieren wir diesen Teil.</p> <pre class="brush: bash">unzip MyExtension.xpi install.rdf</pre> <p>Nun benötigen wir ein weiteres Tool, genannt McCoy. Dafür gibt es eine <a href="https://developer.mozilla.org/en-US/docs/McCoy">grafische Variante von Mozilla</a> selbst, oder eine <a href="http://blog.techno-barje.fr//post/2009/10/05/Mozilla-Mccoy-tool-from-the-command-line/">gepatchte Version, die man über das Terminal</a> verwenden kann, was ich hier mache.</p> <p>Zuerst erstellen wir für unsere Extension einen Schlüssel. Ähnlich wie bei Chrome, wird dies <strong>nur einmalig</strong> gemacht und dann immer wieder verwendet.</p> <pre class="brush: bash">MCCOY=~/.mccoy/mccoy $MCCOY -createKey "ThinkOfSomeKey"</pre> <p>Als nächstes fügen wir unseren Public Key in das <code>install.rdf</code> ein und packen es wieder in das XPI.</p> <pre class="brush: bash">$MCCOY -installRDF install.rdf -key "ThinkOfSomeKey" zip -f MyExtension.xpi install.rdf</pre> <p>Nun bilden wir einen Hash vom XPI, da wir diesen noch brauchen werden. Welches Hashing-Verfahren eingesetzt wird, ist relativ egal. Ich habe mich für <em>sha256</em> entschieden.</p> <pre class="brush: bash">XPI_HASH=$(sha256sum MyExtension.xpi | sed "s/ .*//g" -)</pre> <p>Gratuliere, wir sind ungefähr bei der Hälfte! Schauen wir uns jetzt etwas Leichtes an: Den Button auf der Website, um den Installations-Prozess einzuleiten. Dafür haben wir eine HTML-Seite mit einem Element, das die ID <code>trigger_ff</code> hat. Stehe das Nachfolgende nun in einer JS-Datei namens <code>foo.js</code>:</p> <pre class="brush: js">var trigger = document.getElementById( "trigger_ff" ); trigger.addEventListener( "click", function( e ) { e.preventDefault(); var params = { "MyExtension": { URL: "http://example.org/MyExtension.xpi", IconURL: "http://example.org/MyIcon_32x32.png", Hash: "sha256:%XPI_HASH%", toString: function() { return this.URL; } } }; InstallTrigger.install( params ); }, false );</pre> <p>Den Hash können wir beispielsweise so einfügen:</p> <pre class="brush: bash">sed -i "s;%XPI_HASH%;sha256:$XPI_HASH;g" foo.js</pre> <p>Als nächstes widmen wir uns der RDF-Datei für Updates. Erstelle eine Datei <code>updates-firefox.rdf</code>.</p> <pre class="brush: xml">&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"&gt; &lt;Description about="urn:mozilla:extension:MyExtension@example.org"&gt; &lt;em:updates&gt; &lt;Seq&gt; &lt;li&gt; &lt;Description&gt; &lt;em:version&gt;1.1&lt;/em:version&gt; &lt;em:targetApplication&gt; &lt;Description&gt; &lt;em:id&gt;{ec8030f7-c20a-464f-9b0e-13a3a9e97384}&lt;/em:id&gt; &lt;em:minVersion&gt;18.0&lt;/em:minVersion&gt; &lt;em:maxVersion&gt;19.*&lt;/em:maxVersion&gt; &lt;em:updateLink&gt;http://example.org/MyExtension.xpi&lt;/em:updateLink&gt; &lt;em:updateHash&gt;%XPI_HASH%&lt;/em:updateHash&gt; &lt;/Description&gt; &lt;/em:targetApplication&gt; &lt;/Description&gt; &lt;/li&gt; &lt;/Seq&gt; &lt;/em:updates&gt; &lt;/Description&gt; &lt;/RDF&gt;</pre> <p>Die ID <code>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</code> wird nicht verändert, denn hierbei handelt es sich um die ID für Firefox – es wird gesagt, dass dies eine Extension für Firefox ist und z.B. nicht für Thunderbird.</p> <p>Klickt der Benutzer den Installations-Button, erfährt Firefox den Hash und überprüft, ob die zu installierende Datei auch die ist, die hier angepriesen wurde. Dieser Vergleich findet auch bei Updates statt. Daher müssen wir auch hier den Platzhalter <code>%XPI_HASH%</code> ersetzen.</p> <pre class="brush: bash">sed -i "s;%XPI_HASH%;sha256:$XPI_HASH;g" updates-firefox.rdf</pre> <p><em><strong>Hinweis:</strong> Der Hash muss für jede neue Version erneut errechnet und eingefügt werden!</em></p> <p>Nun kommt wieder McCoy zum Einsatz, indem wir in das Update-RDF unsere Signatur einfügen.</p> <pre class="brush: bash">$MCCOY -signRDF updates-firefox.rdf -key "ThinkOfSomeKey"</pre> <p>Wenn man möchte, kann man das erzeugte RDF auch noch überprüfen lassen.</p> <pre class="brush: bash">$MCCOY -verifyRDF updates-firefox.rdf -key "ThinkOfSomeKey"</pre> <p>Geschafft.</p> <hr /> <h3>Relevantes</h3> <p>Wer mein Build-Skript und Templates für die XML/RDF/JS-Dateien sehen möchte, findet diese <a href="https://github.com/sebadorn/MLE/blob/master/build.sh">in meinem GitHub-Repository</a>.</p> <p>Diese Tutorials und Dokumentation könnten von Interesse sein:</p> <ul> <li><a href="http://dev.opera.com/addons/extensions/">Opera Extensions: Documentation</a></li> <li><a href="http://developer.chrome.com/extensions/packaging.html">Chrome Extensions: Packaging</a></li> <li><a href="https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/">Firefox SDK Documentation</a></li> <li><a href="https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/getting-started-with-cfx.html">Firefox SDK: Getting Started with cfx</a></li> <li><a href="https://developer.mozilla.org/en-US/docs/Installing_Extensions_and_Themes_From_Web_Pages">Firefox: Installing Extensions and Themes From Web Pages</a></li> </ul> Mozilla Dev Derby: Pete’s Adventure Fri, 01 Mar 2013 21:29:00 +0100 http://www.sebadorn.de/2013/03/01/mozilla-dev-derby-petes-adventure http://www.sebadorn.de/2013/03/01/mozilla-dev-derby-petes-adventure Seba Informatik <p class="illu"><img style="width: 600px; height: 420px;" src="//sebadorn.de/media/2013/03/petes-adventure.png" alt="Pete’s Adventure" /></p> <p>Im Januar habe ich zum zweiten Mal bei der <a href="https://developer.mozilla.org/en-US/demos/devderby">Mozilla Dev Derby</a> einen Beitrag eingereicht. Das Thema lautete diesmal <strong>Drag&amp;Drop</strong> und da dachte ich mir, dass sich doch etwas Hübsches finden lassen müsste. Eine erste Idee, auf die ich mich ziemlich versteift hatte, gab ich später doch wieder auf – ich hätte meinen eigenen Ansprüchen nicht Genüge tun können. Von mir stammt nun <a href="https://developer.mozilla.org/ms/demos/detail/petes-adventure">Pete's Adventure</a> (<a href="https://sebadorn.de/devderby/dragndrop/">Mirror</a>), eine kurze interaktive Geschichte, mit der ich doch sehr zufrieden bin. <a href="https://github.com/sebadorn/Mozilla-Dev-Derby--Drag-and-Drop">Code und Ressourcen sind auf GitHub zu finden.</a></p> <h3>Drag&amp;Drop</h3> <p>Drag&amp;Drop mit JavaScript ist nicht schwer. Ein paar wenige (dumme) Besonderheiten muss man nur kennen. Zum Beispiel, dass es in Firefox nur funktioniert, wenn auch irgendwelche Daten gesetzt sind. Ein leerer String reicht da schon:</p> <pre class="brush: js">var note = document.getElementById( "note" ); note.addEventListener( "dragstart", function( e ) { e.dataTransfer.setData( "text/plain", "" ); }, false );</pre> <p>Ein Drop wird zudem nur erkannt, wenn man die Events <code>dragenter</code> und <code>dragover</code> für das Element stoppt:</p> <pre class="brush: js">function stop( e ) { e.preventDefault(); } var dropzone = document.getElementById( "dropzone" ); dropzone.addEventListener( "dragenter", stop, false ); dropzone.addEventListener( "dragover", stop, false ); dropzone.addEventListener( "drop", doSomething, false );</pre> <p>Das dürften auch schon die größten Stolpersteine gewesen sein. Nun warte ich weiter auf das Ergebnis des Wettbewerbs, das eigentlich sehr bald verkündet werden müsste.</p> <hr /> <p><strong>Update 2013-03-02:</strong> Ha, nächster Tag und das Ergebnis ist da. <a href="https://hacks.mozilla.org/2013/03/announcing-the-winners-of-the-january-2013-dev-derby/">Erster Platz!</a></p> <hr /> BOINC – CPU-Zeit spenden für die Wissenschaft Sun, 28 Oct 2012 18:35:00 +0100 http://www.sebadorn.de/2012/10/28/boinc-cpu-zeit-spenden-fuer-die-wissenschaft http://www.sebadorn.de/2012/10/28/boinc-cpu-zeit-spenden-fuer-die-wissenschaft Seba Informatik <p class="illu"><img style="width: 600px; height: 251px;" src="//sebadorn.de/media/2012/10/boinc_1.png" alt="BOINC logo" /></p> <p>Nicht jeder wissenschaftlichen Forschungsgruppe steht ein Super-Rechner zur Ver&shy;fügung. Eine Möglichkeit, dennoch aufwendige Simulationen durchführen zu lassen, ist die <strong>Berkeley Open Infrastructure for Network Computing</strong> – kurz BOINC. Das Projekt basiert dabei auf freiwilliger Unterstützung durch zumeist Privatpersonen, die ihre Rechenleistung zur Verfügung stellen. Die installierten Clients erhalten Arbeitspakete, berechnen diese und senden ihre Ergebnisse zurück an das Projekt-Backend.</p> <h3>Client</h3> <p class="illu"><img style="width: 600px; height: 330px;" src="//sebadorn.de/media/2012/10/boinc_2.jpg" alt="BOINC client" /></p> <p>Der Client existiert sowohl für Windows, Mac als auch Linux – nicht zuletzt dank Freigabe des Quellcodes in 2003. Ziel des Clients ist es, <em>ungenutzte</em> CPU-Zeit zu verwenden: Sollte man gerade nur Musik hören, im Internet surfen oder andere unstrapaziöse Dinge erle&shy;digen. In den Optionen lässt sich auch einstellen, ob nur zu bestimmten Tageszeiten ge&shy;rechnet werden soll und ob ein CPU-Auslastungslimit zu beachten ist. Natürlich kann man das Programm auch einfach von Hand starten und beenden – was meine Vorgehens&shy;weise ist.</p> <p><strong>Bedenken</strong> sollte man allerdings, dass gerade der Dauerbetrieb eine nahezu durch&shy;gängige Belastung der CPU bedeutet – und damit höherer Stromverbrauch und Wärme&shy;entwicklung. Fängt plötzlich der Lüfter an aufzudrehen, sollte man es vielleicht doch lieber sein lassen oder zumindest einen Blick auf die Temperaturen haben.</p> <h3>Teilnehmen</h3> <ul> <li>Alle relevanten Informationen finden sich auf der offiziellen BOINC-Seite: <a href="https://boinc.berkeley.edu/">https://boinc.berkeley.edu/</a>.</li> <li>Dort sind ebenfalls zahlreiche <a href="https://boinc.berkeley.edu/projects.php">verfügbare Projekte</a> aus verschiedenen Forschungsgebieten aufgelistet.</li> <li>Ich selbst nehme bisher nur am <a href="http://www.worldcommunitygrid.org">World Community Grid</a> teil.</li> </ul> Der erste Selbstbau-PC Sun, 19 Aug 2012 19:56:00 +0200 http://www.sebadorn.de/2012/08/19/der-erste-selbstbau-pc http://www.sebadorn.de/2012/08/19/der-erste-selbstbau-pc Seba Informatik <p class="illu"><img style="width: 600px; height: 360px;" src="https://sebadorn.de/media/2012/08/pc.png" alt="PC" /></p> <p>Lang hat es gedauert. Endlich habe ich einen neuen PC. Nachdem ich zweimal Fertigsysteme zurückgeschickt habe, entschloss ich mich, selbst Hand anzulegen. Dazu musste ich mich nur erst einmal informieren, da meine Hardware-Kenntnisse nur knapp über 0 schwebten. So habe ich mich durch Reviews, <a href="http://www.computerbase.de/forum/showthread.php?t=215394">Empfehlungen</a> und <a href="http://tinyurl.com/falconguide">Info-Grafiken</a> gearbeitet und mir <a href="http://www.youtube.com/watch?v=uUY0tP5jYIo">ein zwei&shy;stün&shy;diges Video angeschaut</a>, wie man die Teile dann zusammenbaut.</p> <!--more--> <p>Schließlich kam der große Moment: Eingeschaltet und das <a href="http://de.wikipedia.org/wiki/Extensible_Firmware_Interface">EFI</a> lud, wunderbar! Die Festplatte gab Klick&shy;laute von sich und wurde nicht erkannt. Reklamation und weiter ging es einige Tage später.</p> <p>Neue Festplatte ist da. Wenig euphemistisch baute ich sie ein, doch – Tada! – sie funktioniert. Also konnten die Betriebssysteme und Software folgen. Als ich dann Spiele ausprobierte, stutzte ich. Die Grafikkarte kam auf 60°C beim Browsen und 90°C (Lüfter auf 90%) nach 5&nbsp;Minuten Spiel (Minecraft in "Balanced"-Modus und Bastion). Also Reklamation und weiter ging es eine Woche später. Zu diesem Zeitpunkt war ich extrem angepisst; beste <em>Ich-habe-keinen-Bock-mehr-auf-den-Scheiß!</em>-Stimmung.</p> <p>Vor dem Einbau schaute ich die Ersatz-Grafikkarte noch einmal streng an. Ein Kabel hing leicht in den Raum zwischen Karte und Lüfter. Entweder hätte es den Lüfter blockiert oder es wäre mit der Zeit kaputtgeschabt worden. Mit ein wenig Kabelbinder war dem zum Glück leicht beizukommen. Die Temperaturen liegen nun in einem guten Bereich<sup>[1]</sup>.</p> <h3>Eckdaten (grob)</h3> <p>Weder habe ich vor zu overclocken, noch plane ich Grafikkarten in SLI zu verwenden. Aus&shy;reich&shy;ender Support von Linux ist zudem ein Muss.</p> <ul> <li><strong>Motherboard:</strong> ASRock Z68 Extreme3 Gen3</li> <li><strong>CPU:</strong> i5 2400</li> <li><strong>GPU:</strong> GTX 560 Ti</li> <li><strong>RAM:</strong> 8GB 1333MHz</li> <li><strong>HDD:</strong> WD Caviar Black 500GB</li> </ul> <h3>Gelerntes</h3> <p>Man kann CPUs <strong>undervolten</strong>. Weniger Saft bedeutet weniger Wärme. Für die Hardware be&shy;steht dabei angeblich kein Risiko und schlimmstenfalls schmiert der PC unter Last ab. Dann setzt man die Einstellung einfach wieder eine Stufe zurück. Ich bin jetzt bei einem Offset von -1.125V, was mir ca. -3°C bringt.</p> <p>Man kann <strong>Arbeitsspeicher übertakten</strong> und es gibt Modelle mit Heatsinks für bessere Wärmeableitung. Ich bezweifel ganz ehrlich, dass man außerhalb von Benchmarks eine Verbesserung beobachten kann.</p> <p>Es gibt <a href="http://www.amazon.de/s/ref=nb_ss_w/303-8157015-2701015?__mk_de_DE=%C5M%C5Z%D5%D1&url=search-alias%3Daps&field-keywords=gtx+690&x=0&y=0">Grafikkarten, die teurer sind</a> als der PC, den ich mir nun zusammengestellt habe. Und es gibt Leute, die kaufen sich gleich zwei und betreiben sie in SLI.</p> <h3>Fazit</h3> <p>Ganz so leise wie mein alter ist er nicht. Damit habe ich auch nie gerechnet, schon allein weil im alten die Grafikkarte noch passiv gekühlt war und der neue über zwei Gehäuselüfter ver&shy;fügt. Aber ich habe mich schnell an das leichte Summen gewöhnt – für das ich erst einmal die Lüfter-Geschwindigkeiten herunterdrehen musste, die standard&shy;mäßig auf „Full On“ standen.</p> <p>Wirklich gespart habe ich durch das Zusammenbauen nicht. Dafür steckt das drin, was ich wollte. Und als Informatiker habe ich auch auf diesem Gebiet endlich mal ein wenig Erfahrung gesammelt.</p> <hr /> <p class="annotation">[1] Ungefähr 50 bis 60°C bei 40 bis 50% Lüftergeschwindigkeit nach knapp 1&nbsp;Stunde Minecraft/Bastion. Wirklich höher ging es danach nicht mehr. Im Normalbetrieb sind es recht konstant 42°C bei 30%.</p> <p class="annotation">Einbau der CPU: <em>„Habe ich die CPU richtig herum draufgelegt? Nochmal nachschauen. Ja, okay. Dann schließe ich jetzt … Liegt die wirklich richtig drauf? Ah, okay, war richtig. Das lässt sich aber schwer schließen. Ohje&shy;ohje&shy;ohje&shy;ohje… Okay, ist zu.“</em></p> <p class="annotation">Festschrauben des Motherboards: <em>„Und jetzt hier die … Fuck, mir ist die Schraube runtergefallen auf das Board! Hoffentlich hat das nichts zerkratzt! Jetzt ganz vorsichtig aufsammeln … komm her, du! Gah. Gah! Okay.“</em></p> Zusatzstory zum Grafiktreiber-Drama: Broken pipe Fri, 22 Jun 2012 18:07:00 +0200 http://www.sebadorn.de/2012/06/22/zusatzstory-zum-grafiktreiber-drama-broken-pipe http://www.sebadorn.de/2012/06/22/zusatzstory-zum-grafiktreiber-drama-broken-pipe Seba Informatik <p class="illu"><img style="width: 600px; height: 800px;" src="https://sebadorn.de/media/2012/06/broken_pipe.png" alt="Could not write bytes: Broken pipe" title="Ich dokumentiere meine eigene Torheit in der Hoffnung, jemand mit dem gleichen Problem findet hier die Lösung." /></p> <p>So geschehen nach dem Wechsel vom Nouveau-Treiber zum aktuellen NVIDIA-Treiber. Direkt nach Auswahl des Betriebssystems im Boot-Menü war der Bildschirm schwarz und darauf stand nur „Could not write bytes: Broken pipe“. Ich habe schon den Rest des Tages für eine Neuinstallation und -einrichtung draufgehen sehen.</p> <p>Dazu kam es nicht. Dank einer panisch eingeworfenen Live-CD konnte ich am einzigen Datei&shy;ort nachschauen, von dem ich weiß, dass er mit dem Grafiktreiber zu tun hat: <code>/etc/X11/</code>. Da lag dann auch meine <em>alte</em> <code>xorg.conf</code> in der dick und fett stand <strong><code>Driver "nouveau"</code></strong> …</p> <p>Datei gelöscht. Neugestartet. Geht wieder. Ich liebe Live-CDs.</p> Die System-Upgrade-Odyssee ODER Ringkampf mit einem Schuppentier Mon, 18 Jun 2012 01:30:00 +0200 http://www.sebadorn.de/2012/06/18/die-system-upgrade-odyssee-oder-ringkampf-mit-einem-schuppentier http://www.sebadorn.de/2012/06/18/die-system-upgrade-odyssee-oder-ringkampf-mit-einem-schuppentier Seba Informatik <p class="illu"><img style="width: 600px; height: 400px;" src="https://sebadorn.de/media/2012/06/Pangolin.png" alt="Pangolin" /></p> <p>Vor ein paar Wochen hielt ich es für eine gute Idee, nun doch einmal mein Ubuntu-Betriebs&shy;system zu aktualisieren. Während bei mir noch <em>Natty Narwhal</em> (11.04) lief, war bereits <em>Precise Pangolin</em> (12.04) im Umlauf. Mein Ziel war es also – ohne völlige Neuinstallation –, <a href="http://de.wikipedia.org/wiki/Ubuntu#Versionstabelle">von Natty auf Oneiric auf Precise</a> zu aktualisieren. So nahm das Unheil seinen Lauf.</p> <!--more--> <h3>1. Hürde: Oneiric -> Precise</h3> <blockquote> <p><code>ERROR Dist-upgrade failed: 'E:Fehler: Unterbrechungen durch pkgProblemResolver::Resolve hervorgerufen; dies könnte durch zurückgehaltene Pakete verursacht worden sein.'</code></p> </blockquote> <p>Also … irgendetwas Installiertes steht wie eine Kuh auf den Gleisen? Eine Log-Datei von Interesse zum versuchten Upgrade ist zu finden unter <code>/var/log/dist-upgrade/apt.log</code>. Das war allerdings für mich Neuland. Das häufig auftauchende Schlüsselwort „Broken“ schien aber eine gute Fährte zu sein. Interessant wurde dann folgende Entdeckung:</p> <blockquote> <p><code>Broken libopencv...<br /> &nbsp;&nbsp;Considering libcv...<br /> Broken libcv...<br /> &nbsp;&nbsp;Considering libopencv...</code></p> </blockquote> <p>Da dreht sich doch etwas im Kreis? Tatsächlich, nachdem ich libopencv und libcv deinstalliert hatte, lief das Upgrade auf Precise.</p> <h3>2. Drama: Der Grafiktreiber</h3> <hr /> <p><strong>Update vom 18.06.12:</strong> Mit dem NVIDIA-Treiber 302.17 scheint das Problem behoben zu sein. Dafür am besten das <a href="https://launchpad.net/~ubuntu-x-swat/+archive/x-updates?field.series_filter=precise">X Updates-PPA</a> einbinden.</p> <hr /> <p><strong>Update vom 19.06.12:</strong> Vielleicht auch nicht. Vielleicht habe ich auch einfach nur Pech und es liegt noch mehr im Argen als nur der Grafiktreiber.</p> <hr /> <p><strong>Update vom 29.06.12:</strong> Weiterhin Freezes. Bin wieder bei Nouveau.</p> <hr /> <p><strong>Update vom 30.06.12:</strong> Neverending story. Auch mit Nouveau Freezes. Mit dem Linux-Kernel 3.2 ging es wohl bergab. Hoffentlich bleibt mir der Scheiß bei meinem neuen Rechner erspart. Was ich noch probieren könnte, ist GRUB als Boot-Option <code>intel_iommu=off</code> mitzugeben. Das hat wohl bei Leuten mit ähnlichen Problemen geholfen.</p> <hr /> <p>Zuerst fiel mir nur auf, dass es in Unity Darstellungsfehler gab – verzerrte Desktopsymbole, verflackernter Hintergrund bei transparenten Flächen. Ähnliches fand sich dann aber auch mit der GNOME Shell. Trat ein Darstellungsfehler erst auf, spielte sich der Rest auch nur noch in Rucklern ab. Auch komplette Freezes kamen vor, bei denen nur noch ein Kaltstart half.</p> <p>Was nun, wie ich vermute, die Ursache war: Die Kombination aus neuem Linux-Kernel + aktuellem NVIDIA-Treiber + meine uralte NVIDIA-Grafikkarte. <a href="https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers/+bug/982485">Auf jeden Fall sind Probleme mit dem NVIDIA-Treiber bekannt.</a></p> <p>Mein erster Ansatz war es, die noch neuere Version 295.53 aus dem <a href="https://launchpad.net/~ubuntu-x-swat/+archive/x-updates?field.series_filter=precise">X Updates-PPA</a> zu nehmen, anstatt der Version 295.44 aus dem Standard-PPA. Nun, es hat nullkommanichts gebracht.</p> <p>Schließlich wechselte ich zum OpenSource-Treiber „Nouveau“. Jetzt läuft alles relativ stabil, aber leider mit starken Abstrichen. Auch nur halbwegs anspruchsvolle 3D-Anwendungen kann ich vergessen – Minecraft lief zuletzt mit OptiFine, Sichtweite „Short“ und kleinem Fenster mit 20&nbsp;FPS.</p> <h3>Währenddessen Gelerntes</h3> <ul> <li><em>Unity</em> und <em>GNOME Shell</em> können nicht in friedlicher Koexistenz neben&shy;einander liegen. Während Unity <em>Compiz</em> als Fensterverwaltung benötigt, startet GNOME Shell erst gar nicht, wenn Compiz läuft.</li> <li>Mit <code>gnome-session-quit --force-logout</code> kann man sich abmelden. Der Login-Screen ist der sichere Hafen, der (fast) immer funktioniert.</li> <li>Mit <code>sudo service lightdm restart</code> startet man die Desktopverwaltung neu. In der Regel wird man dadurch auch ausgeloggt und landet wieder beim Login-Screen.</li> <li>Grafiktreiber sollte man wirklich nicht einfach per <code>apt-get remove</code> deinstallieren. Das birgt viel Unheil. Stattdessen über die GUI „Zusätzliche Treiber“ gehen.</li> </ul> <hr /> <p class="annotation">&#x95; Im X Updates-PPA liegt mittlerweile der NVIDIA-Treiber 302.17. Ich muss mich mal informieren, ob damit das Problem behoben sein könnte.</p> <p class="annotation">&#x95; Weder GNOME Shell noch Unity überzeugen mich so richtig. Dass die GNOME Shell quasi nur aus JavaScript und CSS besteht, finde ich auch fragwürdig. Ich meine: JavaScript! – ich bitte euch!</p> <p class="annotation">&#x95; Um dem auch etwas Positives abzugewinnen: Mein Linux-Fu ist wieder leicht stärker geworden.</p> <p class="annotation">&#x95; Oh, aber 720p-Videos lassen sich ohne Murren abspielen. Anime, yey~.</p> Ein meisterhafter Übergang Mon, 14 May 2012 21:00:00 +0200 http://www.sebadorn.de/2012/05/14/ein-meisterhafter-bergang http://www.sebadorn.de/2012/05/14/ein-meisterhafter-bergang Seba Informatik Studentendasein <p>Vor ein paar Monaten habe ich meine Bachelor-Thesis ab&shy;geschlossen und ziemlich gut be&shy;standen. Woohoo! Das Thema hatte den <em>(Auf den Punkt)</em>-Titel und <em>(Das verdient fette Schrift)</em>-Titel <strong>„Browserbasiertes, kollaboratives Whiteboard“</strong>. Das Projekt ist allerdings nicht auf meinem <a href="http://www.github.com/sebadorn">GitHub-Account</a> zu finden, da ich mit dem Ergebnis ehrlich gesagt wenig zufrieden bin. Das <em>(Warum denn?)</em> näher auszuführen, würde nur ein paar Zeilen jaunernde Eigen&shy;schuldzuweisung <em>(Nichts anderes ist es.)</em> bedeuten – lassen wir das also. Hier habe ich noch ein nichts-sagendes Foto meiner gebundenen Thesis und gehe dann zum nächsten Punkt über, der sich in meinem Leben fast so nahtlos angeschlossen hat, wie der nächste Absatz.</p> <p class="illu forced"><img src="https://sebadorn.de/media/2012/05/bachelorthesis.jpg" style="width: 600px; height: 330px;" alt="Cover Bachelor-Thesis" /></p> <p>Obwohl ich es lange Zeit nicht vorhatte, habe ich mich entschlossen ein Masterstudium an&shy;zuschließen. Warum nun doch? Mein Praktikums-Chef hat es mir damals empfohlen, mein Prof sowieso und <em>(Sehr auschlaggebend!)</em> das Masterstudium ist vom Kurt-Schumacher-Ring hochgezogen zu Unter den Eichen, wo ich ohnehin schon die ganze Zeit studiert habe. So bin ich nun seit rund fünf Wochen wieder im 1. Semester. Diesmal auf dem Weg zum „Master of Science in Informatik“.</p> <!--more--> <ul> <li><strong>Formale Modelle</strong> und <strong>Logik und Berechenbarkeit</strong> gehen soweit. Könnte damit zusammenhängen, dass ich leider immer seltener die Übungsauf&shy;gaben vorbereite.</li> <li>In <strong>Machine Learning</strong> muss man sich erst ein wenig reinfinden, aber es macht Spaß. Jetzt läuft auch die Projektphase an, in der ich verschiedenen KIs das Vier-Gewinnt-Spielen beibringen werde.</li> <li><strong>Kryptologie</strong> interessiert mich wirklich sehr, aber die Aufgabenblätter sind für mich viel Aufholjagd – ich hechel immer mal mehr, mal weniger hinter&shy;her. Das Problem ist hier auch die Sprache C, in der meine Kenntnisse bestenfalls als anfängerhaft einzustufen sind.</li> <li>Dann gibt es da noch <strong>Constraint-basierte Systeme</strong> und <em>(Holy shit!)</em> die Sprache Prolog ist ein einziger Brainfuck. Man kann es Pimaldaumen darauf herunterbrechen, dass man im Code nicht mehr den Lösungsweg vorgibt, sondern hinreichend das Problem beschreiben muss. No Loops, Recursion only, Final Destination.</li> </ul> <p>Es mag an meinen gewählten Kursen liegen, aber das Studium hat ziemlich angezogen. Er&shy;staunlicherweise schaffe ich es, dreimal die Woche gegen 5&nbsp;Uhr aufzustehen. <em>(Tages&shy;beginn lässt sich am dunklen Blau zwischen den Rollladenlamellen noch nicht recht erahnen.)</em> Nach all den Jahren habe ich endlich den geheimen Trick zu einem erträglichen frühen Morgen ge&shy;funden: Dehnübungen. Der Wecker spielt meine Musik, ich stehe vorm Bett und strecke und recke mich für ein paar Minuten. „Topfit“ mag sicherlich noch immer anders aussehen, aber es reicht, es reicht.</p> <p>Im Bad steht eine Temperaturanzeige für Außen. Kurz bevor die Sonne aufgeht, wird es immer nochmal ein paar Kommagrade kühler. Als wäre die <em>(Sonne)</em> Wärme eine Welle am Strand, die sich erst aufbaut und man zusehen kann, wie das Wasser zurückgezogen wird, bevor es dann doch wieder über den Sand spült.</p> <p>Wegen ein wenig Aufgabenstress gab es daher in letzter Zeit auch keine Blogaktivität. Ich habe einen Zettel mit kleinen Themen für Einträge geschrieben; die verwurste ich in nächster Zeit mal. So wie mit diesem hier. Das meiste wird sehr Seba-spezifisches Zeug sein.</p> Wenn die Ubuntu-Live-CD/DVD nicht mit dem Nouveau-Treiber starten will Sun, 15 Apr 2012 15:00:00 +0200 http://www.sebadorn.de/2012/04/15/wenn-die-ubuntu-live-cd-dvd-nicht-mit-dem-nouveau-treiber-starten-will http://www.sebadorn.de/2012/04/15/wenn-die-ubuntu-live-cd-dvd-nicht-mit-dem-nouveau-treiber-starten-will Seba Informatik <p class="illu"><img style="width: 600px; height: 376px;" src="https://sebadorn.de/media/2012/04/livedvd-fehler.jpg" /></p> <p>Wie auf Twitter vielleicht mitbekommen, bin ich einmal wieder ein wenig frustiert, weil ein be&shy;stellter PC erstens zu laut ist, und zweitens die Ubuntu-Live-DVD nicht startet. Gegen Ersteres kann ich selbst nicht viel unternehmen ohne jegliche Gewährleistung aus dem Fenster zu schmeißen. Für das Live-DVD-Problem habe ich wenigstens heute eine Lösung gefunden.</p> <p>Der erste Screenshot zeigt das Problem. Es wird von der DVD (Ubuntu 11.10, 64bit) gebootet, es kommt kurz der pink-lilane Screen mit dem Tastatur- und Männchen-Symbol und danach die Text-Ausgabe aus dem Bild oben. Ab da geht nichts mehr.</p> <h3>Woran mag es liegen?</h3> <p>Okay, vielleicht ist die DVD fehlerhaft? Nope, sie funktioniert sowohl mit meinem aktuellen (bald 6 Jahre alten, flüsterleisen ALDI-)PC, als auch am Rechner meines Vaters. <em>Fehlerhafte DVD: Ausgeschlossen.</em></p> <p>Sind da verwertbare Informationen in dem Text? Hm, <code>modprobe</code>, das war doch irgendetwas mit Treibern? Weiter oben steht auch mehrmals das Wort <code>driver</code> und … <code>nouveau</code>? Das ist der OpenSource-Treiber für NVIDIA-Grafikkarten! Zum Google-Mobil! <sup>Na na na na na na na na …</sup></p> <h3>So hat es nun geklappt</h3> <p>DVD wird gelesen, bootet und der pinka-lilane Screen mit der Tastatur und dem Männchen ersch&shy;eint. Drück jetzt <code>[Enter]</code> um in das erweiterte Menü zu kommen. Falls nach einer Sprache gefragt wird, wähle deine Sprache mit <code>[Enter]</code> aus. Drück die Taste <code>[F6]</code> für die erweiterten Bootoptionen und setze ein Häkchen bei <code class="highlight">nomodeset</code>. (Mit <code>[Esc]</code> lässt sich das Untermenü wieder schließen.) Starte das Live-System. Phase&nbsp;1 ist abgeschlossen. Bei mir hat das Ubuntu-Live-System nun geladen.</p> <!--more--> <p>Jetzt möchte man aber vermutlich noch den richtigen Grafikkartentreiber ausprobieren. Norm&shy;alerweise lässt sich dieser unter dem Punkt „Zusätzliche Treiber“ bequem auswählen. Bei mir war das Fenster leer. Sitzt du im selben Boot? Fuck GUIs, wir nehmen das Terminal:</p> <pre class="brush: bash">sudo apt-get install nvidia-current sudo nvidia-xconfig </pre> <p>Damit haben wir den aktuellen propietären NVIDIA-Treiber heruntergeladen, installiert und ausgewählt. Damit die Änderung auch in Kraft tritt, muss der X-Server (die grafische Ober&shy;fläche) neugestartet werden. Entweder über einen Neustart – was bei einer Live-CD nicht in Frage kommt, da alle Änderungen dadurch zunichte gemacht werden – oder per Terminal.</p> <pre class="brush: bash">sudo service lightdm restart</pre> <p>Sollte es <code>lightdm</code> nicht geben, versuche es mit <code>gdm</code>. Der Benutzer wird daraufhin abgemel&shy;det (alle Fenster werden geschlossen etc.) und neu angemeldet. Für mich hat es geklappt. Das Fenster für propietäre/zusätzliche Treiber war zwar immer noch leer, aber wie man sieht, wird der NVIDIA-Treiber verwendet:</p> <p class="illu"><img style="width: 600px; height: 350px;" src="https://sebadorn.de/media/2012/04/livedvd-nvidia.jpg" /></p> <p>Das ganze dann auch mit einer Installation auszuprobieren, tja, dazu komme ich vorerst nicht, da noch das Problem mit der Lautstärke des PCs besteht. Also wird er sehr wahrscheinlich zurückgeschickt. Aber für die Zukunft ist es schonmal gut, diese Lösung gefunden zu haben.</p> <hr /> <p class="annotation">Wieder einmal wird ein selbstgebautes System interessanter. Was ich Anfänger in den letzten Monaten über PC-Hardware gelernt habe … da kann ich eigentlich auch den nächsten Schritt machen und mal selbst eine Maschine zusammenbauen.</p>