Neither one nor Many
Software engineering blog about my projects, geometry, visualization and music.
Indien je urenregistratie moet invullen met begin- en eindtijden is het soms lastig als je dat achteraf doet. Soms weet je nog wel uit het hoofd wat je gedaan hebt, soms zoek je dat op in (verstuurde) e-mails, of aantekeningen, agendapunten, enz.
Als je dan toch de tijden moet reverse-engineeren, kun je het jezelf natuurlijk ook wat makkelijker maken door in een simpele lijst je werkzaamheden te tikken. Op te geven hoeveel tijd het bij elkaar moet zijn, van een aantal zelf de tijd invullen en vastzetten ('freeze'), een beetje aan knopjes draaien om aan te geven wat meer en wat minder werk is, af en toe op F5 drukken om te zien hoe de taart verdeeld is.
Indien tevreden, kun je het hieruit overkopieeren. Mij heeft het al meerdere malen geholpen
Executable hier te downloaden.
I like both equally but am a little more familiar with git. Although now I prefer bitbucket over github due to private repositories being free . Actually I think currently mercurial tool support on windows seems better too (I didn't expect TortoiseHG to be so neat, as I had only used Tortoise CVS in the past, and I didn't like it, but thats probably due to the nature of CVS).
Some notes, small annoyances I encountered on my system and how I fixed them.
I'm actually using Adam Pash' mouser for a few years now on vimkeys with a small patch to improve the performance. A few days ago me and Marijn Koesen talked about an OSX / platform independent version. We used to talk about this before, but now being familiar with wxWidgets I realised creating a clone in C++ was probably easy.
My first windows-only version used a simple timer and a few tricks to prevent keypresses from escaping to the active window. The current version is now improved, and no longer uses a timer. I was also able to minimize an artifact the original mouser also had with the flickering of the blue shapes; it is really fast and transparency appears not to slow it down either (it did on my PC with the original mouser).
The only thing missing is multiple-monitor support at this moment, but give us a few days. Having only one monitor isn't helping .
EDIT 11-04-2010: A nice SuperMouser milestone day: Marijn added undo key 'U'. I added multiple monitor support for windows, 'M' key. The latter in need of some testing (I tested it by hardcode-dividing my screen into two halfs).
Marijn Koesen is collaborating and also adding OSX support. The GTK2 version is coming soon. And multiple-monitor support currently has highest priority.
It's all on github for download. The binary for windows is on github as well.
Roadmap:
Probably lots of them on the internet, but I made one myself (don't ask me why) Just searched for some encode and decode functions w/google codesearch, then added a simple main().
Usage: base64conv.exe [OPTION] [SOURCE] [DEST]
where [OPTION] is either --to-base64 or --from-base64,
[SOURCE] is the file to convert
[DEST] is the target/output file (will be overwritten)
Examples: base64conv.exe --to-base64 somefile.exe somefile.txt
base64conv.exe --from-base64 somefile.txt somefile.exe
(something like ./base64conv on linux)
Here you have it:
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 Rene Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include <iostream>
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static char *
base64_encode(const unsigned char *input, int length)
{
/* http://www.adp-gmbh.ch/cpp/common/base64.html */
int i=0, j=0, s=0;
unsigned char char_array_3[3], char_array_4[4];
int b64len = (length+2 - ((length+2)%3))*4/3;
char *b64str = new char[b64len + 1];
while (length--) {
char_array_3[i++] = *(input++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; i < 4; i++)
b64str[s++] = base64_chars[char_array_4[i]];
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; j < i + 1; j++)
b64str[s++] = base64_chars[char_array_4[j]];
while (i++ < 3)
b64str[s++] = '=';
}
b64str[b64len] = '\0';
return b64str;
}
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
static unsigned char *
base64_decode(const char *input, int length, int *outlen)
{
int i = 0;
int j = 0;
int r = 0;
int idx = 0;
unsigned char char_array_4[4], char_array_3[3];
unsigned char *output = new unsigned char[length*3/4];
while (length-- && input[idx] != '=') {
//skip invalid or padding based chars
if (!is_base64(input[idx])) {
idx++;
continue;
}
char_array_4[i++] = input[idx++];
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = strchr(base64_chars, char_array_4[i]) - base64_chars;
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
output[r++] = char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = strchr(base64_chars, char_array_4[j]) - base64_chars;
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++)
output[r++] = char_array_3[j];
}
*outlen = r;
return output;
}
/**
* This is the interface I added to these two convert functions,
* that were originally written by Rene Nyffenegger.
* Found them via google codesearch, specifically in
* http://jacksms.googlecode.com/svn/base64.cpp and
* http://jacksms.googlecode.com/svn/base64.hh
* 2011, Ray Burgemeestre.
*/
#include <string>
#include <fstream>
using namespace std;
void display_usage(const char *programname)
{
cerr << "Usage: " << programname << " [OPTION] [SOURCE] [DEST]" << endl
<< " where [OPTION] is either --to-base64 or --from-base64," << endl
<< " [SOURCE] is the file to convert" << endl
<< " [DEST] is the target/output file (will be overwritten)" << endl
<< "" << endl
<< "Examples: base64conv.exe --to-base64 somefile.exe somefile.txt" << endl
<< " base64conv.exe --from-base64 somefile.txt somefile.exe" << endl
<< "(something like ./base64conv on linux)" << endl;
}
int main(int argc, char *argv[])
{
enum Arguments { PROGRAM, OPTION, SOURCE, DEST, ARGS_LEN };
if (argc != ARGS_LEN) {
display_usage(argv[PROGRAM]);
return -1;
} else {
char *sourcedata = NULL;
ifstream::pos_type sourcedataLen = 0;
ifstream sourcefile (argv[SOURCE], ios::in|ios::binary|ios::ate);
if (sourcefile.is_open()) {
sourcedataLen = sourcefile.tellg();
sourcedata = new char [static_cast<int>(sourcedataLen)];
sourcefile.seekg(0, ios::beg);
sourcefile.read(sourcedata, sourcedataLen);
sourcefile.close();
cout << "source file " << argv[SOURCE] << " loaded" << endl;
} else {
cerr << "unable to open source file, " << argv[SOURCE] << endl;
return -1;
}
if (strcmp(argv[OPTION], "--to-base64") == 0) {
cout << "converting to base64" << endl;
char *base64string = base64_encode(reinterpret_cast<unsigned char *>(sourcedata),
static_cast<int>(sourcedataLen));
ofstream outputfile(argv[DEST], ios::out|ios::binary|ios::ate);
if (outputfile.is_open()) {
outputfile.write (base64string, strlen(base64string));
outputfile.close();
cout << "base64 string written to dest file, " << argv[DEST] << endl;
} else {
cerr << "unable to open dest file, ", argv[DEST];
return -1;
}
}
else if (strcmp(argv[OPTION], "--from-base64") == 0) {
cout << "converting from base64" << endl;
int outlen = 0;
unsigned char *decodedbase64string= base64_decode(sourcedata,
static_cast<int>(sourcedataLen), &outlen);
ofstream outputfile(argv[DEST], ios::out|ios::binary|ios::ate);
if (outputfile.is_open()) {
outputfile.write(reinterpret_cast<const char *>(decodedbase64string), outlen);
outputfile.close();
cout << "decoded base64 string written to dest file, " << argv[DEST] << endl;
} else {
cerr << "unable to open dest file, ", argv[DEST];
return -1;
}
} else {
display_usage(argv[PROGRAM]);
cerr << "@@ invalid [OPTION] specified.";
return -1;
}
delete[] sourcedata;
}
}
If you need to protect yourself from yourself, like I needed to do. Then this simple notepad replacement executable may be for you . Click here to download. What it does is:
When you own New text document (1).txt until New text document (19).txt, asdf.txt, jlksdjflkajsd.txt, etc., on your desktop. Then you have the same problem I had: using notepads for storing temporary buffers and things like that. With this replacement you are forced to keep just one buffer, which is automatically saved with CTRL+S. And when you do want to keep specific stuff, you select that text and CTRL+D (dispatch to file).
To install it you need to do the following:
Here's the copy (it is included in my .zip). This is not the same script I used, but my guess is it'll probably work:
@echo off
takeown /f c:\windows\syswow64\notepad.exe
cacls c:\windows\syswow64\notepad.exe /G Administrators:F
takeown /f c:\windows\system32\notepad.exe
cacls c:\windows\system32\notepad.exe /G Administrators:F
takeown /f c:\windows\notepad.exe
cacls c:\windows\notepad.exe /G Administrators:F
copy c:\windows\syswow64\notepad.exe c:\windows\syswow64\notepad.exe.backup
copy c:\windows\system32\notepad.exe c:\windows\system32\notepad.exe.backup
copy c:\windows\notepad.exe c:\windows\notepad.exe.backup
rem this has to be executed from within the installation dir.
copy notepad.exe c:\windows\syswow64\notepad.exe
copy notepad.exe c:\windows\system32\notepad.exe
copy notepad.exe c:\windows\notepad.exe
You can read the INSTALL file in the .zip bundle for details.
Example configuration
; configuration for notepad.exe replacement
editor=C:\Program Files\Vim\vim72\gvim.exe
file=C:\temp.txt
title=Untitled
maximized=0
When navigating directories, every now and then pushd .
and popd
just aren't sufficient.
When directory structures tend to become quite deep, I get really annoyed with typing change-directory
’s and wish I had some bookmarks.
That's why some time while ago I created a simple tool for usage within (GNU) screen:
CTRL+X pops up a menu for selecting common directories and files ("bookmarks"!).
This makes switching between those long directory structures a lot more pleasant !
// Configuration file ($HOME/.launcher) [ {"Websites, configuration" : [ {"dir" : "/srv/www/vhosts/ray.burgemeestre.net"}, {"dir" : "/srv/www/vhosts/www.burgemeestre.net"}, {"dir" : "/usr/local/src/mongoose/am"}, {"dir" : "/usr/local/src/mongoose/am/output/ray-blog-burgemeestre-net"}, {"file" : "/etc/apache2/vhosts.d/www-burgemeestre-net.conf"}, {"file" : "/etc/apache2/vhosts.d/ray-blog-burgemeestre-net.conf"}, {"file" : "/etc/apache2/vhosts.d/ray-burgemeestre-net.conf"} ]}, {"Common Lisp" : [ {"dir" : "/var/chroot/lispbot/home/trigen"}, {"file" : "/var/chroot/lispbot/home/trigen/bot.lisp"} ]}, {"C++ projects" : [ {"dir" : "/usr/local/src/launcher"}, {"dir" : "/usr/local/src/launcher/src"}, {"file" : "/usr/local/src/launcher/src/parser.cpp"} ]} ]
Choosing a directory in the menu opens it in a new screen tab. Choosing a file opens a new menu which allows you to dispatch it to specific editors or e-mail it (options currently all hardcoded in the source).
Most of the code is still from 2007, but these last two days I refactored the code a bit. And added some build scripts. (Needless to say I didn't get to implementing all features I had in mind.) It's all on github for download.
It installs /usr/local/bin/launcher
and two helper bash scripts (/usr/local/bin/launch_scr.sh
and /usr/local/bin/launch_scr2.sh
).
You bind it to screen in your .screenrc
with this line: bindkey ^x exec /usr/local/bin/launch_scr.sh
.
(Also for your .screenrc
I recommend using a caption if you don't already with:
caption always "%{Yb} %D %Y-%02m-%02d %0c %{k}|%{G} %l %{k}|%{W} %-w%{+b}%n %t%{-b}%+w"
)
Just wanted to use the 'in practice' part because it sounds so cool . At work I used to have some cool tricks I'd use at customers (test-environments) or local setups to debug. One of my favourite snippets I use all the time is the following.
/**
* Debug helper function for logging and restoring requests. Do not use in
* production environments.
*
* - Logs the URL and request data.
* - With __STATE_DEBUGGER__ parameter in the URL it displays a listing
* of logged requests.
* - Clicking an item in that listing, re-creates that request.
*
* I know it is usually not good practice to have *one* function do multiple
* things. Make sure you know what you're doing ;)
*
* @param writableDirectory directory for saving the requests in files
* @return void (call it solely for it's side-effects ;-))
*/
function statedebugger($writableDirectory = '/tmp')
{
if (!is_dir($writableDirectory) || !is_writable($writableDirectory)) {
trigger_error('parameter writableDirectory needs to exist and be
writable', E_USER_ERROR);
}
$indexFile = $writableDirectory . '/index.txt';
if (!isset($_GET['__STATE_DEBUGGER__'])) {
// Write state file for request
$stateFileName = tempnam($writableDirectory, 'p');
$fd = fopen($stateFileName, 'wb');
fwrite($fd, serialize(array($_SERVER, $_GET, $_POST, $_REQUEST,
!isset($_SESSION) ? array() : $_SESSION)));
fclose($fd);
// Rewrite index
$indexFileContents = file_exists($indexFile) ?
file_get_contents($indexFile) : '';
$fd = fopen($indexFile, 'wb');
fwrite($fd, implode(array($indexFileContents, //INEFFICIENT
$stateFileName, ' ', $_SERVER['REQUEST_URI'], "\n")));
fclose($fd);
} else {
if (!isset($_GET['set_state'])) {
// Show index/listing of states
$indexFileLines = array_reverse(explode("\n",
file_get_contents($indexFile)));
foreach ($indexFileLines as $line) {
if (empty($line))
continue;
list($filename, $requestUri) = explode(" ", $line);
printf("<a href=\"%s%s__STATE_DEBUGGER__&set_state=%s\">%s</a><br/>\n",
$requestUri, (strpos($requestUri, "?") === FALSE ? "?" :
"&"), $filename, $requestUri);
}
exit(0);
} else {
// Restore a specific state
list ($_SERVER, $_GET, $_POST, $_REQUEST, $_SESSION) =
unserialize(file_get_contents($_GET['set_state'])); //DANGEROUS
}
}
}
statedebugger('E:/TPSC/htdocs/CRMS-Test/webframe/templates_c/');
Just paste that in the config, or whatever global headerfile (your software probably has), and it logs all requests to some specified directory. Usually when something goes wrong within a very specific context (a specific user with rights, session variables, page, ..), it would be nice to just log the requests (e.g. with post data), and use that information to re-create the (failing) request. So you can keep pressing F5 in your browser window while fixing the bug. This function does that. Also this can be especially useful when debugging AJAX requests, i.e. without firebug, or you can let the user create the very specific context (with privileges and user settings) that causes some failure, and in that way create the test-case.
(Add __STATE_DEBUGGER__
to the URL as a parameter, and it will show a listing with clickable requests.
Clicking redirects to that page and initializes it with the recorded $_GET, $_POST, $_SESSION
, ...)
(By the way I didn't bother to make this very secure (see file_get_contents($_GET['set_state'])
for example), because this should only be used when debugging. This could easily be improved with some numerical parameters, optional descriptions, limit to fixed number of requests...)
One of the reasons I was using allegro for my project was that they already had (apparantly) a lot of blending modes available. You have set_blender_alpha(), set_blender_hue(), set_blender_color(), but these are not the same as in photoshop. I got some unexpected behaviour from these, and I will edit this post later and elaborate on that some more.
Most of them ignore the r, g, b parameters as well. So it appears they were a big disappointment. By the way, a quick glance at the Allegro 5 source made me think they aren't implemented there either.
That's why I did a quick search and found someone who already collected macro's for all photoshop blending types. By Nathan Moinvaziri. Today, or I should say yesterday (now at 3:17am), I implemented some wrapper functions around it that make it available to allegro, with an API that looks and feels the same as allegro.
I put it on github with a demo program. From the README:
Put al_blend.h in your project somewhere and include it. Or put it in the
allegro include directory, i.e. /usr/local/src/allegro/include or
C:\allegro\include.
Now instead of using the allegro blenders with getpixel() and putpixel(), you
can use put_blended_pixel().
put_blended_pixel():
Writes a pixel into a bitmap with specific blender.
Description:
void put_blended_pixel(BITMAP *bmp, int x, int y, int color,
(*blender_func)(int &basecolor, int &blendcolor));
Example:
put_blended_pixel(screen, x, y, somecolor, blender_lighten);
Available blenders are: blender_darken, blender_multiply, blender_average, blender_add, blender_subtract, blender_difference, blender_negation, blender_screen, blender_exclusion, blender_overlay, blender_softlight, blender_hardlight, blender_colordodge, blender_colorburn, blender_lineardodge, blender_linearburn, blender_linearlight, blender_vividlight, blender_pinlight, blender_hardmix, blender_reflect, blender_glow, blender_phoenix, blender_hue, blender_saturation, blender_color, blender_luminosity,