I find it pleasant to have nicknames coloured in busy channels, that's why I made this. It simply generates colours by hashing the nicknames. This ensures that a given nickname will always be the same colour.
The script
;;;
;;; Lazy nickname coloring script
;;;
;;; Color all nicknames automatically by calculating a numeric hash over the nickname.
;;; The calculated number is used to pick a (space delimited) color from the %colors variable
;;; (set in "on START" event).
;;; Colors are made configurable because yellow on white is annoying, and you may want to use
;;; black or white depending on your background color.
;;;
;; Initialize
on 1:START: {
.initialize_coloring
}
alias initialize_coloring {
; use the following colors only
.set %colors 1 2 3 4 5 6 7 9 10 11 12 13 14 15
; reset all entries in the clist
while ($cnick(1)) {
.uncolor_nick $cnick(1)
}
}
;; Events
; Parse the /names <channel> response(s)
raw 353:*: {
var %names = $4-
var %i = 1
var %n = $gettok(%names,0,32)
while (%i <= %n) {
var %current_nick = $gettok(%names,%i,32)
var %firstchar = $mid(%current_nick, 1, 1)
while (%firstchar isin @+%) {
%current_nick = $mid(%current_nick, 2)
%firstchar = $mid(%current_nick, 1, 1)
}
.color_nick %current_nick
inc %i
}
}
; Handle nick changes/joins/quits
on 1:NICK: {
.uncolor_nick $nick
.color_nick $newnick
}
on 1:JOIN:*: {
.color_nick $nick
}
on 1:QUIT: {
.uncolor_nick $nick
}
;; Helper functions
; usage: color_nick <nickname>
alias color_nick {
if (!%colors) {
.initialize_coloring
}
var %colors_idx = $calc($hash($1, 16) % $numtok(%colors, 32)) + 1
var %nick_color = $gettok(%colors, %colors_idx, 32)
.cnick $1 %nick_color
}
; usage: uncolor_nick <nickname>
alias uncolor_nick {
.cnick -r $1
}
Copy & paste it in your remote (open with alt + r).
You may need to enable nicklist colouring in general. Use alt + b, Nick colors, choose "Enable".
The following image was rendered by another process
Motivation for using separate processes for rendering is if you wish to have multiple threads rendering. I do a lot of set blending type, put pixels, set blender type again, more pixels, etc. If I use async() to render multiple images at once these function calls might interfere as race conditions.
Probably a noobish moment, but I never realized the "stack" was this limited. I tried declaring something like
struct structw800h600
{
...
Pixels pixels[800 * 600].
};
message_queue mq (create_only, "pixels",
1, //max message number
sizeof(structw800h600)); //max message size
structw800h600 img;
memset(&img, 0x00, sizeof(structw800h600));
This code caused an exception while constructing the object that declared an instance of the struct on the stack:
Unhandled exception at 0x003E5017 in Starcry.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00702000).
Shows break here in chkstk.asm (because I am in debug mode):
[...]
; Find next lower page and probe
cs20:
sub eax, _PAGESIZE_ ; decrease by PAGESIZE
test dword ptr [eax],eax ; probe page. <<<<<<<<<<<<<<<<<<<< here
jmp short cs10
_chkstk endp
end
I did not find out the exact threshold but the the crash occured when the size of the struct was above ~1024972 bytes or ~1000 kB. (Size of each pixel object is 16 byte). If I understand it correctly the stack is only several MB so I was simply storing too much data on it.
Still posting this because I almost jumped to the false conclusion that it was a windows platform shared memory limitation.
Simply allocate the Pixel objects from the free-store and send that through the message queue. Something like:
Pixel *pixels = new Pixel[800 * 600];
I have posted on using allegro 4 with wxWidgets before. Allegro 5 is more easy.
Just the stuff I encountered and how to fix
Fix main conflict
#define ALLEGRO_USE_CONSOLE 1 Avoids the following error. 1>MSVCRTD.lib(crtexe.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup #define ALLEGRO_USE_CONSOLE 1 #include <allegro5/allegro.h> #include <allegro5/allegro_image.h> #include <allegro5/allegro_primitives.h> #include <allegro5/allegro_font.h> #include <allegro5/allegro_ttf.h>
Draw ALLEGRO_BITMAP on static canvas
Note that there is no equivalent of Allegro 4's draw_to_hdc() function. With a little grepping in the source code I found out that (for windows anyway) you have functions that do the same in C:\allegro5\src\win\wmcursor.c
Just borrow local_draw_to_hdc from there and use it in the paint event.
staticbitmap->Connect(wxID_STATIC, wxEVT_PAINT, wxPaintEventHandler(SharedMemoryTest::OnPaint), NULL, this);
void SharedMemoryTest::OnPaint( wxPaintEvent& event )
{
wxPaintDC dc(wxDynamicCast(event.GetEventObject(), wxWindow));
WXHDC wxHDC = wxPaintDC::FindDCInCache((wxWindow*) event.GetEventObject());
HDC hDC = (HDC) wxHDC;
local_draw_to_hdc(hDC, bmp, 0, 0);
}
If you are behind a firewall, chances are you can tunnel through it with Proxytunnel. This post does not describe anything new, but I think is still useful because it includes configuration of apache and ssh client examples.
The goal is being able to tunnel through a (corporate) firewall/proxy. And even more important, have your communication encrypted. This also has the advantage that even if you are not restricted, a corporate firewall/proxy can still not cache the websites you visit.
We do this by establishing an ssh session to some machine, and useing ssh portforwarding from there. This target machine may be your home computer or some server on the internet.
If you are able to run your SSH server on port 80 or 443, you might want to do that because then you can simply define the firewall as a proxy in PuTTY. The firewall should probably allow the communication, especially on 443 as this is normally for HTTPS and encrypted (as is SSH). I haven't tested this, but I believe you should be able to skip the proxytunnel stuff.
I assume you already have Apache running on port 80 and 443, so switching SSH to one of those ports won't be possible. We simply configure Apache so that it becomes itself another proxy that can make the connect to port 22, or 42 in the example I'm going to use. If you do not want to use apache, you can put your webserver of choice on a different port and use Apache's mod_proxy to redirect a virtual host to it.
In short how it works:
Your ssh client will NOT communicate directly to your ssh server. Instead it will communicate with proxytunnel, and proxytunnel establishes the actual connection. Proxytunnel will first connect to the "corporate" firewall/proxy and request a connection to your server on the HTTPS port, The firewall will then consider all communication HTTPS encrypted traffic and therefor allow it. But actually a mod_proxy is configured to respond to connection requests to specific destinations (using CONNECT dest:port HTTP/1.1). So we issue another CONNECT connection to the destination + SSH port. From that moment on proxytunnel simply redirects all read/write to the ssh client.
Once connected to your SSH server you can simply use the Port forwarding stuff that the SSH protocol supports.
Example config
I will be using this hosts throughout the post, you will have to replace these.
| Ip | Host | Description |
|---|---|---|
| 46.51.179.218 | ext.cppse.nl | My server machine, runs the apache @ port 80 and destination ssh @ 42 |
| NA | whatismyipaddress.com | Some website @ port 80 that displays remote host (optional for testing) |
| 172.18.12.11 | NA | The firewall @ port 8080, accepts only connections to ports 80,443. |
Configure proxy on some Apache server
You need mod_proxy, mod_proxy_http, mod_proxy_connect modules enabled in Apache. (Not 100% sure about mod_proxy_http.)
Create a VirtualHost like this:
<VirtualHost *:80>
ServerAdmin no-reply@ext.cppse.nl
ServerName ext.cppse.nl
ErrorLog /var/log/apache2/error_log
TransferLog /var/log/apache2/access_log
# Allow proxy connect (forward-proxy) to servers only on port 80 (http) and 42 (at my box SSH)
ProxyRequests On
AllowConnect 80 42
# Deny all proxying by default...
<Proxy *>
Order deny,allow
Deny from all
</Proxy>
# This directive defines which servers can be connected to.
# Access is controlled here via standard Apache user authentication.
<ProxyMatch (46\.51\.179\.218|ext.cppse.nl|whatismyipaddress.com|www.whatismyipaddress.com)>
Order deny,allow
Allow from all
#You should replace the above two rules with something like this:
# Deny from all
# Allow from <some_host>
# Allow from <some_host>
</ProxyMatch>
</VirtualHost>
This example will allow from any source to CONNECT to four locations: 46.51.179.218, ext.cppse.nl, whatismyipaddress.com and www.whatismyipaddress.com. Only destination ports 80 and 42 are allowed. We'll be using 46.51.179.218 on port 42 (SSH server), and {www.}whatismyipaddress.com on port 80 (plain HTTP) for testing.
- Add this VirtualHost as the first virtual host. Loading it /after/ other vhosts made the proxy deny all CONNECT's on my machine.
- Port 443 would be nicer, again, on my machine I couldn't do this because I have other HTTPS sites configured, and couldn't get it to use the proxy "as HTTP on port 443". My apache seems to expect SSL communication although I didn't enable SSL on the vhost.
- The vhost name "ext.cppse.nl" seems unimportant, the Proxy settings appear not to be specifically bound to this vhost. This might explain why using port 443 didn't work.
- I can imagine there would be some more complicated trick to make it possible to configure "unencrypted" traffic over port 443 for a specific vhost, butthis works well enough for me.
Test if this proxy works
You might want to test this from some location where you are not behind the firewall. Configure it as a proxy in your browser:
This is why I added [www.whatismyipaddress.com][] and port 80 in the Virtual Host, open it:
- You can also test the SSH connection if your client supports usage of an HTTP proxy.
- You also might want to replace the default allow by the default deny config in the vhost.
- You might want to remove port 80 from the AllowConnect parameter in the vhost, and the whatismyipaddress domain(s).
Configure proxytunnel for PuTTY
In our example we have the proxy "172.18.12.11:8080", with no-authentication required. If you have a proxy that requires a username and password use the -P "username:password" parameter on proxytunnel. Also see the help for more available options.)
Install proxytunnel on windows
I made a zip file with Putty "Development snapshot 2012-01-16:r9376" because it
supports "local proxy" feature we need to use for Proxytunnel, also included
version 1.9.0.
You can download PuTTY Tray a version of PuTTY that supports local proxy and some more very nice additional features!!
When PuTTY is configured to use Proxytunnel it delegates the connection to proxytunnel, which will first connect to our newly configured proxy "46.51.179.218:80" (the one we configured in apache) using the firewall/proxy 172.18.12.11:8080. Once connected to our proxy we connect to our intended destination "46.51.179.218:42". In PuTTY you use %host:%port (these values get replaced).
This is a command you can use for testing at commandline:
C:\proxytunnel>proxytunnel -v -p 172.18.12.11:8080 -r 46.51.179.218:80 ^
-d 46.51.179.218:42 -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"
Connected to 172.18.12.11:8080 (local proxy)
Tunneling to 46.51.179.218:80 (remote proxy)
Communication with local proxy:
-> CONNECT 46.51.179.218:80 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.1 200 Connection established
Tunneling to 46.51.179.218:42 (destination)
Communication with remote proxy:
-> CONNECT 46.51.179.218:42 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.0 200 Connection Established
<- Proxy-agent: Apache/2.2.12 (Linux/SUSE)
Tunnel established.
SSH-2.0-OpenSSH_5.1
You give exactly the same command to PuTTY although, instead of the -v flag and hardcoded destination you use the -q (quiet mode) (and %host:%port). PuTTY then communicates by reading/writing to the started proxytunnel process, instead of a socket.
This is how you configure PuTTY
Note that the Keep-alive may be necessary if the firewall we're going to tunnel through actively closes connections if they are idle for longer than xx seconds.
You can configure all kinds of portforwarding.
Install proxytunnel on linux
Download proxytunnel and "make" like any other tool. If you are missing development packages, I may have a precompiled 32 bit version available that might work on your box. Todo: Add download link.
linux-yvch:/usr/local/src # tar -zxvf proxytunnel-1.9.0.tgz
...
linux-yvch:/usr/local/src # cd proxytunnel-1.9.0
..
linux-yvch:/usr/local/src/proxytunnel-1.9.0 # make
..
linux-yvch:/usr/local/src/proxytunnel-1.9.0 # make install
..
linux-yvch:/usr/local/src/proxytunnel-1.9.0 # cd
Just as with PuTTY you need to configure your ssh config: In linux I prefer to keep it verbose (the -v setting, you can use -q for quiet mode). Note that openssh uses %h:%p for host / port replacement.
linux-yvch:~ # cat ~/.ssh/config
Host 46.51.179.218 ext.cppse.nl ext.cppse.nl
DynamicForward 1080
ProxyCommand proxytunnel -v -p 172.18.12.11:8080 -r 46.51.179.218:80 \
-d %h:%p -H "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n"
ServerAliveInterval 30
Connecting with openssh should yield something like:
linux-yvch:~ # ssh -l proxy -p 42 46.51.179.218
Connected to 172.18.12.11:8080 (local proxy)
Tunneling to 46.51.179.218:80 (remote proxy)
Communication with local proxy:
-> CONNECT 46.51.179.218:80 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.1 200 Connection established
Tunneling to 46.51.179.218:42 (destination)
Communication with remote proxy:
-> CONNECT 46.51.179.218:42 HTTP/1.0
-> Proxy-Connection: Keep-Alive
-> User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n
<- HTTP/1.0 200 Connection Established
<- Proxy-agent: Apache/2.2.12 (Linux/SUSE)
Tunnel established.
Password: *****
Last login: Thu Jan 26 15:55:40 2012 from 46.51.179.218
__| __|_ ) SUSE Linux Enterprise
_| ( / Server 11 SP1
___|\___|___| x86 (32-bit)
For more information about using SUSE Linux Enterprise Server please see
http://www.novell.com/documentation/sles11/
Have a lot of fun...
YOU ARE IN A RESTRICTED SHELL BECAUSE THIS ACCOUNT IS ONLY FOR TUNNELING
proxy@ip-10-235-45-12:/home/proxy>
After the "Tunnel established" you continue as with any other SSH connection.
Using SSH port forwarding
It would have been more elegant if the first connect would have been to port
443. Because then the communication, although when sniffing you see the CONNECT statement and the SSH banner in plain text.
From the firewall perspective it is all encrypted data. It just coincidentally
happens to be readable
.
But after the initial stuff everything is encrypted as we're tunneling SSH.
I'm not sure if it is possible to communicate in SSL to the second proxy,
because then it won't be detectable at all.. the SSL communication would be
encrypted twice!
I already included in the PuTTY screenshots and OpenSSH example a Dynamic Forward (socks) proxy on 1080. This means that SSH will start a listener socket on port 1080 accepting connections and tunneling it through the established connection. The SSH protocol supports this, and this feature is (I think) enabled by default, it is configurable on the server in your sshd config.
You can then configure your browser to use the socks proxy, localhost:1080 and all communications will go through the established tunnel. Remote desktop, at the time of writing, doesn't support the use of a proxy, but you can create a "normal" port-forward as for this to a specific destination & port.
If your firewall does not support CONNECT you might want to try cURLproxy, a proxy program I wrote that works simply by downloading and POSTing HTML. Available here: curlprox[cURLproxy].
DISCLAIMER: Okay, probably still almost any firewall. There are a few posts on the internet about how SSH tunnels bypass "almost any firewall", I believe this proxy will probably bypass a whole lot more firewalls.
So I had to do come up with something better than "almost any"
.
When is this useful?
ProxyTunnel is awesome as it allows you to tunnel to SSH through--for example--port 443. And due to SSH supporting port forwards you can go from there to wherever you want. If I am correct, it requires that the proxy in question supports the CONNECT syntax.
Sometimes however, proxies are more restricted than that: CONNECT may not be supported; connections are not allowed to stream (i.e., file downloads are first downloaded by the proxy server, scanned for viruses, executables and other filetypes may be blocked); base64 may actually be decoded to see if it contains anything that isn't allowed, it may go as far as to inspect content of zip files and may have restrictions on the maximum file size for downloads (XX MB limit). In that case ProxyTunnel won't suffice.
If you're unfortunate enough to be behind such a firewall, no worries because now there is a way to tunnel through it!
The only requirement for it to work is that you can receive plain text from a webpage, and post data to it. One that you own or have access to. Well If you can't do that, I suggest you look for another Job, because this is REALLY important!!!!1 (Not really
but then this proxy solution won't work).
Do not expect it to be very performant with broadband type of stuff by the way.
How it works in short
It works with three PHP scripts. And just like with Proxytunnel you need to run one of them on your local computer: localclient.php. This script binds to a local port, you connect with your program to this local port. Each local client is configured to establish a connection with some destination host + port. But the cool part is, it does so by simply reading plain old HTML from an url, and posting some formdata back to it. Well actually it appears to be plain old HTML, because it's the data prefixed with an HTML tag, followed by the connection identifier and the DES encrypted data (converted into base64).
The curl proxy (as I call it, because I use the cURL extension in PHP) retrieves HTML pages like this:
Example of packet with data "PONG :leguin.freenode.net", is sent as the following HTML:
<PACKET>a5bc97ba2f6574612MNIoHM6FyG0VuU6BTF/Pv/UcVkSXM5AbiUrF4BDBB4Q=
|______||_______________||__________________________________________|
| | `=BASE64 OF ENCRYPTED DATA
| `=Session id / socket id
`=Fake HTML tag
POSTing back sends a string with the same syntax back, basically only prefixed with "POST_DATA=".
In order for this to work, a second script has to be callable on the web, you must be able to access it, and the same machine has to be able to make the connections you want. For example: http://your-server/proxy.php (you could rename it to something less suspicious; there are some smart things you can do here, but I'll leave that to your imagination
). All proxy.php does is write and read files from a directory, nothing more.
Then a shellscript has to be started to run in background, with access to the same directory. This script scans that directory for instructions, specifically starting server.php processes for new connections. The actual connection is made in the server.php script. And all this script does is read from the same directory for packets received, which it will send to it's socket, any data read from the proxy is written back to the directory, which proxy.php will eventually sent back to the client.
Graphical explanation
You should follow the arrows in the same order as presented in the Legend. Click to enlarge the image.
Design decisions
When I had the idea to make it, I didn't feel like spending alot of time on it, so I hacked it together in a few hours. Then I tested it, it worked and it got me exited enough to refactor it and make a blog post out of it.
- After the encryption of the packets I use base64 encoding, which increases the size of the messages, but it looks more HTML-like. If I wanted to send the encrypted data raw I'd have to do some more exotic stuff, maybe disguise it as a file upload, because AFAIK a plain old POST does not support binary data.
- I use BASE64 and not urlencode on the encrypted data, because when I tested it urlencode produced even more overhead. Of course the BASE64 string is still "urlencoded" before POST, but only a few chars are affected.
- I don't use a socket for communicating between proxy.php and server.php, but files and some lock-files because I preferred them somehow. A database would be nicer, but implies more configuration hassle.
Encryption used
define('CRYPT_KEY', pack('H*', substr(md5($crypt_key),0,16)));
function encrypt_fn($str)
{
$block = mcrypt_get_block_size('des', 'ecb');
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
return base64_encode(mcrypt_encrypt(MCRYPT_DES, CRYPT_KEY, $str, MCRYPT_MODE_ECB));
}
function decrypt_fn($str)
{
$str = mcrypt_decrypt(MCRYPT_DES, CRYPT_KEY, base64_decode($str), MCRYPT_MODE_ECB);
$block = mcrypt_get_block_size('des', 'ecb');
$pad = ord($str[($len = strlen($str)) - 1]);
return substr($str, 0, strlen($str) - $pad);
}
If you prefer something else, simply re-implement the functions, you'll have to copy them to all three scripts (sorry, I wanted all three scripts to be fully self-contained).
I found my "ASCII key → md5 → 16 hexadecimal display chars → actual binary" a pretty cool find by the way. Did you notice it? 
Demonstration
Note that first I demo it where the server is running on an Amazon AMI image. Appended to the video is a short demo where I run the server on my local windows pc (just to show how it it'd work on windows). This second part starts when I open my browser with the google page.
Remote desktop actually works pretty good through the curl proxy by the way. Establishing the connection is a little slow like with WinSCP, but once connected it performs pretty good. I could't demo it because I don't have a machine to connect to from home.
Sourcecode & downloads
Put it here on bitbucket: https://bitbucket.org/rayburgemeestre/curlproxy Placed it under MPL 2.0 license, which seamed appropriate. Basically this means that when you distribute it with your own software in some way, you'll have to release your code changes/improvements/bugfixes (applicable to curlproxy) to the initial developer. This way the original repository will also benefit and you're pretty much unrestricted.
Work in progress...
It will be a lot easier to compile. No longer dependant on the json lib. A single .cpp file (as the code is quite small).
No makefile, just a g++ goto.cpp -o goto -lncurses
Get the source code here
P.S. I added colours:
Bash wrapper script
With Apache (2.2) you could get an generic "Internal Server Error" error message in case the cgi sends the wrong headers. There is probably a setting for this in Apache as well, but I always create a bash wrapper script. For example someapp.cgi:
#!/bin/bash
printf "Content-type: text/html\n\n"
/path/to/actual_appl
This immediately makes the output visible and you can comment the printf statement once fixed. This trick only makes sense if you don't have quick access to a debugger or a core dump.
Running application in chroot
There are plugins for apache AFAIK for running cgi applications in a chroot. I didn't experiment with these, as I simply use my (probably lame) bash wrapper here as well:
#!/bin/bash
sudo -E /usr/bin/chroot /usr/local/src/some_jail /usr/bin/some_appl 2>&1
The -E flag means "preserve environment". To allow this you have to configure sudoers properly (visudo). Something like this:
wwwrun ALL=(ALL) SETENV: ALL, NOPASSWD : /usr/bin/chroot
This is no rocket science but I thought this was a really cool solution to the problem. 
First I created a helper function Xprintf to interface with an existing C API that works with (non const) char arrays. Hence its char * return value.
char *Xprintf(const char *format, ...);
// This function works in the following situations
foo1(Xprintf("Hello world: %d", 1001)); // void foo1(char *);
foo2(Xprintf("Hello world: %d", 1001)); // void foo2(const char *);
foo3(Xprintf("Hello world: %d", 1001)); // void foo3(const string);
foo4(Xprintf("Hello world: %d", 1001)); // void foo4(const string &);
foo5(Xprintf("Hello world: %d", 1001),
Xprintf("...", ...)); // void foo5(char *, char *);
Xprintf cannot use just one buffer because the case of 'foo5' would fail (it would get the same pointer twice).
I needed a different return value, like std::string, so that copies could be returned which would clean themselves up as soon as they went out of scope.
But std::string does not provide implicit casting to const char *, only explicit casting through .c_str().
The call to foo1 would become: foo1(const_cast
The following fixes it, creating a tmp_str class that extends std::string and simply provides the implicit cast:
class tmp_str : public std::string
{
public:
tmp_str(const char *str)
: std::string(str) {}
// g++ is fine with adding this one, xlC isn't
//operator const char *() const { return c_str(); }
operator char *() const { return const_cast<char *>(c_str()); }
};
tmp_str cHelperCharArray::Xprintf(const char *format, ...)
{
char buffer[512] = {0x00};
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
va_end(args);
return tmp_str(buffer);
}
A note why tmp_str is-a std::string and not an is-implemented-in-terms-of: the call to foo4 would fail as it would not accept tmp_str as a reference to string (A parameter of type "const std::basic_string,std::allocator > &" cannot be initialized with an rvalue of type "tmp_str".). )
g++ accepts all these foo* functions, but IIRC xlC doesn't like foo2. In that case I had to cast to const. Adding the const char * operator overload would make some casts for that compiler ambiguous.
mplayer can easily be instructed to render on a custom window with the -wid (window handle) parameter.
// On windows long targetWindowId = reinterpret_cast<long>(canvas->GetHWND()); // On Linux long targetWindowId = GDK_WINDOW_XWINDOW(canvas->GetHandle()->window);
Now that I got it to render on my canvas, I cannot render on top of it without flickering, because I cannot do double buffering. (I cannot control when mplayer renders frames on the window). That's why I add a second window that reads the first window to a bitmap, I can do whatever I want to that bitmap, and display it
. This meant that I could no longer use my preferred video renderer on windows -vo direct3d because somehow that setting doesn't actually draw on the window, just in the same region. When reading the first window I'd get an empty bitmap and not the video. I ended up using -vo directx:noaccel in order to properly read it.
Fix overlap problem
This posed another problem, when hovering the second window on top of the first, it interferes with the video as it renders itself in window1 first. I only encountered this on my windows pc:
I decided to ignore this problem and try to find a way to hide the first window so that it wouldn't interfere. I tried minimizing it, Hide(), move it outside the screen, etc. But mplayer would not render the video in these cases. I then tried making the window 100% transparent and this worked. It also fixed my overlap-problem as I could now overlap the windows without problems. Somehow making the windows transparent forces the no-hardware-acceleration-directx renderer to behave differently. Making the window 1% transparent also fixes the overlap-problem.
Fix linux support
On Linux I use the -vo X11 video output, and overlap wasn't a problem. The only annoying thing is that in order to get the GTK window handle you have to include a GTK header in C++, which requires adding a lot of include directories to your include path. Because you need to cast the window handle to a GTKWidget instance, and ask it for the xid.
Result
The code is available on bitbucket and works on Windows (tested Windows vista with aero theme) and Linux (openSUSE 11.4). Makefile and Visual studio project files included.
Calculation of average
I had a problem in my project, and came up with this solution. For each pixel on the screen I calculated a bunch of numbers. In a second pass I needed averages of these numbers. So I stored each in a vector and in the second pass calculated averages. Like this:
double result2 = 0;
for (auto i=myNumbers.begin(); i!=myNumbers.end(); ++i) {
double &Nx = *i;
result2 += Nx * (1.0 / N);
}
// result2 now contains the average
The problem is that this method needs to know N inside the loop. So I came up with this:
double result3 = 0;
for (int i=0; i<100; i++) {
int nmbr = (int)dist(eng);
cout << ((i == 0) ? "random numbers: " : ", ");
cout << nmbr;
// Calculate the average, N is not required, storing all numbers or TOTAL is not required.
double frac1 = 1.0 / static_cast<double>(i + 1); // 1/1, 1/2, 1/3, 1/4,...
double frac2 = 1.0 - frac1; // 0, 1/2, 2/3, 3/4,...
result3 *= frac2;
result3 += nmbr * frac1;
}
// result3 now contains the average
Download the source code snippet here.
Putting source files in separate directory
View youtube video
(don't forget to enable 720p and fullscreen)
- How to put source and image files in subdirectories in DialogBlocks..
- NOTE for git users: If you're going to do this with an existing git repository. My advice is to do a move through git as well: "git mv source dest". So that git knows it was a file move, not a delete/create.
Seperate implementation files on a few panels
View youtube video
(don't forget to enable 720p and fullscreen)
- We put two panels (with their contents) into separate implementation files.
- Show some examples you can do with it (or more easily than before).
- Events and other functions can be nicely grouped into that implementation.
Compiling with MinGW
View youtube video
(don't forget to enable 720p and fullscreen)
- How to add configuration for MinGW
Compiling with Visual C++ compiler with project files
View youtube video
(don't forget to enable 720p and fullscreen)
- Shows how to set the platform SDK.
- Some specific errors you can get w/ DialogBlocks.
- How to use generated project file(s) for visual studio.
- How they handle file changes
- Why to use: autocomplete, debugger, etc.
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.
- Git usage on Windows Vista notes
- Git removing contents permanently from repository
- Converting from Git to Mercurial
- Mercurial through proxy
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:
- 80% Multiple monitor support (working on it)
- 100%
Undo history key (for correcting mistakes) - 0% Remember settings in config.
- 80% OSX compatibility
- 60% GTK2 compatibility
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 René 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.
René 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 René 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:
- Always instantiates exactly one blank/new/untitled notepad instance at a time.
- The untitled stuff is kept in a user configurable file. So you are not constantly bothered with a Save as dialog..
- Easily 'dispatch' selections within your notepad to separate files.
- When called to open a file, does open a new editor for that file. That editor is configurable.
- Supports better CTRL+Z, CTRL+Y than default notepad.
- Supports CTRL+F and search next with F3.
- (For goto-line with CTRL+G you'll have to dispatch the text to another editor!)
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:
- Disable UAC (on Vista for example)
- Reboot the system
- Execute a batch file like the following. Got this copy from here.
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
!
Create bookmarks file
// 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"}
]}
]
Use in screen with C-x
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).
Installation
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.
Record and re-create requests..
/**
* 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,
There are a few things I figured out how to do, and I seem to use them on a regular basis. For these snippets I have to search through my projects every now and then, to find them, and it would be a lot easier if I kept them somewhere listed. Hopefully you will find something useful as well.
Clearing a sizer
I usually make master/detail screens where the detail list is a vertical sizer with stuff on it. Usually a panel with more stuff on it. Every now and then you may want to clear it. I use:
while (theSizer->GetChildren().GetCount() > 0) {
theSizer->GetItem(static_cast<size_t>(0))->DeleteWindows();
theSizer->Remove(0);
}
// populate the sizer again maybe
sizerChangelogItems->Layout();
You might want to surround the code with Freeze() and Thaw() for performance.
Hiding and showing a panel on an AUI frame
This is more of a note I keep forgetting about: Instead of calling the Hide() and Show() on the window (panel) itself (which compiles). Use the following:
someWindow->GetAuiManager().GetPane(someWindow->splitterwindow1).Show(false);
someWindow->GetAuiManager().GetPane(someWindow->panel1).Show(true);
someWindow->GetAuiManager().Update();
Using dropdowns with object data.
I use wxChoice a lot, I especially like that the items themselves can refer to anything. Typically how I initialize a choicebox:
wxChoice *choice = ...;
Object *someObject = ...;
for (...) {
choice->Append("object 1", (void *)someObject);
}
choice->SetStringSelection("object 1");
Then usually I have an event that triggers on the selection of an item in the choicebox. In the event I first get the wxChoice, then cast the client data for the selected item to the object pointed to:
void SomeWindow::OnChoiceSelected( wxCommandEvent& event )
{
wxChoice *choice = static_cast<wxChoice *>(event.GetEventObject());
Object *someObject = static_cast<Object *>(choice->GetClientData(choice->GetSelection()));
if (someObject != NULL) {
//...
}
}
Directory functions
wxString resdir(RESOURCE_DIR);
if (!::wxDirExists(resdir)) {
throw std::runtime_error("resources dir not found");
}
wxDir dir(resdir);
if ( !dir.IsOpened() ) {
throw std::runtime_error("resources directory could not be opened");
}
wxString filename;
bool cont = dir.GetFirst(&filename);
while ( cont ) {
int lastdot = filename.Find('.', true);
if (lastdot != wxNOT_FOUND) {
wxString name(filename.SubString(0, lastdot - 1));
wxString ext(filename.SubString(lastdot + 1, filename.Length()));
if (!ext.compare(_T("cpp"))) {
...
} else {
...
}
}
cont = dir.GetNext(&filename);
}
Write to file function
wxFile article(markdownfilename, wxFile::write);
if (!article.IsOpened()) {
throw std::runtime_error("main.md could not be opened for writing");
}
article.Write(textctrlArticleBody->GetValue());
article.Close();
Today I moved from my "127 machine" (localhost) to an Amazon EC2 server.
First I tried the Amazon Linux AMI (32-bit) micro, but it was to difficult to get GTK working. Package manager was yum, which is nice, but a few packages I needed weren't available, decided to check another AMI out.
Found SUSE AMI (32-bit) on micro, package manager is zypper, and works like a charm. GTK was already installed on this one. Everything is up and running.
I've used Debian for a lot of years for personal use and at work, and henceforth became accustomed to Debian. I'm actually finding out SUSE isn't that bad either!
On my local machine GTK was ugly by default, so I immediately changed the theme to something less hideous. On this SUSE AMI the default GTK is pretty fancy, although the fonts were missing ;)
What annoys me is that forwarding X11 over the internet is slower than I expected. Editting text inside the forwarded "ArticleManager" isn't particularly fast. Still love my weird blogging system though!
The original wallpaper by ~Pocket7878 on devianART was designed for a 1024 x 768 pixels resolution. I have 1280 x 1024. And although even that is probably considered small, created a 1280 x 1024 version. Just by creating a new canvas with the surrounding color and centered his wallpaper on top of it (so not by upscaling). The mascotte is of course by Conrad Barski (author of Land of lisp)
This is how the text is structured (excerpt)
The Commentary 147
1. The exposition of the root verses 147
2. The meaning of the title 147
2. The homage of the translator 150
2. The text itself 151
3. An examination and establishment of the two truths 151
4. The two truths identified 151
5. A demonstration that no entities exist on the ultimate level 151
6. The main argument of the Madhyamakalankara (stanza I) 151
7. An investigation of the subject of the probative argument 152
7. An investigation of the argument 153
8. A Prasangika or a Svatantrika argument? 153
You may think this isn't so bad, but this goes on for a few levels more, especially in the first part of the book. Sometimes this reaches a depth of 21 levels. That would mean that using more regular numbering you would get sections like 1.2.1.2.2.2.1.1.2, and worse.
I wrote a little Lisp program to parse this outline lines-of-text.
And output it with proper indenting. Output for (print-tree 4)
The Commentary 147
1 The exposition of the root verses 147
2 The meaning of the title 147
2 The homage of the translator 150
2 The text itself 151
3 An examination and establishment of the two truths 151
4 The two truths identified 151
4 Answers to objections made to this distinction of the two truths 293
4 The benefits of understanding the two truths correctly 337
3 The conclusion: a eulogy of this approach to the two truths 361
4 An outline of the tradition in which the Chittamatra and Madhyamaka (..)
4 In praise of this path 363
2 The conclusion 377
3 The author's colophon 377
3 The colophon of the translators 377
3 Colophon of Mipham Rinpoche 381
1 The necessity for the explanation of the root verses 378
Will update this post as soon as I've made an interactive version of this tree, which I consider of great value maintaining an overview text's structure.
I am considering testing either Wt, the C++ Web toolkit or CppCMS, a C++ Web Development Framework in the process. Then i'll probably make some small C++ program for this, and use it as cgi in Mongoose (g++ outline.cpp -o outline.cgi!).
My two favourite libraries ever
Allegro is a game programming library for C/C++ developers distributed freely, supporting the following platforms: Unix (Linux, FreeBSD, etc.), Windows, MacOS X and Haiku/BeOS. (...) source
wxWidgets is a C++ library that lets developers create applications for Windows, OS X, Linux and UNIX on 32-bit and 64-bit architectures as well as several mobile platforms including Windows Mobile, iPhone SDK and embedded GTK+. (...) source
The required code summarized
I include allegro before wxWidgets, not sure if that matters, but what does matter is: the workaround regarding the RGB define, the use of winalleg.h instead of windows.h (needed to avoid conflict between allegro and windows.h); and disabling the 'magic' main() in allegro (wxWidgets' main is used):
#define ALLEGRO_NO_MAGIC_MAIN #define RGB AL_RGB #include <allegro.h> #include <winalleg.h> #undef RGB
Then I initialize allegro like this in OnCreate() of your wxWidgets application:
install_allegro(SYSTEM_NONE, &errno, NULL); set_palette(desktop_palette); // example set_color_depth(32); // example
You could draw allegro BITMAP's to wxStaticBitmap's in the OnPaint() event of the wxWidgets window. But you'll have to decide for yourself what method is most appropriate for your project.
wxPaintDC dc(wxDynamicCast(event.GetEventObject(), wxWindow));
// you probably already have the wxPaintDC
// Found the following code by searching for a very long time in the
// wxWidgets source code ;)
WXHDC wxHDC = wxPaintDC::FindDCInCache((wxWindow*) event.GetEventObject());
HDC hDC = (HDC) wxHDC;
... // do your thing here
draw_to_hdc(hDC, allegroBitmap, 0, 0);
// where allegroBitmap is a valid "BITMAP *"
It took me a while to figure out how I could use draw_to_hdc like this. It's not that obvious (see allegro forums). Others have been struggling with drawing allego bitmaps on wxStaticBitmaps. This method will still yield you quite a high fps count.
Known issues
wxWidgets eventloop--or apparently random--crashes I have experienced some weird behaviour with using allegro with wxWidgets. There have been cases, that although allegro was initialized, in a separate window allegro seems to fail after calls to save_bmp(). Not in the function itself, but even weirder, after having called save_bmp() more than once, eventually the wxWidgets event handling system crashed, complaining about unhandled events. I guess something really wasn't properly initialized (although everything seemed to work, drawing, saving, etc.)
If you experience this, just initialize allegro in that window's OnCreate(), it should fix the problem. Maybe it has to do something with threading, that would explain why allegro has to be initialized (in that thread).
Assembly optimized {get|put}pixel crashes With _getpixel32() and _putpixel32() assembly, the optimized versions of getpixel() and putpixel(). For me these didn't work properly on 64-bit windows environments. After turning off some compiler optimizations the problem seemed to go away.
Non-assembly optimized {get|put}pixel crashes Continuing on the previous, if you replace _getpixel32 with getpixel, you may need to explicitely set a blending mode for 64-bit environments. Somehow I didn't do that, and as the non-assembly optimized getpixel function does bounds checking and blending modes, this became important.
[Edit 22-nov-2010: Just remembered this, but didn't verify it. It may have
something to do with calling drawing_mode(..), and not setting a specific
blending mode like set_trans_blender(..). Setting one of those, or instead
of drawing_mode and set_trans_blender a call to solid_mode(), would fix it.]
[Edit 29-01-2012: By the way. Allegro 5 integrates a little easier with wxWidgets, although come to think of it, I had to grep the source, or more specifically the examples directory in order to get the functions needed to render on a wxStaticBitmap though. I'll create a post for it: TODO]
It's actually a multi-topic blog.
Some posts I consider more as pages for a personal website, these posts will be dispatched to a different website, not this blog.
Blog topics include Software Engineering--C++, Lisp, Webdevelopment. Advances in my graphical motion rendering engine (my main project). Some other of my interests, buddhist philosophy (mainly the madhyamakalankara and madhyamakavatara).
I did Software Engineering at the University of Amsterdam, and consider my main field of expertise Software Construction. I have lots of professional experience in webdevelopment. I taught myself C++ over the years, and more recently wxWidgets as well, and even more recently I have embarked on the quest to learn Lisp as well.
What's new about this blog??
Hopefully some ideas on this blog will be new, or just fun. It's also for myself to keep a track of certain stuff. About the blog itself, it doesn't use typical blog or CMS software. It uses C++, and has interfaces to other tools. I created this system in a few hours this weekend, it combines all the minimalist stuff I really like in Webdevelopment.
How does it work? I have to run Xming on my windows box, and request from the administration panel of the blog a management console. This opens a C++ program developed using DialogBlocks (my best software-buy ever!!) / wxWidgets. In this application I can add sites, and choose what categories should be dispatched to it. This is the weirdest part I guess, no regular login + management through a webinterface.
Why do I do it like this? First of all I don't like to write HTML, I came to hate it actually. Basically the same goes for CSS. I've done too many of that stuff in my life. That's why I can define a (simple) site template with a HAML and SASS, and add some markers in it for replacement.
The editor simply has a listing of articles, which are stored in multimarkdown syntax. (note: this format is actually easily converted to LaTeX pdf documents as well!(works like a charm)). I have made some facilities to make it easy to add i.e. C++ or Lisp code-snippets (they can be editted in separate files). Using this code prettifyer by Mike Samuel. They will be represented by a string like (lisp-code filename), and if I use that in the markdown document it will place syntax highlighted code with the snippet there. I made something similar for images and some meta-data with regards to the articles is stored using TinyXML.
In the editor I can request gvim or xemacs to edit the markdown (or a snippet) as well.
Lastly, I can (re)generate (parts of- or the entire-)website with a single click. It will convert HAML files to HTML. Markdown to HTML. Merge the snippets, merge articles with main html. And the website is updated.
(demo) Example snippet:
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
Quote from Wikipedia:
In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state.[1]
In practice, the difference between a mathematical function and the notion of a "function" used in imperative programming is that imperative functions can have side effects, changing the value of already calculated computations. (...) Conversely, in functional code, the output value of a function depends only on the arguments that are input to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) both times.
Lisp e-books
- Practical Common Lisp by Peter Seibel.
- Common Lisp: A Gentle Introduction to Symbolic Computation by David S. Touretzky.
- Succesful Lisp by David B. Lamkins..
I've started with gentle --actually a really old book--and have planned to read pcl second. [Edit 23-11-2010, now actually bought a used copy as well!]