Neither one nor Many //blog.cppse.nl Software engineering blog about my projects, geometry, visualization and music. EBPF Flamegraphs C++ Ubuntu 20.04 //blog.cppse.nl/flamegraphs-ebpf-c++<div class="datestamp">February 10 2022</div> <div class="h1"><a href="//blog.cppse.nl/flamegraphs-ebpf-c++"> <h1 id="ebpfflamegraphscubuntu20.04">EBPF Flamegraphs C++ Ubuntu 20.04</h1> </a></div> <p>Some of the stuff I'm posting on my 'blog' might be better categorized as a knowledge base articles, or more simply 'notes'.. In any case, this might be one more such posts <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sB" />, just some caveats I ran into setting stuff up on my Ubuntu Server 20.04 LTS.</p> <h2 id="flamegraphs">Flame Graphs</h2> <p>Invented by Brendan Gregg, who is someone that I honestly greatly admire. He is behind a lot of amazing presentations/talks, online content and tools w/r/t performance. Recently, when I got very interested in EBPF, stumbled upon his work again.</p> <p>I've used Flame Graphs in the past with <code>perf</code>, and so I was curious to try it with <em>ebpf</em> this time. Flame Graphs can give very useful insights into potential bottlenecks. Below is one that I've created as an example, but will refer to <a href="https://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html">Brendan's website</a> if you want to dive into them more, and see more interesting examples.</p> <p><center> <div> <a href="//cdn.cppse.nl/168-flamegraph.png"><img border="0" src="//cdn.cppse.nl/168-large-thumb-flamegraph.png" /></a> <br/> <a href="//cdn.cppse.nl/profile.svg?x=1083.3&amp;y=565"><i>Click here to open the SVG in interactive mode (warning: 12 MiB, loading takes a while!)</i></a> </div> </center></p> <p>As I tried to make all the tools work, I discovered the Ubuntu packages are a bit old, and I ran into a few issues. The steps that worked for me are based on <a href="https://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html">this link, section 4.2.</a>:</p> <h2 id="installprerequisites">1) Install prerequisites</h2> <pre><code>sudo apt install -y bison build-essential cmake flex git libedit-dev \ libllvm7 llvm-7-dev libclang-7-dev python zlib1g-dev libelf-dev libfl-dev python3-distutils</code></pre> <p>Remove the old <code>bpfcc-tools</code> package, if you've installed them before (gave me nothing but issues, such as python scripts raising errors, that have already been fixed upstream). We will fetch the latest version from github instead.</p> <pre><code>apt remove -y bpfcc-tools</code></pre> <h2 id="installbcctoolsbpfcc-tools">2) Install bcc tools (bpfcc-tools)</h2> <pre><code>git clone https://github.com/iovisor/bcc cd bcc</code></pre> <p>Then execute the following:</p> <pre><code>mkdir -p build; cd build export LLVM_ROOT=/usr/lib/llvm-7 cmake .. make sudo make install cmake -DPYTHON_CMD=python3 .. # build python3 binding pushd src/python/ make sudo make install popd</code></pre> <p>Note the <code>export LLVM_ROOT=/usr/lib/llvm-7</code>, this was critical in my case, since I had newer versions:</p> <pre><code>trigen@ideapad:~&gt; find /usr/lib -name 'llvm-*' -type d /usr/lib/llvm-7 /usr/lib/llvm-10 /usr/lib/llvm-12</code></pre> <p>CMake would pick up the latest llvm-12, and that would cause compilation errors. See: <a href="https://github.com/iovisor/bcc/issues/3601">https://github.com/iovisor/bcc/issues/3601</a></p> <h2 id="dotheprofilingwithebpf">3) Do the profiling with EBPF</h2> <ul> <li>Step 1: I would start my executable program <code>starcry</code> in a terminal, have it render a bunch of stuff.</li> <li>Step 2: Then sample for 60 seconds on the specific pid as <code>root</code>, see below.</li> </ul> <p><br/></p> <pre><code>export PYTHONPATH=$(dirname `find /usr/lib -name bcc | grep dist-packages`):$PYTHONPATH /usr/share/bcc/tools/profile # see if it produces meaningful output</code></pre> <p>The <code>PYTHONPATH</code> had to be exported correctly first (on my system in any case) or the <code>profile</code> tool would raise a Python error. THen do the actual sampling with:</p> <pre><code>sudo python3 /usr/share/bcc/tools/profile -F 99 -adf 10 -p $(pidof starcry) &gt; /path/to/out.profile-folded</code></pre> <h2 id="generatetheflamegraph">4) Generate the Flame Graph</h2> <pre><code>git clone https://github.com/brendangregg/FlameGraph cd FlameGraph ./flamegraph.pl --colors=java /path/to/out.profile-folded &gt; profile.svg</code></pre> <p>That should be it!</p> <h3 id="example">Example</h3> <p>From the example PNG included in this blog post:</p> <pre><code>trigen@ideapad:~/projects/FlameGraph[master]&gt; export PYTHONPATH=$(dirname `find /usr/lib -name bcc | grep dist-packages`):$PYTHONPATH trigen@ideapad:~/projects/FlameGraph[master]&gt; sudo python3 /usr/share/bcc/tools/profile -U -F 99 -adf 10 -p $(pidof starcry) &gt; out.profile-folded WARNING: 17 stack traces could not be displayed. Consider increasing --stack-storage-size. trigen@ideapad:~/projects/FlameGraph[master]&gt; ./flamegraph.pl --colors=java ./out.profile-folded &gt; profile.svg</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 10 Feb 2022 00:00:00 +0000 Settings for Tide graph Casio G-Shock GLX 150 //blog.cppse.nl/casio<div class="datestamp">April 4 2020</div> <div class="h1"><a href="//blog.cppse.nl/casio"> <h1 id="settingsfortidegraphcasiog-shockglx150">Settings for Tide graph Casio G-Shock GLX 150</h1> </a></div> <h3 id="lunitidalinterval">lunitidal interval</h3> <p>In <code>ADJUST</code> Settings set the right Longitude. I.e., if Lat/Lon for my city is: <code>52.601234, 4.700493</code> The only relevant part is the longitude, <code>+4</code> or <code>+5</code> in this case. I configured <code>4</code> for longitude, and <code>E</code> for <code>EAST</code>, negative numbers should be <code>WEST</code>.</p> <p>Then lookup whatever beach you are interested in, the closest to me is Egmond Aan Zee. The <a href="//cdn.cppse.nl/qw3295.pdf">manual</a> contains a bunch of them, but this list is not very complete, or useful at all IMO. And I learned in The Netherlands these times differ greatly per beach.</p> <p>So, better find the High and Low tide times yourself, in my case for Egmond Aan Zee:</p> <p><a href="https://egmondaanzee.org/nl/getijden-eb-vloed.php">https://egmondaanzee.org/nl/getijden-eb-vloed.php</a></p> <p>This lunitidal interval (HH:MM) is called a "havengetal" or "haventijd" in my language. And it's dutch definition is:</p> <p>"De haventijd is het tijdsverschil tussen de hoogste waterstand en de doorgang van zon of maan door de meridiaan, voor een gegeven plaats."</p> <p>Translated: the difference between the highest tide and the passing of the sun or moon through the meridian, for a given place.</p> <p>Today is the 4th of april 2020 with the highest tide at 13:44 (84 cm).</p> <!-- <a href="//cdn.cppse.nl/164-Selection_023.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_023.png" /></a> --> <p><center><a href="//cdn.cppse.nl/164-Selection_023.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_023.png" /></a></center></p> <p>According to this site the moon passes the meridian at exactly 23:00 for my location today.</p> <p><a href="https://www.timeanddate.com/moon/@2754516">https://www.timeanddate.com/moon/@2754516</a></p> <!-- <a href="//cdn.cppse.nl/164-Selection_028.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_028.png" /></a> --> <p><center><a href="//cdn.cppse.nl/164-Selection_028.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_028.png" /></a></center></p> <p>We need the difference:</p> <p>From <code>23:00</code> to <code>13:44</code> is: <code>14:44</code>.</p> <p>This results in settings for my casio: <code>4 LONG E + INT 14:44</code>.</p> <h3 id="testing">testing</h3> <p>Then test in the tide mode for different dates, and it should work! Personally I noticed that low tide is not completely synchronized, it's one "bar" later with the wave on the watch. I suspect that is because the actual graph is not a perfect sine wave, but looks a little skewed. I guess this may vary per beach.</p> <!--- https://www.nauticlink.com/nl/getijklok/NL_havengetallen_2009.pdf Haventijd maan meridiaan: 23:00 hoogste water: tijdsverschil tussen de hoogste waterstand en de doorgang van zon of maan door de meridiaan 13:44 84cm 24 - 9 --> <!-- <a href="//cdn.cppse.nl/164-Selection_029.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_029.png" /></a> --> <p><center><a href="//cdn.cppse.nl/164-Selection_029.png"><img border="0" src="//cdn.cppse.nl/164-thumb-Selection_029.png" /></a></center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 04 Apr 2020 00:00:00 +0000 Deploying owncloud via Helm on Kubernetes //blog.cppse.nl/deploy-owncloud-via-helm-on-kubernetes<div class="datestamp">October 19 2018</div> <div class="h1"><a href="//blog.cppse.nl/deploy-owncloud-via-helm-on-kubernetes"> <h1 id="deployingowncloudviahelmonkubernetes">Deploying owncloud via Helm on Kubernetes</h1> </a></div> <p>For this tutorial I'm assuming Kubernetes with Helm + Ingress is already deployed. If not, I still included the commands I used near the end of this article.</p> <p><center> <a href="//cdn.cppse.nl/160-logos.png"><img border="0" src="//cdn.cppse.nl/160-thumb-logos.png" /></a> </center></p> <h2 id="owncloud">OwnCloud</h2> <p>My NAS is running at home with <a href="https://rockstor.com">Rockstor</a>, and I'm using RAID10 with btrfs. Rockstor has (docker) apps support with the feature called Rock-on's, they also include OwnCloud, but after updating and some other issues with Rockstor at some point my deployment broke. This frustrated me so I've decided to switch to Kubernetes instead.</p> <p>I use my own cloud (no pun intended) as an alternative over using services owned by Google/Amazon/Apple. When you plan to do the same, just make sure to also make proper backups.</p> <h2 id="deployowncloudwithhelm">Deploy OwnCloud with Helm</h2> <p>Following <a href="https://github.com/helm/charts/tree/master/stable/owncloud">the instructions</a>; copy their default <code>values.yaml</code> (from <a href="https://github.com/helm/charts/blob/master/stable/owncloud/values.yaml">here</a>). Tweak all the values. It seems important to define a hostname! (If you try accessing the service later via IP address, the webinterface will not accept this.)</p> <p><code>helm install --name my-owncloud -f owncloud.yaml stable/owncloud --set rbac.create=true</code></p> <p>Notes: <code>owncloud.yaml</code> is my <code>values.yaml</code>, and I expect the <code>rbac.create=true</code> not to be needed but I used it anyway it was left over when copy &amp; pasting another command.. For convenience you can download my <a href="//cdn.cppse.nl/160-owncloud.yaml"><code>owncloud.yaml</code></a>.</p> <h2 id="owncloudwillrequiresomestorage.">Owncloud will require some storage.</h2> <p>In my case I made a btrfs share named <code>/mnt2/NAS/kubeowncloudstorage</code>. </p> <p>Then created three folders inside it:</p> <pre><code>mkdir -p /mnt2/NAS/kubeowncloudstorage/data mkdir -p /mnt2/NAS/kubeowncloudstorage/mariadb mkdir -p /mnt2/NAS/kubeowncloudstorage/apache</code></pre> <p>Set the right permissions for these folders, owncloud will write as user id(1).</p> <pre><code>chown 1:1 /mnt2/NAS/kubeowncloudstorage -R</code></pre> <p>Then apply the following yaml (<code>kubectl apply -f kube_owncloud_storage.yaml</code>):</p> <pre><code>nas:/root # cat kube_owncloud_storage.yaml kind: PersistentVolume apiVersion: v1 metadata: name: kube-owncloud-storage-data labels: type: local spec: capacity: storage: 3072Gi storageClassName: owncloud-storage-data accessModes: - ReadWriteOnce hostPath: path: /mnt2/NAS/kubeowncloudstorage/data --- kind: PersistentVolume apiVersion: v1 metadata: name: kube-owncloud-storage-mariadb labels: type: local spec: capacity: storage: 8Gi storageClassName: owncloud-storage-mariadb accessModes: - ReadWriteOnce hostPath: path: /mnt2/NAS/kubeowncloudstorage/mariadb --- kind: PersistentVolume apiVersion: v1 metadata: name: kube-owncloud-storage-apache labels: type: local spec: capacity: storage: 1Gi storageClassName: owncloud-storage-apache accessModes: - ReadWriteOnce hostPath: path: /mnt2/NAS/kubeowncloudstorage/apache</code></pre> <p>If you redeploy Kubernetes and/or the system in general, I forgot when exactly but a PersistentVolume may end up in a state that prevents PersistentVolumeClaim's to not bind to the Volumes. There was a trick to force it to bind, IIRC <code>kubectl edit pv kube-owncloud-storage-data</code> and you can remove the reference it has to an existing PVC. But it was a few weeks ago I experimented with this so sorry I don't remember the details. Only now I stumbled upon my notes and decided to wrap it up in a blog post.</p> <h2 id="setupingressbewareofcaveat">Setup ingress (beware of caveat!)</h2> <pre><code>nas:/root # cat owncloud_ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx ingress.kubernetes.io/proxy-body-size: 500m nginx.ingress.kubernetes.io/proxy-body-size: 500m name: owncloud namespace: default spec: rules: - host: ******DOMAIN NAME******* http: paths: - backend: serviceName: my-owncloud-owncloud servicePort: 80 path: /</code></pre> <p>Take a careful look at these two options in the annotations:</p> <pre><code>ingress.kubernetes.io/proxy-body-size: 500m nginx.ingress.kubernetes.io/proxy-body-size: 500m</code></pre> <p>They took me two hours of debugging, owncloud was throwing errors 413 Request Entity Too Large when syncing some larger video files from my phone to owncloud. Thinking this must be an issue inside owncloud I experimented with lots of parameters, fixes for php, apache, etc. Then realized it could be the Ingress in Kubernetes. The above example makes sure it doesn't block uploads up to half a gigabyte.</p> <h2 id="done">DONE!</h2> <p>The end result should look something like this in Kubernetes:</p> <pre><code>nas:/root # kubectl get all NAME READY STATUS RESTARTS AGE pod/my-nginx-nginx-ingress-controller-664f4547d8-vjgkt 1/1 Running 0 16d pod/my-nginx-nginx-ingress-default-backend-5bcb65f5f4-qrwcd 1/1 Running 0 16d pod/my-owncloud-mariadb-0 1/1 Running 0 16d pod/my-owncloud-owncloud-6cddfdc8f4-hmrh5 1/1 Running 2 16d NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 &lt;none&gt; 443/TCP 16d service/my-nginx-nginx-ingress-controller LoadBalancer 10.103.57.37 192.168.2.122 80:32030/TCP,443:30453/TCP 16d service/my-nginx-nginx-ingress-default-backend ClusterIP 10.101.16.224 &lt;none&gt; 80/TCP 16d service/my-owncloud-mariadb ClusterIP 10.104.48.71 &lt;none&gt; 3306/TCP 16d service/my-owncloud-owncloud LoadBalancer 10.102.95.4 &lt;pending&gt; 80:32287/TCP 16d NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deployment.apps/my-nginx-nginx-ingress-controller 1 1 1 1 16d deployment.apps/my-nginx-nginx-ingress-default-backend 1 1 1 1 16d deployment.apps/my-owncloud-owncloud 1 1 1 1 16d NAME DESIRED CURRENT READY AGE replicaset.apps/my-nginx-nginx-ingress-controller-664f4547d8 1 1 1 16d replicaset.apps/my-nginx-nginx-ingress-default-backend-5bcb65f5f4 1 1 1 16d replicaset.apps/my-owncloud-owncloud-6cddfdc8f4 1 1 1 16d NAME DESIRED CURRENT AGE statefulset.apps/my-owncloud-mariadb 1 1 16d nas:/root # kubectl get ingress NAME HOSTS ADDRESS PORTS AGE owncloud ***************** 80 16d nas:/root # kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE kube-owncloud-storage-apache 1Gi RWO Retain Bound default/my-owncloud-owncloud-apache owncloud-storage-apache 16d kube-owncloud-storage-data 3Ti RWO Retain Bound default/my-owncloud-owncloud-owncloud owncloud-storage-data 16d kube-owncloud-storage-mariadb 8Gi RWO Retain Bound default/data-my-owncloud-mariadb-0 owncloud-storage-mariadb 16d nas:/root # kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE data-my-owncloud-mariadb-0 Bound kube-owncloud-storage-mariadb 8Gi RWO owncloud-storage-mariadb 16d my-owncloud-owncloud-apache Bound kube-owncloud-storage-apache 1Gi RWO owncloud-storage-apache 16d my-owncloud-owncloud-owncloud Bound kube-owncloud-storage-data 3Ti RWO owncloud-storage-data 16d</code></pre> <h2 id="deployingkubeonasinglenodemachinenotes">Deploying Kube on a single node machine notes</h2> <p>Just in case you are also attempting to install Kubernetes for the first time, a reference of the commands used in my setup. First I followed the official docs to deploy kubeadm,kubelet etc. <a href="https://kubernetes.io/docs/setup/independent/install-kubeadm/">See here.</a></p> <p>My init looked like this:</p> <pre><code>kubeadm init --pod-network-cidr=192.168.0.0/16</code></pre> <p>At this point you may get some errors, and you have to fix the errors, maybe even <code>kubeadm reset</code> and then retry. Until I was okay with the remaining errors, I proceeded with: </p> <pre><code>kubeadm init --pod-network-cidr=192.168.0.0/16 --ignore-preflight-errors=all # these steps will be recommended from above command: mkdir -p $HOME/.kube sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # I chose calico for networking kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml kubectl apply -f https://docs.projectcalico.org/v3.1/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml # Then after a while (maybe check if kubelet etc. come up correctly, try "kubectl get no") # Make sure the master node is not excluded for running pods. kubectl taint nodes --all node-role.kubernetes.io/master- # I also executed this patch, but I think it's not needed anymore, it was still in my helper script kubectl -n kube-system get deployment coredns -o yaml | sed 's/allowPrivilegeEscalation: false/allowPrivilegeEscalation: true/g' | kubectl apply -f - # Then I looked up the kubelet service file with `systemctl cat kubelet` and edited: vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf # added this to above file, the --resolv-conf: # #ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS --resolv-conf=/etc/resolv.conf # #ALSO: I edited /etc/resolv.conf, I removed the ipv6 nameserver entry, and added 8.8.8.8 as per https://hk.saowen.com/a/e6cffc1e02c2b4643bdd525ff9e8e4cfb49a4790062508dca478c0c8a0361b5a systemctl daemon-reload systemctl restart kubelet kubectl get pod -n kube-system kubectl delete pod coredns-68fb79bcf6-9zdtz -n kube-system kubectl delete pod coredns-68fb79bcf6-t7vsm -n kube-system kubectl get pod -n kube-system -o wide</code></pre> <p>Solution for the last bit I got from <a href="https://github.com/coredns/coredns/issues/2087">here</a>. However this may have been a random issue that I just ran into, because on different servers I don't recall I had to the steps regarding coredns.</p> <p>Possible commands</p> <pre><code>helm reset --force helm init --upgrade --service-account tiller # don't remember if these two commands were still necessary kubectl create serviceaccount --namespace kube-system tiller kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller</code></pre> <h2 id="unrelatednotesaboutownclouditself">Unrelated notes about Owncloud itself</h2> <p>Links for solutions for problems that I ran into at some point in time:</p> <ul> <li>https://central.owncloud.org/t/file-is-locked-how-to-unlock/985</li> </ul> <p>Links that eventually pointed me in the right direction for the <code>413 Request Entity Too Large</code> error.</p> <ul> <li>https://forum.owncloud.org/viewtopic.php?t=23199</li> <li>https://www.keycdn.com/support/413-request-entity-too-large</li> <li>https://stackoverflow.com/questions/18740419/how-to-set-allowoverride-all</li> <li>https://github.com/nginxinc/kubernetes-ingress/issues/21</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 19 Oct 2018 00:00:00 +0000 How to print an endlessly folding card //blog.cppse.nl/print-endless-folding-card<div class="datestamp">June 21 2018</div> <div class="h1"><a href="//blog.cppse.nl/print-endless-folding-card"> <h1 id="howtoprintanendlesslyfoldingcard">How to print an endlessly folding card</h1> </a></div> <p>I remember having something like this as a child: <a href="http://www.endlessfoldingcard.com/endless-folding-card-p-768-918.html">http://www.endlessfoldingcard.com/endless-folding-card-p-768-918.html</a>. It was not that one but something similar, a flyer for some stupid product. I was fascinated by it and could keep folding it for hours. It was nice made out of some very strong paper/cardboard, unfortunately I probably got rid of it at some point.</p> <p>It took me a long time to find it again, every now and then I would try to look it up on Google (with years in between), unable to find it. Until I had a moment of clarity and tried the additional keyword "Endless", and finally found something that I remembered.</p> <h2 id="figuringouthowitworks">Figuring out how it works</h2> <p>All the YouTube videos I found would basically fold the thing together, and then decorate the card afterward, however I wanted to print it beforehand in such a way that it would turn out nicely when folded. To be honest <a href="https://www.youtube.com/watch?v=s_PP5JY8ubo">this one</a> does attempt to explain some of the layouting, but it wasn't clear enough for me. This is <a href="https://www.youtube.com/watch?v=BOSIHIQhjjQ">another video</a> that shows how to fold it clearly.</p> <p>There are a few things that you will notice when you fold one, some parts of the card stay constant for example, and not all views have corners. Anyway I decided to treat it as a grid of tiles. I just printed 2 two-sided pieces of paper with unique numbers for each tile. Then I deciphered which tiles end up where after folding, and which ones are rotated 180 degrees. See madness below.</p> <p><style> .askdgjlkasjdglkajsdgl img { width: 100px; } .askdgjlkasjdglkajsdgl2 img { width: 200px; } </style></p> <div class="askdgjlkasjdglkajsdgl2"> <a href="//cdn.cppse.nl/156-IMG_20180529_225017.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-IMG_20180529_225017.jpg" /></a> &nbsp; <a href="//cdn.cppse.nl/156-IMG_20180531_010651.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-IMG_20180531_010651.jpg" /></a> &nbsp; <a href="//cdn.cppse.nl/156-IMG_20180531_010617.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-IMG_20180531_010617.jpg" /></a> &nbsp; <a href="//cdn.cppse.nl/156-IMG_20180531_010608.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-IMG_20180531_010608.jpg" /></a> </div> <p>See the cardboard in action here: <a href="//cdn.cppse.nl/VID_20180531_011204.mp4">VID_20180531_011204.mp4</a></p> <h2 id="designandprintthecard">Design and print the card!</h2> <p>Designing the card in something like gimp is a lot of work of course, and it would be hell if you need to prepare it manually for printing. Luckily I wrote a C++ program that uses the very cool <a href="https://github.com/kmhofmann/selene">Selene</a> Image library, which I learned about via the Dutch C++ User Group <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sD" />, Michael (the author) gave an awesome lightning talk about it. It was good timing because a few days after that I needed to write this program.</p> <p>I didn't put this code on github, but here is the source code:</p> <pre class="prettyprint lang-cpp"><code data-language="c">// This file is using the `Selene` library. // Copyright 2017-2018 Michael Hofmann (https://github.com/kmhofmann). // Distributed under MIT license. See accompanying LICENSE file in the top-level directory. #include &lt;selene/img/ImageAccess.hpp&gt; #include &lt;selene/img/ImageDataToImage.hpp&gt; #include &lt;selene/img/ImageToImageData.hpp&gt; #include &lt;selene/img_io/IO.hpp&gt; #include &lt;selene/img_ops/Algorithms.hpp&gt; #include &lt;selene/img_ops/Transformations.hpp&gt; #include &lt;selene/io/FileReader.hpp&gt; #include &lt;selene/io/FileWriter.hpp&gt; #include &lt;cassert&gt; #include &lt;iostream&gt; #include &lt;string&gt; #include &lt;boost/filesystem.hpp&gt; using namespace sln::literals; struct tile { size_t index; size_t folded_index; bool flipped; }; struct tile_index { sln::PixelIndex x; sln::PixelIndex y; }; // clang-format off std::array&lt;tile, 64&gt; tiles {{ /** * There are basically four sides (a,b,c,d), which I've divided into 16 tiles per side. * Side a &amp; b will be printed on one piece of paper, double sided, as will sides c &amp; d. * In total there are 64 tiles, and this table basically combines two states for each tile. * * When designing you create four &quot;views&quot; for each side, which is the desired outcome, * or the first state of each tile. Like: * {1, 2, 3, ..., 17, 18, 19, ..., 62, 53, 64.} (nothing will be rotated) * * But once you print them and glue them together, and you fold your way through the card, * the tiles appear in different order, and sometimes even rotated 180 degrees. * This is the second state of the tiles. Something like: * {1, 2, 3, ..., 19, 50, 51, ..., 43+rotate, 42+rotate, 43+rotate} * * Both states are combined in this table, for each tile the following fields: * - index: the tile number (for the first state) * - folded index: the tile number (for the second state) * - flipped: whether the tile is rotated 180 degrees in the 2nd state. * * So basically what needs to happen is move each tile from index -&gt; folded_index, rotate * if needed, and you can just print the resulting images and when you assemble the card * all your designs appear the way you intended them to. Doing this in something like * Photoshop or Gimp would be a huge pain, so that's why I made this program. */ // a {1, 1, false}, {2, 2, false}, {3, 3, false}, {4, 4, false}, {5, 5, false}, {6, 6, false}, {7, 7, false}, {8, 8, false}, {9, 9, false}, {10, 10, false}, {11, 11, false}, {12, 12, false}, {13, 13, false}, {14, 14, false}, {15, 15, false}, {16, 16, false}, // b (corner tiles are glued) {17, 19, false}, {18, 50, false}, {19, 51, false}, {20, 18, false}, {21, 23, false}, {22, 54, false}, {23, 55, false}, {24, 22, false}, {25, 27, false}, {26, 58, false}, {27, 59, false}, {28, 26, false}, {29, 31, false}, {30, 62, false}, {31, 63, false}, {32, 30, false}, // c (corner tiles are unset, tiles on left and right are similar as previous (side b)) {33, 0, false}, {34, 53, true}, {35, 56, true}, {36, 0, false}, {37, 0, false}, {38, 24, false}, {39, 21, false}, {40, 0, false}, {41, 0, false}, {42, 28, false}, {43, 25, false}, {44, 0, false}, {45, 0, false}, {46, 57, true}, {47, 60, true}, {48, 0, false}, // d (corner tiles are glued) {49, 40, true}, {50, 39, true}, {51, 38, true}, {52, 37, true}, {53, 36, true}, {54, 35, true}, {55, 34, true}, {56, 33, true}, {57, 48, true}, {58, 47, true}, {59, 46, true}, {60, 45, true}, {61, 44, true}, {62, 43, true}, {63, 42, true}, {64, 41, true}, }}; // clang-format on template &lt;typename PixelType&gt; sln::Image&lt;PixelType&gt; load_image(const char* filename) { auto img_data = sln::read_image(sln::FileReader(filename)); if (!img_data.is_valid()) { std::cerr &lt;&lt; &quot;Image data could not be decoded.&quot; &lt;&lt; std::endl; } const auto img = sln::to_image&lt;PixelType&gt;(std::move(img_data)); assert(img.is_valid()); return img; } template &lt;typename T&gt; void copy(T src, T dst) { for (auto y = 0_idx; y &lt; src.height(); ++y) { for (auto ptr = src.data(y), end = src.data_row_end(y), ptr2 = dst.data(y); ptr != end; ++ptr, ++ptr2) { *ptr2 = *ptr; } } } int main() { // load the four all-equal in size views for the endless card auto img1 = load_image&lt;sln::Pixel_8u4&gt;(&quot;card_a.png&quot;); auto img2 = load_image&lt;sln::Pixel_8u4&gt;(&quot;card_b.png&quot;); auto img3 = load_image&lt;sln::Pixel_8u4&gt;(&quot;card_c.png&quot;); auto img4 = load_image&lt;sln::Pixel_8u4&gt;(&quot;card_d.png&quot;); assert(img1.width() == img2.width() &amp;&amp; img1.height() == img2.height()); assert(img2.width() == img3.width() &amp;&amp; img2.height() == img3.height()); assert(img3.width() == img4.width() &amp;&amp; img3.height() == img4.height()); // concat all images into one long image, making it easier to calculate coordinates for each tile sln::Image&lt;sln::Pixel_8u4&gt; input(img1.width(), sln::to_pixel_length(img1.height() * 4)); sln::Image&lt;sln::Pixel_8u4&gt; output = clone(input); copy(img1, sln::view(input, 0_idx, 0_idx, img1.width(), img1.height())); copy(img2, sln::view(input, 0_idx, sln::to_pixel_index(img1.height() * 1), img1.width(), img1.height())); copy(img3, sln::view(input, 0_idx, sln::to_pixel_index(img1.height() * 2), img1.width(), img1.height())); copy(img4, sln::view(input, 0_idx, sln::to_pixel_index(img1.height() * 3), img1.width(), img1.height())); // helper lambda to get coordinate for a given tile number const auto tile_width = sln::to_pixel_length(img1.width() / 4); const auto tile_height = sln::to_pixel_length(img1.height() / 4); auto index_to_x_and_y = [=](size_t index) -&gt; tile_index { const size_t x = ((index - 1) % 4) * static_cast&lt;sln::PixelLength::value_type&gt;(tile_width); const size_t y = ((index - 1) / 4) * static_cast&lt;sln::PixelLength::value_type&gt;(tile_height); return {sln::to_pixel_index(x), sln::to_pixel_index(y)}; }; // copy each tile to the correct location for printing the endless card for (const auto&amp; tile : tiles) { auto src_index = index_to_x_and_y(tile.index); auto dst_index = index_to_x_and_y(tile.folded_index); if (tile.folded_index == 0) { continue; } auto src = sln::view(input, src_index.x, src_index.y, tile_width, tile_height); auto dst = sln::view(output, dst_index.x, dst_index.y, tile_width, tile_height); if (tile.flipped) { copy(sln::rotate&lt;sln::RotationDirection::Clockwise180&gt;(src), dst); } else { copy(src, dst); } } // debug sln::write_image(to_image_data_view(input, sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result.png&quot;)); sln::write_image(to_image_data_view(output, sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result2.png&quot;)); // write the resulting cards ready to print, result a + b double sided, and c + d double sided. sln::write_image( to_image_data_view(sln::view(output, 0_idx, 0_idx, img1.width(), img1.height()), sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result_a.png&quot;)); sln::write_image( to_image_data_view(sln::view(output, 0_idx, sln::to_pixel_index(img1.height() * 1), img1.width(), img1.height()), sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result_b.png&quot;)); sln::write_image( to_image_data_view(sln::view(output, 0_idx, sln::to_pixel_index(img1.height() * 2), img1.width(), img1.height()), sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result_c.png&quot;)); sln::write_image( to_image_data_view(sln::view(output, 0_idx, sln::to_pixel_index(img1.height() * 3), img1.width(), img1.height()), sln::PixelFormat::RGBA), sln::ImageFormat::PNG, sln::FileWriter(&quot;result_d.png&quot;)); return 0; } </code></pre> <h2 id="inputoutput">Input &amp; Output</h2> <p>The program reads <code>card_a.png</code>, <code>card_b.png</code>, <code>card_c.png</code>, <code>card_d.png</code>. Does it's magic and procudes: <code>result_a.png</code>, <code>result_b.png</code>, <code>result_c.png</code>, <code>result_d.png</code>.</p> <div class="askdgjlkasjdglkajsdgl"> <a href="//cdn.cppse.nl/156-result.png"><img border="0" src="//cdn.cppse.nl/156-thumb-result.png" /></a> <-- the *input* images <a href="//cdn.cppse.nl/156-result2.png"><img border="0" src="//cdn.cppse.nl/156-thumb-result2.png" /></a> <-- the *output* images </div> <p>As you can see the resulting images look a bit weird, but when printed double sidedly, card <code>A</code> and <code>B</code> on one side, and card <code>C</code> and <code>D</code> on the other side of the paper, you can cut them out and fold them.</p> <h2 id="howtofold">How to fold?</h2> <p>This is how I did it, I didn't plan to make a blog post so I didn't completely document every step. But at least on this picture should give you an idea:</p> <div class="askdgjlkasjdglkajsdgl2"> <a href="//cdn.cppse.nl/156-IMG_20180609_125437.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-IMG_20180609_125437.jpg" /></a> </div> <p>For folding I bought a "Scor-Pal", I can recommend this tool it makes nice folds, doing without would give crappy results on thick paper IMO.</p> <p>The polar bear piece of paper is side <code>A</code> &amp; <code>C</code>, and cut in the middle already, and two horizontal pre-folds are made (sloppy ones though <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sP" />). The other two sides <code>C</code> &amp; <code>D</code> are pre-folded as well, but I didn't cut it horizontally yet. After cutting, glue the white edges on sides <code>B</code> &amp; <code>C</code> together, and have it dry, the card should be done.</p> <h2 id="conclusion">Conclusion</h2> <p>After having folded about 60 of these I finally got the hang of it and I could produce pretty slick cards. One thing I did was print myself with a color laser printer on very thick paper, this gave "meh" results, the toner had trouble sticking to the paper especially after folding. I recommend doing the printing at a specialized shop, maybe to avoid the toner letting loose, but also because aligning both sides is important. Not all printers are great at this I've discovered, especially if you have to use the manual feed for the thick paper.</p> <p>What worked best is this order:</p> <ul> <li>Printing both sides</li> <li>Cut out the two sides with something like <div class="askdgjlkasjdglkajsdgl"><a href="//cdn.cppse.nl/156-ALBRSA4-TR410.jpg"><img border="0" src="//cdn.cppse.nl/156-thumb-ALBRSA4-TR410.jpg" /></a></div></li> <li>Do the (in total four) pre-folds on both pieces of paper (do them in the right direction)</li> <li>Cut the first one vertically, perfectly in the middle.</li> <li>Cut the second one horizontally, perfectly in the middle.</li> <li>Put glue on the corners for the second one now (the one cut horizontally)</li> <li>Then align those two pieces perfectly so it appears whole again.</li> <li>One by one add the other (the one cut vertically) two pieces, you'll find with the glue still wet it's easy to adjust.</li> <li>When done, let it dry, and later on pre-fold some more for the definitive card.</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 21 Jun 2018 00:00:00 +0000 i3 floating point window resize percentage wise and centered //blog.cppse.nl/i3-floating-window-resize<div class="datestamp">May 30 2018</div> <div class="h1"><a href="//blog.cppse.nl/i3-floating-window-resize"> <h1 id="i3floatingpointwindowresizepercentagewiseandcentered">i3 floating point window resize percentage wise and centered</h1> </a></div> <p>I'm a heavy user of scratch pads with i3, I often don't like the dimensions of a window after you make them floating. As do other people, see <a href="https://github.com/i3/i3/issues/1949">here</a> and <a href="https://github.com/i3/i3/issues/2816">here2</a>.</p> <p>I've used a customized version of the solution proposed in one of the comments by the creator of i3-gaps (Airblader) <a href="https://github.com/i3/i3/issues/1949#issuecomment-142270350">here3</a>. This has served me well, but one thing bugged me when using multiple monitors it wouldn't center the window correctly, so I made a Python script that first uses Qt to get all screen dimensions and determine the correct offset based on the Mouse position. It's a bit overkill probably, but it works, so I'm happy with it.</p> <h3 id="step1:downloadmakeandmakeinstallwmutilswm.">Step 1: download, make, and make install <a href="https://github.com/wmutils/core">wmutils</a>.</h3> <p>Note that if you update your system in the meantime, it may have to be recompiled at some point, I've experienced this with the <code>lsw</code> command which is using some X calls that changed after I updated from Ubuntu 17.04 -> 17.10.</p> <h3 id="step2:putthispythonscriptsomewhere">Step 2: put this python script somewhere</h3> <pre><code>#!/usr/bin/env python3 import os import os.path import psutil import subprocess import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from pymouse import PyMouse from sys import platform def clamp(n, smallest, largest): return max(smallest, min(n, largest)) def current_screen_size(mouse_position): for screen in app.screens(): (x, y) = mouse_position screen_geom = screen.availableGeometry(); if (x &gt;= screen_geom.left() and y &gt;= screen_geom.top() and x &lt;= screen_geom.left() + screen_geom.width() and y &lt;= screen_geom.top() + screen_geom.height()): return screen_geom return app.desktop().availableGeometry(-1) # read ratio from config file ratio_file = "/tmp/resize_ratio.txt" ratio = 70 if os.path.isfile(ratio_file): file = open(ratio_file, "r") ratio = int(file.read()) file.close() if len(sys.argv) &gt; 1: if sys.argv[1] == 'inc': ratio += 10 elif sys.argv[1] == 'dec': ratio -= 10 ratio = clamp(ratio, 10, 100) # get mouse and screen specs app = QApplication(sys.argv) mouse = PyMouse() screen = current_screen_size(mouse.position()) # call wmutils::core utilities currentWindow = subprocess.check_output(["pfw"]) # resize the window new_width = (screen.width() / 100) * ratio new_height = (screen.height() / 100) * ratio # subprocess.call([ # "wrs", # "-a", # str(new_width), # str(new_height), # currentWindow # ]) # position the window centered (+ resize) new_x = screen.left() + ((screen.width() - new_width) / 2) new_y = screen.top() + ((screen.height() - new_height) / 2) subprocess.call([ "wtp", str(new_x), str(new_y), str(new_width), str(new_height), currentWindow ]) # persist current ratio file = open("/tmp/resize_ratio.txt", "w") file.write(str(ratio)) file.close() </code></pre> <p>Don't forget <code>chmod +x /path/to/resize.py</code></p> <p>You may need to install some <code>python3 -m pip install ...</code> when you try to run it you'll probably discover what exactly, I forgot to keep a requirements.txt. From what I remember you need at least: <code>python -m pip install pyuserinput pyqt5 python-xlib</code></p> <h3 id="step3:modifyresizemodeini3">Step 3: modify resize mode in i3</h3> <p>Probably you already have a "resize" mode, just add something like <code>SHIFT</code>+<code>J</code> and <code>SHIFT</code>+<code>K</code> to that mode to call the python script:</p> <pre><code>mode "resize" { bindsym Shift+k exec $HOME/.bin/resize.py inc bindsym Shift+j exec $HOME/.bin/resize.py dec ... } bindsym $mod+r mode "resize"</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 30 May 2018 00:00:00 +0000 My visit to Meeting C++ 2016! //blog.cppse.nl/visit-to-meetingcpp-2016<div class="datestamp">November 21 2016</div> <div class="h1"><a href="//blog.cppse.nl/visit-to-meetingcpp-2016"> <h1 id="myvisittomeetingc2016">My visit to Meeting C++ 2016!</h1> </a></div> <p><a class="right" href="//cdn.cppse.nl/137-20161117_134305.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-20161117_134305.jpg" /></a> Finally, I was able to attend this conference, missing out two years in a row, and it was great. So far it has been the largest yet with 600 attendees, and AFAIK Bjarne Stroustrup was present for the first time this year.</p> <p>I went to Berlin with my girlfriend two days before the event so we had a chance to see Berlin. Even though the weather was very much what you would expect around this time of year, cloudy, rainy, <em>etc.</em> we had a great time. Especially renting bikes and sightseeing.</p> <p><center> <BR/> <i>[Image caption] Brief moment of no-rain..</i> </center></p> <h2 id="talksiattended...day1">Talks I attended... DAY 1</h2> <p><strong>Opening Keynote - Bjarne Stroustrup</strong></p> <p>What is C++ and what will it become? It was a nice presentation showing the strength of C++ and providing a little history here and there (like code below). Funny quote from the presentation "Only a computer scientist makes a copy then destroys the original"; The committee has a difficult task, making the C++ language less complex, but the only thing the committee can do is <em>add</em> more to it <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sB" />, but they still succeed (<em>i.e.</em>, with <code>auto</code>, <code>constexpr</code>, ..).</p> <pre><code>int i; // 70's? for (i=0; i&lt;10; i++) a[i] = 0; ---------- for (int i=0; i&lt;10; i++) a[i] = 0; // 80's? no declaration outside the for ---------- for (auto &amp;el : a) el = 0; // mistakes like reading out of bounds no longer possible // ... also mistakes like; for (int i=0; i&lt;10; j++) {}</code></pre> <p>Boris Sch&auml;ling asked "Scott Meyers retired from C++ a year ago; do we need to be worried about you?", luckily we don't have to worry ;-). Bjarne answered that he tried a few times to quit C++ in the past, but apparently he is not very good at it <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />.</p> <p><style style="text/css"> .bjarne2img img { height: 211px; } </style></p> <p><center> <span class="bjarne2img"><a href="//cdn.cppse.nl/137-20161118_102034.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-20161118_102034.jpg" /></a></span> <a href="//cdn.cppse.nl/137-20161118_113314.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-20161118_113314.jpg" /></a> </center></p> <p><strong>Learning and teaching Modern C++ - Arne Mertz</strong></p> <p>The speaker made an interesting point regarding some pitfalls, <em>i.e.</em> that many C++ developers learned C first, pointers, pointer arithmetic, C++03, C++11, .., basically a "layered evolution". However Modern C++ isn't a layered evolution, rather it is a "moving target". Nowadays we prefer <code>make_unique</code>, <code>unique_ptr</code> and therefor why not postpone teaching <code>new</code>, <code>delete</code>, <code>new[]</code>, <code>delete[]</code>, pointer arithmetic <em>etc.</em> when teaching Modern C++? The same goes for C-style arrays, more complex to teach as opposed to <code>std::array</code>. </p> <p>Actually kind of sad news; there are still schools in some Countries where C++ is taught with Turbo C++ (see this <a href="http://stackoverflow.com/questions/40398720/string-problems-with-turbo-c">SO question</a> from a few days ago) compiler (which is <em>extremely</em> outdated). Other notes I scribbled down were for me to check "clang tidy" and adding "<a href="https://isocpp.org">isocpp.org</a>" to my RSS feeds.</p> <p>Wouter van OOijen--a professor teaching C++ in the context of embedded devices--made a good point: the order in which material is presented to students is the most difficult thing to get right. In most books on C++ the order doesn't make sense for embedded, that's why he creates his own material.</p> <p><strong>Implementation of a multithreaded compile-time ECS in C++14 - Vittorio Romeo</strong></p> <p>This was quite interesting, maybe it was just me but in the beginning of the presentation it wasn't clear to me what an <a href="https://github.com/SuperV1234/ecst">Entity Component System</a> was, it became clear to me during the talk though. He walked us through the implementation, advanced templating, lambdas, bit fiddling, all quite interesting, maybe a bit too much content for one presentation but very impressive stuff. The room temperature during the presentation was extremely hot, making it sometimes difficult to concentrate and the talk went a bit over the scheduled time.</p> <p>Some stuff I found interesting: the usage of sparse sets, the use of proxy objects to make sure that certain methods of the library cannot be called at the wrong time.</p> <pre><code>ctx-&gt;step([&amp;](auto&amp; proxy) { // do something with proxy });</code></pre> <p>He went through a large list of features and how they are implemented <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <p><strong>Ranges v3 and microcontrollers, a revolution -- Odin Holmes</strong></p> <p><a class="right" href="//cdn.cppse.nl/137-20161118_160111.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-20161118_160111.jpg" /></a> Quite an awesome talk this one, the speaker is extremely knowledgeable on meta programming and embedded programming. His company works with devices with very little memory (just a few kilobyte) and this talk was very forward looking. There was a crash course regarding limitations for such devices, there is limited stack space, how do exceptions and interrupts play along with it.</p> <p>He then started with real demo/<code>hello world</code> for such a device and demonstrated how even that small code contained bugs and a lot of boilerplate. The rest of the talk he showed how to improve it, like instead of parsing (dangerously) with <code>scanf</code> (you can overflow the buffer, so you need a "large enough" buffer up-front... "And we all know that coming up with a <em>size</em> for a large enough buffer is easy, right?" <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />) can be replaced with a statemachine known at compile time. Ranges can be applied to lazy evaluate input, and as a result it would consume only the minimal memory.</p> <p><strong>C++ Today - The Beast is back - Jon Kalb</strong></p> <p>Why was C/C++ successful? It was based on proven track record, and not a "pure theoretical language". High-level abstractions at low cost, with a goal of zero-abstraction principle. In other words; not slower than you could do by coding the same feature by hand (i.e., vtables).</p> <p>If you like a good story and are curious about why there was a big red button on the IBM 360, the reason behind the C++ "Dark ages" (2000 - 2010), where very little seem to happen, then this is the presentation to watch. Spoiler alert: <em>cough</em> Java <em>cough</em>, OOP was the buzzword at the time, it was "almost as fast", computers got faster and faster, we "solved the performance issue"!</p> <p>Interesting statements I jotted down "Managed code optimizes the wrong thing (ease of programming)", and regarding Java's finally (<code>try {} catch {} finally {}</code>): "finally violates DRY". He then asked the audience a few times what DRY stands for, which is quite funny as some people realize they were indeed repeating themselves, not all as someone else yelled "the opposite of WET" <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sB" />. He also "pulled the age card" when discussing Alexander Stephanov (the author of the STL) "You kids think <code>std::vector</code> grew on trees!".</p> <h2 id="day2">DAY 2</h2> <p><strong>Functional reactive programming in C++ - Ivan Cukic</strong></p> <p>Talk of two parts, first functional programming: higher order functions, purity, immutable state. <em>Functional thinking</em> = data transformation. He discussed referential transparency, <em>f.i.</em> replacing any function with its value should produce the same outcome. This can depend on your definition.</p> <pre><code>int foobar() { std::cout &lt;&lt; "Returning 42..." &lt;&lt; '\n'; return 42; }</code></pre> <p>Above function when used in <code>int n = foobar();</code> can be replaced by 42, and the line of code would result in exactly the same thing (n containing 42), however the console output won't be printed. Whether you consider <code>std::cout</code> to count as part of the referential transparency is up to you.</p> <p>He continued with <em>Object thinking</em> = no getters, ask the object to do it. "Objects tend to become immutable.". I will have to review the presentation to get exactly what was meant by this.</p> <p>Next: reactive programming, if I am correct this was his definition:</p> <ul> <li>responds quickly</li> <li>resilient to failure</li> <li>responsive under workload</li> <li>based on message-passing</li> </ul> <p>Note: reacting <em>not</em> replying, i.e., piping Linux shell commands there is only one-way data flow. To conclude, some random notes I made during his talk below.</p> <ul> <li>He's writing a book on Functional programming in C++</li> <li><code>flatmap</code> from functional programming does <code>[x, a], [y, b, c]</code> -> <code>x, a, y, b, c</code>.</li> <li>His talk reminded me to lookup the meaning of placing <code>&amp;&amp;</code> behind a member function declaration.</li> </ul> <p>See below for an example from cppreference.com.</p> <pre><code>#include &lt;iostream&gt; struct S { void f() &amp; { std::cout &lt;&lt; "lvalue\n"; } void f() &amp;&amp;{ std::cout &lt;&lt; "rvalue\n"; } }; int main(){ S s; s.f(); // prints "lvalue" std::move(s).f(); // prints "rvalue" S().f(); // prints "rvalue" }</code></pre> <p><strong>The Speed Game: Automated Trading Systems in C++ - Carl Cook</strong></p> <p><a class="right" href="//cdn.cppse.nl/137-20161119_102916.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-20161119_102916.jpg" /></a> This talk was probably one of the most well attended talks at the conference. The room was packed. Coming in slightly late I had to sit down on my knees for the entire talk. Which was worth it, I think I liked this talk most of all I attended. It was just the right mix of super interesting material and practical advice.</p> <p>Coming from Amsterdam where Automated Trading companies seem to kind of dominate C++, it has always been very mysterious what exactly it is they do. It felt to me like it was basically the first time the veil was lifted a little bit. It's just amazing to hear how far they go in order to get the lowest latency possible. Within the time it takes for light to travel from the ground to the top of the Eiffel tower they can take an order, assess whether it's interesting or not, and place the order... times ten!</p> <pre><code>// Some practical advice, instead of the following.. if (checkForErrorA) handleErrorA(); elseif (checkForErrorB) handleErrorB(); elseif (checkForErrorC) handleErrorC(); else executeHotPath(); // Aim for this.. uint32_t errorFlags; if (errorFlags) handleError(errorFlags); else { ... hotpath }</code></pre> <p>Really interesting talk to watch whenever it comes online, it shows the importance of optimizing hardware, bypassing the kernel completely in the hot path, staying in user space for 100%, this includes network I/O (<em>f.i.</em>, <a href="http://www.openonload.org/">OpenOnload</a>), cache warming, beware of signed/unsigned conversions, check the assembly, <code>inplace_function</code> (the speakers proposals, <code>stdext::inplace_function&lt;void(), 32&gt;</code>), benchmarking without the 'observable effect' by observing network packets, and more.</p> <p>One note regarding Network I/O for example; if you read a lot but very little is interesting to the hot path, you may negatively affect your cache. A solution would be to offload all the reads to a different CPU and <em>cherry-pick</em> only the interesting reads and send them to the "hot" CPU.</p> <p><strong>Lock-free concurrent toolkit for hazard pointers and RCU - Michael Wong</strong></p> <p>Well, I was a bit tired at this point, so I cannot do the talk justice with a very thorough summary. Even if I could it's better to watch it from Michael Wong himself, because the slides help a lot in understanding the story.</p> <p>I did learn a few things, maybe the first lesson for me is to try stay away from all of this.. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sB" /> Still, aside from being super complicated, it's also an interesting topic, and good to know more about. The ABA problem: he had good slides that visualized actually step-by-step the challenge of updating data in a multi-threading situation, having readers while writing to it, all wrapped in a fun story of Schr&ouml;dingers Cat (and Zoo). Solutions discussed were hazard pointers and RCU (Read Copy Update).</p> <p>The gains you can get by starting late, having a grace period so you can do multiple updates at the same time are interesting to learn about. Situations where "being lazy" actually pays off!</p> <p><strong>Lightning talks!</strong></p> <p><a class="right" href="//cdn.cppse.nl/137-CxoRxwmXcAAz5zT.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-CxoRxwmXcAAz5zT.jpg" /></a> Surprise! They had secret lightning talks planned. To be honest at first I thought it was a bit long to have 1 hour and 40 minutes planned for a Meeting C++ update/review, so this was a nice surprise. My favorite lightning talk was from Michael Caisse reading from the standard as if it were a very exiting story, hilarious. Second James McNellis' "function pointers all the way down" (like "<a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">Turtles all the way down</a>", actually Bjarne also had a reference to this in his keynote). The remaining lightning talks were also very good: Michael Wong, Jens Weller, Chandler Carruth, and Bjarne's. The latter on Concepts was quite interesting; "what makes a good concept?" It has to have semantics specifying it, which in practice seems to be an efficient design technique. Quite funny was his "Onion principle" on abstractions (IIRC?), "you peel away layer by layer, and you cry more and more as you go along" <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />. Also Jens talk was really fun, it started with end of the world scenarios, working towards the future C++ standards.</p> <p><strong>C++ metaprogramming: evolution and future directions - Louis Dionne</strong></p> <p>The closing keynote was a really clear and relaxed presentation of how meta programming evolved, and in particular how <code>boost::hana</code> did. Again a nice lesson of history where Alexandrescu's Modern C++, boost::mpl, boost::fusion and the like all passed the revue. He showed what you can do with <code>boost::hana</code> at compile-time <em>and</em> runtime. His talk really opened my eyes on using <code>constexpr</code>, <code>integral_constant</code>, differences in meta programming with <em>types</em> and <em>objects</em>, and a lot more. It's amazing what his library can do. He argued the world needs more meta programming, but less <em>template</em> meta programming and concluded by sharing his view for the future.</p> <h2 id="theconference">The conference</h2> <p>There was a fun quiz, with really difficult puzzles (C++ programs) that had to be solved in &lt; 3 minutes each. This was basically similar to peeling Bjarne's Onion.. but in a good way.</p> <p>Between talks there were lunch-break Meetups planned (each 20 minutes, each had a specific topic). I attended two and my view is that it's a great idea, but the fact people have to come from talks, and leave on time to catch the next one, sometimes caused the time to be way too short (or yourself missing out on a talk because the room is now full).</p> <p>The organization was super, the drinks and food, especially the second day. The Andel's Hotel is a really good location, the Hotel as well (if you are lucky enough to get a room there). For me it was all really worth the money.</p> <p>Personally I like to write down a summary for myself, but I hope this blog post was also a fun to read to someone else!</p> <!-- <a href="//cdn.cppse.nl/137-CxiFxouWQAABk1s.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-CxiFxouWQAABk1s.jpg" /></a> <a href="//cdn.cppse.nl/137-CxiMe4dXcAAH47s.jpg"><img border="0" src="//cdn.cppse.nl/137-thumb-CxiMe4dXcAAH47s.jpg" /></a> --> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 21 Nov 2016 00:00:00 +0000 Kerberizing Cloudera Manager //blog.cppse.nl/kerberizing-cloudera-manager<div class="datestamp">September 6 2016</div> <div class="h1"><a href="//blog.cppse.nl/kerberizing-cloudera-manager"> <h1 id="kerberizingclouderamanager">Kerberizing Cloudera Manager</h1> </a></div> <p>The following steps are to quickly test how this stuff works.</p> <p>Using my docker images (<a href="https://hub.docker.com/r/rayburgemeestre/cloudera-master/">master</a>, <a href="https://hub.docker.com/r/rayburgemeestre/cloudera-slave/">slave</a>) and helper scripts on <a href="https://bitbucket.org/rayburgemeestre/hadoop-docker/">github</a>, it's easy to get Cloudera Manager running inside a few docker containers. Steps: get most recent docker, install (GNU) screen, checkout the repo, in there do <code>cd cloudera</code>, <code>bash start_all.sh</code>. This should do it. Note that the image(s) require being able to invoke <code>--privileged</code> and the scripts currently invoke <code>sudo</code>. After running the script you get something like (full example output <a href="//cdn.cppse.nl/101-example-output.txt">here</a>).</p> <pre><code>CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 31e5ee6b7e65 rayburgemeestre/cloudera-slave:3 "/usr/sbin/init" 20 seconds ago Up 17 seconds node003 f052c52b02bf rayburgemeestre/cloudera-slave:3 "/usr/sbin/init" 25 seconds ago Up 23 seconds node002 1a50df894f28 rayburgemeestre/cloudera-slave:3 "/usr/sbin/init" 30 seconds ago Up 29 seconds 0.0.0.0:8888-&gt;8888/tcp node001 54fd3c1cf93b rayburgemeestre/cloudera-master:3 "/usr/sbin/init" 50 seconds ago Up 48 seconds 0.0.0.0:7180-&gt;7180/tcp cloudera</code></pre> <p>Not really in the way docker was designed perhaps, it's running <code>systemd</code> inside, but for simple experimentation this is fine. These images have not been designed to run in production, but perhaps with some more orchestration it's possible <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />.</p> <h2 id="step1:installclouderamanager">Step 1: install Cloudera Manager</h2> <p>One caveat because of the way docker controls <code>/etc/resolv.conf</code>, <code>/etc/hostname</code>, <code>/etc/hosts</code>, these guys show up in the output for the <code>mount</code> command. The Cloudera Manager Wizard does some parsing of this (I guess) and pre-fills some directories with values like:</p> <pre><code>/etc/hostname/&lt;path dn&gt; /etc/resolv.conf/&lt;path dn&gt; /etc/hosts/&lt;path dn&gt;</code></pre> <p>Just remove the additional two paths, and change one to <code>&lt;path dn&gt;</code> only. There is a few of these configuration parameters that get screwed up. (Checked until &lt;= CDH 5.8)</p> <h2 id="step2:installkerberospackagesontheheadnode">Step 2: install kerberos packages on the headnode</h2> <pre><code>docker exec -i -t cloudera /bin/bash # go into the docker image for headnode yum install krb5-server krb5-workstation krb5-libs # ntp is already working systemctl enable krb5kdc systemctl enable kadmin</code></pre> <p>Configuration files need to be fixed, so starting will not work yet.</p> <h2 id="step3:modifyetckrb5.conf">Step 3: modify /etc/krb5.conf</h2> <p>Into something like:</p> <pre><code>[logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] dns_lookup_realm = false ticket_lifetime = 24h renew_lifetime = 7d forwardable = true rdns = false default_realm = MYNET default_ccache_name = KEYRING:persistent:%{uid} [realms] MYNET = { kdc = cloudera.mynet admin_server = cloudera.mynet } [domain_realm] .mynet = MYNET mynet = MYNET</code></pre> <p>In this example <code>cloudera.mynet</code> is just <code>hostname --fqdn</code> of the headnode which will be running kerberos. (Note that <code>mynet</code> / <code>MYNET</code> could also be something like <code>foo.bar</code> / <code>FOO.BAR</code>.)</p> <h2 id="step4:modifyvarkerberoskrb5kdckdc.conf">Step 4: modify /var/kerberos/krb5kdc/kdc.conf</h2> <pre><code>[kdcdefaults] kdc_ports = 88 kdc_tcp_ports = 88 [realms] MYNET = { #master_key_type = aes256-cts master_key_type = aes256-cts-hmac-sha1-96 max_life = 24h 10m 0s max_renewable_life = 30d 0h 0m 0s acl_file = /var/kerberos/krb5kdc/kadm5.acl dict_file = /usr/share/dict/words admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal aes256-cts-hmac-sha1-96 }</code></pre> <p>I specifically added <code>aes256-cts-hmac-sha1-96</code> as master key and supported encryption types, and the <code>max_life</code> plus <code>max_renewable_life</code> properties.</p> <p>But there is a chance Cloudera Manager might add this stuff as well.</p> <h2 id="step5:modifyvarkerberoskrb5kdckadm5.acl">Step 5: modify /var/kerberos/krb5kdc/kadm5.acl</h2> <pre><code>*/admin@MYNET *</code></pre> <h2 id="step6:initializethedatabase">Step 6: initialize the database</h2> <pre><code># kdb5_util create -r MYNET -s Loading random data Initializing database '/var/kerberos/krb5kdc/principal' for realm 'MYNET', master key name 'K/M@MYNET' You will be prompted for the database Master Password. It is important that you NOT FORGET this password. Enter KDC database master key: ****** Re-enter KDC database master key to verify: ******</code></pre> <h2 id="step7:addmasterrootadminuser">Step 7: add master <code>root/admin</code> user</h2> <pre><code>[root@rb-clouderahadoop2 krb5kdc]# kadmin.local Authenticating as principal root/admin@MYNET with password. kadmin.local: addprinc root/admin WARNING: no policy specified for root/admin@MYNET; defaulting to no policy Enter password for principal "root/admin@MYNET": ****** Re-enter password for principal "root/admin@MYNET": ****** Principal "root/admin@MYNET" created. kadmin.local: ktadd -k /var/kerberos/krb5kdc/kadm5.keytab kadmin/admin Entry for principal kadmin/admin with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type camellia256-cts-cmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type camellia128-cts-cmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type des-hmac-sha1 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/admin with kvno 2, encryption type des-cbc-md5 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. kadmin.local: ktadd -kt /var/kerberos/krb5kdc/kadm5.keytab kadmin/changepw Entry for principal kadmin/changepw with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type camellia256-cts-cmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type camellia128-cts-cmac added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type des-hmac-sha1 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. Entry for principal kadmin/changepw with kvno 2, encryption type des-cbc-md5 added to keytab WRFILE:/var/kerberos/krb5kdc/kadm5.keytab. kadmin.local: exit</code></pre> <p>This will be the user we will give Cloudera to take over managing kerberos.</p> <h2 id="step8:startservices">Step 8: start services</h2> <pre><code>systemctl start krb5kdc systemctl start kadmin</code></pre> <h2 id="step9:dotheenablesecuritywizardinclouderamanager">Step 9: do the <code>Enable security</code> wizard in Cloudera Manager</h2> <p>This should be self explanatory, but in summary:</p> <ul> <li>Enable the four checkboxes on the first page of the wizard.</li> <li>Next page, kdc = <code>hostname --fqdn</code> headnode, realm = MYNET (in our example). Leave other defaults.</li> <li>Next page, select Manage <code>krb5.conf</code> through Cloudera Manager. Leave all defaults.</li> <li>Next page, Username <code>root/admin</code> and password you typed in step 7.</li> </ul> <p>The wizard will do it's magic and hopefully succeed without problems.</p> <p><center> <a href="//cdn.cppse.nl/129-wizard.png"><img border="0" src="//cdn.cppse.nl/129-large-thumb-wizard.png" /></a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Tue, 06 Sep 2016 00:00:00 +0000 How to screen capture in Windows 10 with HiDPI support //blog.cppse.nl/screen-capture-windows-10-hidpi-ffmpeg<div class="datestamp">May 7 2016</div> <div class="h1"><a href="//blog.cppse.nl/screen-capture-windows-10-hidpi-ffmpeg"> <h1 id="howtoscreencaptureinwindows10withhidpisupport">How to screen capture in Windows 10 with HiDPI support</h1> </a></div> <p>In case you are looking for a free alternative to Camtasia Studio or many other alternatives... One of my favorite tools of all time, <a href="http://ffmpeg.org">ffmpeg</a> can do it for free!</p> <p>The simplest thing that will work is <code>ffmpeg -f gdigrab -framerate 10 -i desktop output.mkv</code> (<a href="http://stackoverflow.com/questions/6766333/capture-windows-screen-with-ffmpeg">source</a>) This gives pretty good results already (if you use an MKV container, FLV will give worse results for example).</p> <h2 id="hidpi:fixmousepointer">HiDPI: Fix mouse pointer</h2> <p><code>gdigrab</code> adds a mouse pointer to the video but does not scale it according to HiDPI settings, so it will be extremely small. You can configure the mouse pointer to extra large to fix that. That mouse pointer won't scale either, but at least you end up with a regular size pointer in the video <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <p><center><a href="//cdn.cppse.nl/125-mouse-pointer.png"><img border="0" src="//cdn.cppse.nl/125-thumb-mouse-pointer.png" /></a></center></p> <h2 id="optional:useh264codec">Optional: Use H264 codec</h2> <p>More options you can find <a href="https://trac.ffmpeg.org/wiki/Encode/H.264">here</a>, I've settled with single pass encoding using <code>-c:v libx264 -preset ultrafast -crf 22</code>.</p> <pre><code>ffmpeg -f gdigrab -framerate 30 -i desktop ^ -c:v libx264 -preset ultrafast -crf 22 output.mkv</code></pre> <h2 id="optional:includesoundinthevideo">Optional: Include sound in the video</h2> <p>First execute <code>ffmpeg -list_devices true -f dshow -i dummy</code> this will give you directshow devices. (<a href="https://trac.ffmpeg.org/wiki/Capture/Desktop">source</a>) On my laptop this command outputs:</p> <pre><code>[dshow @ 00000000023224a0] DirectShow video devices (some may be both video and audio devices) [dshow @ 00000000023224a0] "USB2.0 HD UVC WebCam" [dshow @ 00000000023224a0] Alternative name "@device_pnp_\\?\usb#vid_04f2&amp;pid_b3fd&amp;mi_00#6&amp;11eacec2&amp;0&amp;0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" [dshow @ 00000000023224a0] "UScreenCapture" [dshow @ 00000000023224a0] Alternative name "@device_sw_{860BB310-5D01-11D0-BD3B-00A0C911CE86}\UScreenCapture" [dshow @ 00000000023224a0] DirectShow audio devices [dshow @ 00000000023224a0] "Microphone (Realtek High Definition Audio)" [dshow @ 00000000023224a0] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{1DDF1986-9476-451F-A6A4-7EBB5FB1D2AB}"</code></pre> <p>Now I know the device name I can use for audio is <code>"Microphone (Realtek High Definition Audio)"</code>. Use it for the following parameters in ffmpeg <code>-f dshow -i audio="Microphone (Realtek High Definition Audio)"</code>.</p> <h2 id="theendresult">The end result</h2> <p>I ended up with <code>capture-video.bat</code> like this:</p> <pre><code>ffmpeg -f dshow -i audio="Microphone (Realtek High Definition Audio)" ^ -f gdigrab -framerate 30 -i desktop ^ -c:v libx264 -preset ultrafast -crf 22 output.mkv</code></pre> <p>This is a resulting video where I used this command, resolution of the video is 3840x2160 and the HiDPI scale is set to 2.5.</p> <p><center> <a href="//blog.cppse.nl/videos/screen-capture.mp4" target="_blank"> <div style="background-image:url(//cdn.cppse.nl/screen-capture-750px.png); width: 750px; height: 421px;">&nbsp;</div> </a> </center></p> <p></a> </center></p> <h2 id="update1addmorekeyframesforbetterediting">Update 1> Add more keyframes for better editing</h2> <p>For this I use the following command, to insert a keyframe every 25 frames (the closer to one, the larger the output file will be):</p> <pre><code>ffmpeg.exe -i %1 -qscale 0 -g 25 %2</code></pre> <p>The option <code>-qscale 0</code> is for preserving the quality of the video.</p> <p>(Changing the container to <code>.mov</code> was probably not necessary, I tried this hoping that Adobe Premiere would support it, but it didn't!)</p> <h2 id="update2editing4konwindows10...">Update 2> Editing 4K on Windows 10...</h2> <p>Found the following tool for editing: <a href="http://filmora.wondershare.com/video-editor/">Filmora</a> and (on my laptop) it was able to smoothly edit the footage. They support GPU acceleration, but the additional keyrames really help with a smooth experience.</p> <p><img border="0" src="//cdn.cppse.nl/125-filmora-banner-logo.png" /></p> <p>Once you get the hang of it (shortcut keys are your friend) it's pretty easy to cut &amp; paste your videos.</p> <h2 id="update3supportadobepremiere">Update 3> Support Adobe Premiere</h2> <p>As I discovered Adobe Premiere earlier, doesn't like MKV, but it also doesn't like 4:4:4 (yuv444p), the pixel format used by default (it seems). You can view such information using <code>ffprobe &lt;VIDEO FILE&gt;</code>. Anyway, it seems to like yuv420p, so add <code>-pix_fmt yuv420p</code> to make it work for Premiere:</p> <pre><code>ffmpeg.exe -i input.mkv -qscale 0 -g 25 -pix_fmt yuv420p output.mov </code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 07 May 2016 00:00:00 +0000 Profiling and visualizing with GNU strace //blog.cppse.nl/profiler-based-on-strace<div class="datestamp">April 2 2016</div> <div class="h1"><a href="//blog.cppse.nl/profiler-based-on-strace"> <h1 id="profilingandvisualizingwithgnustrace">Profiling and visualizing with GNU strace</h1> </a></div> <!-- revised one last time the few days before March 24th 2016 --> <p>A crazy idea, building a profiler/visualizer based on strace output. Just for fun. But, who knows there may even be something useful we can do with this..</p> <p>The following image shows exactly such a visualization for a specific HTTP GET request (<em>f.i.</em>, to <code>http://default-wordpress.cppse.nl/wp-admin/index.php</code> (URL not accessible online)). The analysis from the image is based on the strace log output from the Apache HTTP server thread handling the request. Parameters for the strace call include <code>-f</code> and <code>-F</code> so it includes basically everything the Apache worker thread does for itself. (If it were to start a child process, it would be included.)</p> <p><center><a href="//cdn.cppse.nl/115-Selection_037.png"><img border="0" src="//cdn.cppse.nl/115-large-thumb-Selection_037.png" /></a></center></p> <p>This request took 1700 milliseconds, which seems exceptionally slow, even for a very cheap micro compute instance. It is, I had to cheat a little by restarting Apache and MySQL in advance, to introduce some delays that make the graph more interesting. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /> It's still still normal though that <code>strace</code> will slow down the program execution speed.</p> <p>I grouped all strace lines by process ID and their activity on a specific FD (file descriptor). Pairs like open()/close() or socket()/close() introduce a specific FD and in between are likely functions operating on that FD (like read()/write()). I group these related strace lines together and called them "stream"s in the above image.</p> <p>In the image you can see that the longest and slowest "stream" is 1241 milliseconds, this one is used for querying MySQL and probably intentionally closed last to allow re-use of the DB connection during processing of the request. The three streams lower in the visualization follow each other sequentially and appear to be performing a lookup in <code>/etc/hosts</code>, follewed by two DNS lookups directed to <code>8.8.4.4</code>.</p> <h2 id="whyarewedoingthisotherthanbecauseitsawesome">Why are we doing this? (Other than because it's Awesome!)</h2> <p>This works for any strace output, but my idea originated while doing web development. This was for a relatively complicated web application, that was divided in many sub-systems that communicate mostly via REST calls with each other. All these systems had lots of external calls to other systems, and I wanted a view where I could see regardless of which sub-system or actual PHP code being executed, how the performance was for specifically: I/O with (i.e. for i18n/locale) files, scripts, SQL queries to MySQL, Oracle, the REST API calls to system X, Y &amp; Z, Redis, Memcached, Solr, Shared memory even and Disk caching.</p> <p>If only there was a tool really good at capturing that kind of I/O... ahh yeah there is, <code>strace</code>! I switched jobs 7 months ago, before applying my strace tool to this code-base, but I've applied it to similar complex applications with success.</p> <p>We already had tools for (more traditional) profiling of PHP requests. Quite often the interpretation was difficult, probably because of a lot of nasty runtime reflection being used. Also when you needed to follow a slow function (doing a REST call) it was a lot of effort to move profiling efforts to the other system (because of OAuth 1.0b(omg..), expired tokens, ..). Nothing unsolveable of course, but with strace you can just trace everything at once on a development environment (especially in Vagrant which we used), spanning multiple vhosts. If it's just you on the VM, perhaps you can strace the main Apache PID recursively, I didn't try that however, but I think that would work.</p> <p>Products like NewRelic provide dashboards for requests where you can gain such deep insights, "off the shelve", basically, but the downside is that it's not cheap. NewRelic <em>f.i.</em> hooks into Apache &amp; PHP and has access to actual PHP function calls, SQL queries, <em>etc.</em> <code>strace</code> cant do that, because it only sits between the process(es) and the Linux kernel.</p> <h2 id="firstletstakeonestepbackproperlyparsethestraceoutput..">First, let's take one step back &amp; properly parse the strace output..</h2> <div style="margin-left: 15px; float: right;"><a href="//cdn.cppse.nl/115-bnfc-logo.png"><img border="0" src="//cdn.cppse.nl/115-thumb-bnfc-logo.png" /></a></div> <p>It quickly became apparent that I couldn't get away with some trivial regex for parsing it, so I turned to <a href="http://bnfc.digitalgrammars.com/">bnfc</a> and created the following BNF grammer to generate the parser. I was quite suprised that this was so easy that it took me less than a working day to find a tool for the job, learn it and get the grammer right for some strace output.</p> <p>With this tool you are provided with an autogenerated base class "Skeleton" which you can extend to create your own Visitor implementation. With this pattern it becomes quite easy to extract some meta-data you are interested in. I will show a simply example.</p> <h3 id="thegrammer">The grammer</h3> <p>I came up with the following grammer that bnfc uses to generate the Parser. Reading it from top to bottom is more or less the way you can incrementally construct this kind of stuff. You start really small; first chunking multiple strace-lines into single strace-lines, then chunk strace-lines into Pid, Timestamp and (remaining) Line. Then further specify a Pid, the Timestamp, Line, <em>etc.</em>, slowly making the grammer more coarse-grained.</p> <pre><code>EStraceLines. StraceLines ::= [StraceLine]; EStraceLine. StraceLine ::= [Pid] [Timestamp] Line; EPidStdOut. Pid ::= "[pid " PidNumber "] "; EPidOutput. Pid ::= PidNumber [Whitespace] ; EPidNumber. PidNumber ::= Integer; ETimestamp. Timestamp ::= EpochElapsedTime; ELine. Line ::= Function "(" Params ")" [Whitespace] "=" [Whitespace] ReturnValue [TrailingData]; ELineUnfinished. Line ::= Function "(" Params "&lt;unfinished ...&gt;"; ELineContinued. Line ::= "&lt;... " Function " resumed&gt; )" [Whitespace] "=" [Whitespace] ReturnValue [TrailingData]; ELineExited. Line ::= "+++ exited with" [Whitespace] Integer [Whitespace] "+++" ; EFunction. Function ::= Ident ; EFunctionPrivate. Function ::= "_" Ident ; EParams. Params ::= [Param]; EParamArray. Param ::= "[" [Param] "]" ; EParamObject. Param ::= "{" [Param] "}" ; EParamComment. Param ::= "/* " [CommentString] " */"; EParamInteger. Param ::= Number ; EParamFlags. Param ::= [Flag] ; EParamIdent. Param ::= Ident ; EParamString. Param ::= String ; EParamWhitespace. Param ::= Whitespace ; EParamAddress. Param ::= Address ; EParamDateTime. Param ::= DateYear "/" DateMonth "/" DateDay "-" TimeHour ":" TimeMinute ":" TimeSecond ; EParamKeyValue. Param ::= Param "=" Param ; EParamKeyValueCont. Param ::= "..."; EParamExpression. Param ::= Integer Operator Integer; EParamFunction. Param ::= Function "(" [Param] ")" ; EDateYear. DateYear ::= Integer ; EDateMonth. DateMonth ::= Integer ; EDateDay. DateDay ::= Integer ; ETimeHour. TimeHour ::= Integer ; ETimeMinute. TimeMinute ::= Integer ; ETimeSecond. TimeSecond ::= Integer ; EOperatorMul. Operator ::= "*"; EOperatorAdd. Operator ::= "+"; EEpochElapsedTime. EpochElapsedTime ::= Seconds "." Microseconds ; ESeconds. Seconds ::= Integer ; EMicroseconds. Microseconds ::= Integer ; ECSString. CommentString ::= String ; ECSIdent. CommentString ::= Ident ; ECSInteger. CommentString ::= Integer ; ENegativeNumber. Number ::= "-" Integer; EPositiveNumber. Number ::= Integer; EFlag. Flag ::= Ident; EFlagUmask. Flag ::= Integer; ERetvalAddress. ReturnValue ::= Address ; ERetvalNumber. ReturnValue ::= Number ; ERetvalUnknown. ReturnValue ::= "?"; EAddress. Address ::= HexChar; ETrailingDataConst. TrailingData ::= " " [Param] " (" [CommentString] ")"; ETrailingDataParams. TrailingData ::= " (" [Param] ")" ; ESpace. Whitespace ::= " "; ESpace4x. Whitespace ::= " "; ETab. Whitespace ::= " "; terminator CommentString "" ; terminator Param "" ; terminator Pid " " ; terminator Timestamp " " ; terminator TrailingData "" ; terminator Whitespace "" ; separator CommentString " " ; separator Flag "|" ; separator Param ", " ; separator Pid " " ; separator StraceLine ""; token HexChar ('0' 'x' (digit | letter)*);</code></pre> <p>Given the above grammer bnfc can parse this strace line <code>15757 1429444463.750111 poll([{fd=3, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 1 ([{fd=3, revents=POLLIN|POLLRDNORM}])</code> into an Abstract Syntax Tree.</p> <pre><code>[Abstract Syntax] (EStraceLines [ (EStraceLine [(EPidOutput [(EPidNumber 15757)])] [(ETimestamp [(EEpochElapsedTime [(ESeconds 1429444463)] [(EMicroseconds 750111)])])] [(ELine [(EFunction "poll")] [(EParams [ (EParamArray [ (EParamObject [ (EParamKeyValue (EParamIdent "fd") (EParamInteger [(EPositiveNumber 3)])), (EParamKeyValue (EParamIdent "events") (EParamFlags [ (EFlag "POLLIN"), (EFlag "POLLPRI"), (EFlag "POLLRDNORM"), (EFlag "POLLRDBAND")]))])]), (EParamInteger [(EPositiveNumber 1)]), (EParamInteger [(EPositiveNumber 0)])])] ESpace ESpace [(ERetvalNumber [(EPositiveNumber 1)])] [(ETrailingDataParams [(EParamArray [(EParamObject [ (EParamKeyValue (EParamIdent "fd") (EParamInteger [(EPositiveNumber 3)])), (EParamKeyValue (EParamIdent "revents") (EParamFlags [ (EFlag "POLLIN"), (EFlag "POLLRDNORM")]))])])]) ] ) ] ) ])</code></pre> <p>No matter how nested these lines get, it will parse them as long as I didn't forget anything in the grammer. (So far it seems to be complete to parse everything.)</p> <h3 id="visitorexample">Visitor example</h3> <p>Using the BNF grammer, the above structure and occasional peeking at the generated Skeleton base class, you can simply override methods in your own visitor to do something "useful". The following visitor is a <em>less</em> "useful" but simple example that outputs all the strings captured for strace lines containing the open() function. Just to illustrate how you use this Visitor.</p> <pre class="prettyprint lang-cpp"><code data-language="c">class OutputOpenVisitor : public Skeleton { string timestamp; string function; string strings; public: void visitEStraceLine(EStraceLine* p) { timestamp = &quot;&quot;; function = &quot;&quot;; strings = &quot;&quot;; Skeleton::visitEStraceLine(p); if (function == &quot;open&quot;) { cout &lt;&lt; timestamp &lt;&lt; &quot; &quot; &lt;&lt; function &lt;&lt; &quot; &quot; &lt;&lt; strings &lt;&lt; endl; } } void visitEFunction(EFunction* p) { function = p-&gt;ident_; Skeleton::visitEFunction(p); } void visitEEpochElapsedTime(EEpochElapsedTime *p) { auto secs = static_cast&lt;ESeconds *&gt;(p-&gt;seconds_); auto microsecs = static_cast&lt;EMicroseconds *&gt;(p-&gt;microseconds_); timestamp = to_elasticsearch_timestamp(secs, microsecs); Skeleton::visitEEpochElapsedTime(p); } void visitString(String x) { strings.append(x); Skeleton::visitString(x); } }; </code></pre> <p>You can find this example in the examples forder in the <a href="https://bitbucket.org/rayburgemeestre/strace-output-parser/src/9cc6c5d17f688018881bf923044ce2daefe868a5/examples/outputopenvisitor.cpp?at=master&amp;fileviewer=file-view-default">git repository here</a>.</p> <p>After compiling this example into <code>strace-output-visualizer</code>:</p> <pre><code># capture a strace log trigen@firefly:/projects/strace-output-parser[master]&gt; strace -f -F -ttt -s 512 -o test.log uptime 17:53:02 up 32 days, 22:44, 23 users, load average: 2.39, 2.20, 2.12 # strace log contains stuff like trigen@firefly:/projects/strace-output-parser[master]&gt; head -n 10 test.log 19151 1458147182.196711 execve("/usr/bin/uptime", ["uptime"], [/* 47 vars */]) = 0 19151 1458147182.197415 brk(0) = 0x7c1000 19151 1458147182.197484 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) 19151 1458147182.197555 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f45cd85e000 19151 1458147182.197618 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) 19151 1458147182.197679 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 19151 1458147182.197740 fstat(3, {st_mode=S_IFREG|0644, st_size=156161, ...}) = 0 19151 1458147182.197813 mmap(NULL, 156161, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f45cd830000 19151 1458147182.197888 close(3) = 0 19151 1458147182.197969 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) # pipe the log through the example program trigen@firefly:/projects/strace-output-parser[master]&gt; cat test.log | ./strace-output-parser 2016-03-16T16:53:02.198Z open /etc/ld.so.cache 2016-03-16T16:53:02.198Z open /lib/x86_64-linux-gnu/libprocps.so.3 2016-03-16T16:53:02.199Z open /lib/x86_64-linux-gnu/libc.so.6 2016-03-16T16:53:02.200Z open /sys/devices/system/cpu/online 2016-03-16T16:53:02.200Z open /usr/lib/locale/locale-archive 2016-03-16T16:53:02.200Z open /etc/localtime 2016-03-16T16:53:02.201Z open /proc/uptime 2016-03-16T16:53:02.202Z open /var/run/utmp 2016-03-16T16:53:02.273Z open /proc/loadavg</code></pre> <p>Opposed to a simple Visitor like this example, I parse all the lines, prepare a JSON representation for each line and store that in ElasticSearch. This way selecting and filtering can be done afterwards. And also ElasticSearch is really a fast solution in case you want to do more complex queries on your log.</p> <h2 id="aproofofconceptforweb">A Proof of concept for Web</h2> <p>This time at the beginning of each request I have PHP instruct some script to run a strace on the process id for the current PHP script's pid (or rather the Apache worker's) and all it's (virtual) threads and sub processes. (If I would track the Request accross the stack with "Cross application tracing" you can even combine all the relevant straces for a given request. I didn't implement this (again) because of I switched jobs. (<a href="https://docs.newrelic.com/docs/apm/transactions/cross-application-traces/cross-application-tracing">Info on Cross application tracing in newrelic</a>). This is even relatively easy to implement if you have a codebase where you can just make the change (like inject a unique id for the current request in curl call for example).)</p> <p>The following image and code shows how I capture straces from specific PHP requests, like the wordpress example I started this blog with. You can skip this part. Eventually these straces are linked to a specific request, ran through a slightly more elaborate Visitor class and fed into ElasticSearch for later processing.</p> <p><center><a href="//cdn.cppse.nl/115-cdraw.png"><img border="0" src="//cdn.cppse.nl/115-large-thumb-cdraw.png" /></a></center></p> <p>(This omits also some other details w/respect to generating a UUID for each request, and keeping track of what strace outputs are related to each request.)</p> <p>Inject in your application 'header', i.e., top <code>index.php</code>:</p> <pre><code>register_shutdown_function(function () { touch("/tmp/strace-visualizer-test/done/" . getmypid()); }); $file = "/tmp/strace-visualizer-test/todo/" . getmypid(); touch($file); while (file_exists($file)) { sleep(1); } // continue with the request when removed from todo folder</code></pre> <p>A separate long running process runs the following:</p> <pre><code>trigen@CppSe:~/strace-visualizer-test&gt; cat run.ksh #!/bin/ksh93 mkdir -p /tmp/strace-visualizer-test/todo mkdir -p /tmp/strace-visualizer-test/done while true; do find /tmp/strace-visualizer-test/todo/ -type f | \ xargs -I{} -n 1 sh -c "strace -f -F -ttt -s 4096 -o \$(basename {}).strace -p \$(basename {}) &amp; rm -rf {};" find /tmp/strace-visualizer-test/done/ -type f | \ xargs -I{} -n 1 sh -c "(ps axufw | grep [s]trace.*\$(basename {}) | grep -v grep | awk -F ' ' '{print \$2}' | xargs -n 1 kill -1 ) &amp; (sleep 1; rm -rf {};)" printf "."; done</code></pre> <p>This way you end up with <code>.strace</code> files per process ID (it should probably include a timestamp too). The long running process removes the file the client checks from the todo folder as soon as it started strace. That way the client will no longer block and the interesting stuff will be captured. It uses a shutdown handler to instruct the long running process to stop the capture (the Apache thread won't exit, it will wait for a next request).</p> <h2 id="finalsteptoelasticsearch">Final step, To ElasticSearch!</h2> <p>I use a Visitor and my strace parser to create JSON representations for the strace log lines. Containing the meta-data I need: file descriptors, an array with all strings, a timestamp that ElasticSearch can understand out of the box, <em>etc.</em></p> <p>To get to my previous example, I can use <code>cat test.log | ./strace-output-parser elasticsearch localhost 9200 strace_index</code> to import the parsed lines to ElasticSearch.</p> <p><center> <a href="//cdn.cppse.nl/115-elasticsearch-head.png"><img border="0" src="//cdn.cppse.nl/115-large-thumb-elasticsearch-head.png" /></a> </center></p> <p>In above example I use filtering with a plugin called <a href="https://mobz.github.io/elasticsearch-head/">"head"</a> to basically make the same selection as I did with the <a href="#visitorexample">simple visitor example</a>. I also highlighted one specific line to show the JSON representation.</p> <p>I used PHP for processing the wordpress strace output from ElasticSearch and generated the visualization from the very first image in this blog post. You can <a href="//cdn.cppse.nl/115_strace.html">view the HTML output here</a>.</p> <p>Hopefully this blog post was interesting to read, and maybe you find some use for the strace parser yourself. If you do, please let me know, that would be fun to know <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 02 Apr 2016 00:00:00 +0000 Remote debugging with NetBeans 8.1 for C/C++ //blog.cppse.nl/remote-debugging-cmdaemon<div class="datestamp">December 17 2015</div> <div class="h1"><a href="//blog.cppse.nl/remote-debugging-cmdaemon"> <h1 id="remotedebuggingwithnetbeans8.1forcc">Remote debugging with NetBeans 8.1 for C/C++</h1> </a></div> <p>Most people are probably familiar with gdb, and Ribamar pointed out to me there is also a ncurses frontend inside gdb. But in case anyone is interested I learned that NetBeans also supports remote debugging. Even though it's not the most modern IDE in the world, and it's vi emulation is cumbersome <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sP" />, it seems to have pretty good support for remote-debugging. It will just login to some machine via ssh (<em>i.e.</em>, dev11 or a real cluster), and issue <code>gdb &lt;something&gt;</code> and wrap around it. If you make sure it knows where the sources files are on your development machine, you can use all the step-debugging features.</p> <p>The only downside is that loading up <code>cmd</code> in gdb takes a while probably ~ 30 seconds. Still it's a lot faster than debugging with print-statements and recompiling. For <code>cmsh</code> it's already a lot faster and on top of that you can issue a command multiple times via the REPL, so you can step debug it multiple times within the same gdb session. (Beware though that you probably need to <em>connect</em> again as your connection may be lost)</p> <h2 id="exampleworkflow">Example workflow</h2> <p>To show off how it works first with CMDaemon. My workflow is to create a unit-test that fails, set a breakpoint in the unit-test and start the debug.</p> <p><center> <a href="//cdn.cppse.nl/119-set_breakpoint.png"><img border="0" src="//cdn.cppse.nl/119-thumb-set_breakpoint.png" /></a> - <a href="//cdn.cppse.nl/119-debugger_will_stop.png"><img border="0" src="//cdn.cppse.nl/119-thumb-debugger_will_stop.png" /></a> <br/> <i>break point set followed by the debugger stopping execution at that point.</i> </center></p> <p><center> <a href="//cdn.cppse.nl/119-step_into_function.png"><img border="0" src="//cdn.cppse.nl/119-thumb-step_into_function.png" /></a> - <a href="//cdn.cppse.nl/119-stepped_into.png"><img border="0" src="//cdn.cppse.nl/119-thumb-stepped_into.png" /></a> <br/> <i>step-into example, select the function to step into -and click the button highlighted with -.</i> </center></p> <p>There is also the F7 key to "step into", but be prepared to step into assembly a lot of times (use CTRL+F7 to step out, and try again). You will jump into the -> operator, shared pointer dereferences, std::string constructors, before getting into the function you want. (Also note that the first time you step into assembly it will be very slow, but it will get faster the next few times).</p> <h2 id="wizardexampletodebugcmdunittest">Wizard example to debug cmd unit test</h2> <p><center> <a href="//cdn.cppse.nl/119-download.png"><img border="0" src="//cdn.cppse.nl/119-thumb-download.png" /></a> <br> <i>Download from <a href="https://netbeans.org/downloads/">https://netbeans.org/downloads/</a> <br/> <code>chmod +x netbeans-8.1-cpp-linux-x64.sh</code><br/> <code>./netbeans-8.1-cpp-linux-x64.sh</code></i> <br/> <br/> <a href="//cdn.cppse.nl/119-new_project.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_project2.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project2.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_project3.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project3.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_project4.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project4.png" /></a> <br/> <i>Note that you want to set some bogus command like <code>whoami</code>. <br/> Netbeans will try to be smart and clean your project directory for you<br/> (and rebuild without using multiple cores, ..)</i><br><br></p> <p><a href="//cdn.cppse.nl/119-new_project5.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project5.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_project6.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project6.png" /></a> <BR> <i>Note the working directory should be including <code>src</code>.<br/> This is to help <code>gdb</code> later with finding source code.</i> <BR><BR></p> <p><a href="//cdn.cppse.nl/119-new_project7.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project7.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_project8.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_project8.png" /></a></p> <hr> <p><a href="//cdn.cppse.nl/119-new_remote_host.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_remote_host.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_remote_host2.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_remote_host2.png" /></a> <a href="//cdn.cppse.nl/119-new_remote_host3.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_remote_host3.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-new_remote_host4.png"><img border="0" src="//cdn.cppse.nl/119-thumb-new_remote_host4.png" /></a></p> <p><a href="//cdn.cppse.nl/119-set_build_host.png"><img border="0" src="//cdn.cppse.nl/119-thumb-set_build_host.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-set_build_host2.png"><img border="0" src="//cdn.cppse.nl/119-thumb-set_build_host2.png" /></a></p> <p><a href="//cdn.cppse.nl/119-fix_build_result0.png"><img border="0" src="//cdn.cppse.nl/119-thumb-fix_build_result0.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-fix_build_result.png"><img border="0" src="//cdn.cppse.nl/119-thumb-fix_build_result.png" /></a> <br> <i>There is one fix needed that the Wizard didn't set properly for us. <br> Go to project properties, Build / Make, and set Build Result to the executable.<br> The remote debugger will use this value for issuing with gdb, and it's somehow empty by default.</i><BR><BR></p> <hr> <p><a href="//cdn.cppse.nl/119-go_to_file.png"><img border="0" src="//cdn.cppse.nl/119-thumb-go_to_file.png" /></a> &nbsp; <a href="//cdn.cppse.nl/119-debug0.png"><img border="0" src="//cdn.cppse.nl/119-thumb-debug0.png" /></a><BR> <i>Use <code>ALT+SHIFT+o</code> to Jump to the file containing the test.<br/> Set a breakpoint there using <code>CTRL+F8</code></i><BR><BR></p> <p><a href="//cdn.cppse.nl/119-run_command.png"><img border="0" src="//cdn.cppse.nl/119-thumb-run_command.png" /></a> <br> <i>The final thing we want to pass to gdb is the parameters for running our specific unittest. <br> In my example <code>"${OUTPUT_PATH}" --unittests --gtest_filter=LineParserTest.empty</code>.</i><BR><BR></p> <p><br><img border="0" src="//cdn.cppse.nl/119-debug.png" /><br> <br> <a href="//cdn.cppse.nl/119-debug2.png"><img border="0" src="//cdn.cppse.nl/119-thumb-debug2.png" /></a> <a href="//cdn.cppse.nl/119-debug3.png"><img border="0" src="//cdn.cppse.nl/119-thumb-debug3.png" /></a> </center></p> <h2 id="youcanusethesesettingstodoublecheckifeverythingiscorrect">You can use these settings to double check if everything is correct</h2> <p><center> <a href="//cdn.cppse.nl/119-settings0.png"><img border="0" src="//cdn.cppse.nl/119-thumb-settings0.png" /></a> <a href="//cdn.cppse.nl/119-settings1.png"><img border="0" src="//cdn.cppse.nl/119-thumb-settings1.png" /></a> </center></p> <p><center> <a href="//cdn.cppse.nl/119-settings2.png"><img border="0" src="//cdn.cppse.nl/119-thumb-settings2.png" /></a> <a href="//cdn.cppse.nl/119-settings3.png"><img border="0" src="//cdn.cppse.nl/119-thumb-settings3.png" /></a> </center></p> <p><center> <a href="//cdn.cppse.nl/119-settings4.png"><img border="0" src="//cdn.cppse.nl/119-thumb-settings4.png" /></a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 17 Dec 2015 00:00:00 +0000 How to "inspect element" XUL applications //blog.cppse.nl/inspecting-mozilla-xul-applications<div class="datestamp">December 1 2015</div> <div class="h1"><a href="//blog.cppse.nl/inspecting-mozilla-xul-applications"> <h1 id="howtoinspectelementxulapplications">How to "inspect element" XUL applications</h1> </a></div> <p>In addition to my previous blog post <a href="/debugging-mozilla-xul-applications">How to debug XUL applications</a>.</p> <p>Last friday I learned that you can use the DOM inspector on XUL applications as well. This is quite useful if you want to see what events are hidden behind a button, try out layout changes, etc., etc. It is also quite fast, I don't notice any performance difference.</p> <p>These instructions are taken from <a href="http://stackoverflow.com/questions/4648365/how-to-inspect-my-standalone-xul-app-using-dom-inspector-or-similar">a very useful stackoverflow answer</a>. Summarizing:</p> <ul> <li>Add <code>[XRE] EnableExtensionManager=1</code> to your <code>application.ini</code> if it isn't already.</li> <li>If you are using the <code>xulrunner</code> app you already have the Error Console available (for info see my <a href="/debugging-mozilla-xul-applications">previous blog post</a> for this). Type in it the following: <code>window.openDialog("chrome://mozapps/content/extensions/extensions.xul", "", "chrome,dialog=no,resizable=yes");</code>.</li> <li>You will be presented the Add-ons Manager, in there choose "Install Add-on From File..." and download the "DOM Inspector". (I have a local copy here: <a href="//cdn.cppse.nl/addon-6622-latest.xpi">addon-6622-latest.xpi</a> (downloaded from: <a href="https://addons.mozilla.org/en-US/firefox/addon/dom-inspector-6622/">here</a>)).<br/><center><a href="//cdn.cppse.nl/118-Selection_058.png"><img border="0" src="//cdn.cppse.nl/118-thumb-Selection_058.png" /></a></center></li> <li>You need to restart and start xulrunner with an additional <code>-inspector</code> flag.</li> </ul> <p>One tip with the DOM inspector, if you use "File >> Inspect Chrome Document" and the list is huge, highlight an item with your mouse and press the <code>End</code> key on your keyboard. You likely need one at the bottom of the list because those are the XUL files loaded most recently.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Tue, 01 Dec 2015 00:00:00 +0000 How to debug XUL applications //blog.cppse.nl/debugging-mozilla-xul-applications<div class="datestamp">November 25 2015</div> <div class="h1"><a href="//blog.cppse.nl/debugging-mozilla-xul-applications"> <h1 id="howtodebugxulapplications">How to debug XUL applications</h1> </a></div> <p>You can use Mozilla Firefox (Javascript) debugging on your XUL application using the Remote Debugging facility. This blog post could be useful as a HOWTO, because I was lucky enough to attempt this 3rd of July 2015. You see had I tried this today I would have failed, because stuff seems broken in newer versions of xulrunner (and Firefox). This is true for the project I work on at least. The very fact that I struggled with setting this up today was my motivation to dig into why it wasn't working and made me think this might be useful to others.</p> <p>I know everything in this blog post to work for both CentOS 6.6 and Ubuntu 15.04. These steps (except for the xulrunner download) should be platform independent.</p> <p><style style="text/css">#connectpng img { width: 245px; height: 213px; }</style><div id="connectpng" style="display:inline;float:right;margin:10px;"><a href="//cdn.cppse.nl/117-Connect.png"><img border="0" src="//cdn.cppse.nl/117-thumb-Connect.png" /></a></div></p> <h2 id="firstgetaslightlyolderxulrunner">First get a slightly older xulrunner</h2> <p>You need a reasonably new <code>xulrunner</code> in order for Remote Debugging to work. I downloaded xulrunner version 38 at the time from <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/XULRunner">The Mozilla Project Page</a> (xulrunner-38.0.5.en-US.linux-x86_64.tar should be on <a href="http://ftp.mozilla.org/pub/xulrunner/releases/38.0b5/runtimes/">their FTP</a> somewhere, but you can also use <a href="//cdn.cppse.nl/xulrunner-38.0b5.en-US.linux-x86_64.tar.bz2">this local copy hosted with this blog</a>). I think we should cherish that version, because that one works. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <p>The newest and version is version 41, but also the last because they started integrating it in Mozilla Firefox since then. I tried version 41, and grabbing a recent <strike>Thunderbird</strike> Firefox, but all steps work, except when you arrive in the "Connect Dialog", the clickable <code>Main Process</code> hyperlink (as shown in the image) is simply not there for you to click on.</p> <h2 id="enableadebuglistenerinthecode">Enable a debug listener in the code</h2> <p>In your application you need to start the debug listener. Probably in the top of your <code>main.js</code> include the following lines.</p> <pre><code>Components.utils.import('resource://gre/modules/devtools/dbg-server.jsm'); if (!DebuggerServer.initialized) { DebuggerServer.init(); // Don't specify a window type parameter below if "navigator:browser" // is suitable for your app. DebuggerServer.addBrowserActors("myXULRunnerAppWindowType"); } var listener = DebuggerServer.createListener(); listener.portOrPath = '6000'; listener.open();</code></pre> <p>Also enable in the preferences (probably <code>defaults/preferences/prefs.js</code>).</p> <pre><code>pref("devtools.debugger.remote-enabled", true);</code></pre> <p>If you forget to change this last preference you will get the following error.</p> <pre><code>JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -&gt; resource://gre/modules/devtools/server/main.js, line 584: Error: Can't create listener, remote debugging disabled</code></pre> <h2 id="starttheapplicationwiththisxulrunner">Start the application with this xulrunner</h2> <p>Extract the xulrunner runtime to somewhere, <em>i.e.</em> <code>/projects/xulrunner</code>, and issue from the your program's directory like this:</p> <pre><code>shell$&gt; /projects/xulrunner/xulrunner application.ini</code></pre> <h2 id="attachdebuggerfrommozillafirefox">Attach debugger from Mozilla Firefox</h2> <p>Open a fairly recent Firefox browser and open the remote debugger which is available via "Tools -> Web Developer -> Connect...".</p> <p><center> <a href="//cdn.cppse.nl/117-Connect_remote_debugger_from_firefox.jpg"><img border="0" src="//cdn.cppse.nl/117-large-thumb-Connect_remote_debugger_from_firefox.jpg" /></a> </center></p> <p>If the above "Connect.." option is not available, you have to enable the same preference inside Firefox in the "about:config" page. Search for <code>remote-enabled</code>.</p> <p><center> <a href="//cdn.cppse.nl/117-About_config.png"><img border="0" src="//cdn.cppse.nl/117-large-thumb-About_config.png" /></a> </center></p> <p>Then connect to <code>localhost</code> port <code>6000</code>.</p> <p>Your program will present you a dialog to accept the incoming connection from the debugger.</p> <p><center> <a href="//cdn.cppse.nl/117-Remote_debugger_accept_dialog.jpg"><img border="0" src="//cdn.cppse.nl/117-large-thumb-Remote_debugger_accept_dialog.jpg" /></a> </center></p> <p>After accepting you can click to attach to "Main Process" (your program).</p> <p>You should be presented with a debugger that will automatically break when it encounters the <code>debugger</code> keyword. You can also set breakpoints inside.</p> <p>This can look similar to the following image where a call stack is shown, and you have your usual ways to inspect variables and perform step debugging with <code>F10</code>, <code>F11</code>, <code>Shift</code>+<code>F11</code> <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <p><center> <a href="//cdn.cppse.nl/117-Cmgui_debugger_in_action.jpg"><img border="0" src="//cdn.cppse.nl/117-large-thumb-Cmgui_debugger_in_action.jpg" /></a> </center></p> <p>I am convinced it should also be possible to make it so that the javascript in can handle inspection from the debuggers console. In order to get a REPL working there (for inspecting variables), but I didn't find out how this can be achieved. Using the Watch (and Auto) expressions you can already inspect everything.</p> <p>Just beware that once you attach to the process your program can freeze up for a while as the debugger is loading all the javascript files. </p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 25 Nov 2015 00:00:00 +0000 Circlix Clock //blog.cppse.nl/circlix-android-wear-watch-face<div class="datestamp">September 13 2015</div> <div class="h1"><a href="//blog.cppse.nl/circlix-android-wear-watch-face"> <h1 id="circlixclock">Circlix Clock</h1> </a></div> <p>Today I published my first Android (Wear) App! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />. The idea behind this clock is that it uses concentric circles to show time, and doesn't use analog clock hands or numeric time notation. This is something I have on a bigger LCD screen at home for a while now, and now that there is Android Wear for a while, I wanted to implement this for Android.</p> <h2 id="someexamplevisualizations">Some example visualizations</h2> <p><center class="x"> <img border="0" src="//cdn.cppse.nl/110-4times.gif" /> </center></p> <p><style> .x img { width: 100%; } </style></p> <p>There is more theory behind the visualization, more on that on the website: <a href="http://circlix.click">http://circlix.click</a>.</p> <h2 id="androidwatchface">Android Watch Face</h2> <p><center> <iframe width="560" height="315" src="https://www.youtube.com/embed/4chevD7NNEA" frameborder="0" allowfullscreen></iframe> <br/> <a href="https://play.google.com/store/apps/details?id=nl.cppse.circlix"><img src="//circlix.click/en_generic_rgb_wo_60.png"/></a> </center></p> <h2 id="webglfromthewebsite">WebGL from the Website</h2> <p>You need to have WebGL support in your browser in order to see the following live-clock.</p> <p><center></p> <iframe frameborder="0" border="0" src="//circlix.click/gclock.php#3" width="560" height="315"></iframe> <p></center></p> <h2 id="somecommentsonandroidweardevelopment">Some comments on Android Wear development</h2> <p>Android Wear is relatively new, and I never read any book on the Android Framework. Luckily I had some Java experience. Overall I am impressed by the design of the Framework, although it also confused the hell out of me on various occasions @:|@.</p> <p>Some stuff I needed to realize or discover during development:</p> <ul> <li>(Very basic:) an Activity only runs when it's the current activity.</li> <li>If you need stuff running for longer than an Activity, you need Services.</li> <li>In Java you don't have RAII like in C++/PHP. If you have handlers for threads <em>etc.</em> you should stop them in some <code>onDestroy()</code> method.</li> <li>Packaging, creating the APK for use in f.i. the Play Store was counter intuitive, at least for me. Follow the <a href="https://developer.android.com/samples/WatchFace/index.html">example project provided by Google</a> closely in your project w/respect to Gradle files. I had a perfectly good working APK that came out of Android Studio, it worked whenever I sent it to others, but it was not accepted by the Play store.</li> <li>There is already OpenGL support for Watch Faces. You need to extend <code>Gles2WatchFaceService</code>.</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 13 Sep 2015 00:00:00 +0000 Improve performance Jetbrains IDE when working on remote machine //blog.cppse.nl/improve-performance-jetbrains-ide-remote-machine<div class="datestamp">September 2 2015</div> <div class="h1"><a href="//blog.cppse.nl/improve-performance-jetbrains-ide-remote-machine"> <h1 id="improveperformancejetbrainsidewhenworkingonremotemachine">Improve performance Jetbrains IDE when working on remote machine</h1> </a></div> <p>I use CLion in this blog post, but it should be the same for any of the other editors. (PyCharm, PhpStorm, Intellij, <em>etc.</em>).</p> <p>It took me a while to get a setup that works reasonably well for me at work, for what I expect not a very uncommon setup. That's why I'm sharing this in a blog post.</p> <p>The project I'm working on is quite big, 10yr under development; large codebase and a complex build process. The debug build results in a 1.2 GiB executable, all intermediate files generated by the compiler/linker are many, and big. During build a lot of files are removed/(re)created/generated, so in general a lot of I/O happens.</p> <p>Our build machines are extremely powerful, so it doesn't make sense to work on a local machine because of the build times. That's why compiling happens on remote machines. I have worked remotely at a lot of companies, and usually I would simply use vim + a lot of plugins. However, nowadays I'm accustomed to the power IDE's can provide, primarily navigation-wise (jumping to classes, files, finding usages, <em>etc.</em>) and simply don't want to work without a proper IDE.</p> <h2 id="thisismysetup">This is my setup</h2> <p><center><a href="//cdn.cppse.nl/113-3.png"><img border="0" src="//cdn.cppse.nl/113-large-thumb-3.png" /></a></center></p> <p>I use an <code>NFS</code> mount (<code>sshfs</code> would suffice as well) where I mount from the remote to local, not the other way around, or compiling will be extremely slow. In my opinion using file synchronization in these kinds of setups is too error prone and difficult to get right.</p> <p>As a side-note; I've seen synchronization work moderately okay within a PHP project. But so far not in a C++ project where intermediate/build-files/libraries are first of all <em>large</em> and scattered throughout the project folder.</p> <p>In my <a href="/jetbrains-fsnotifier-over-remote-connection">previous blog post</a> we fixed fsnotifier such as in the previous image, but this also causes a new problem. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sA" /></p> <h2 id="lotsofioisslowovernetworkmount">Lot's of I/O is slow over network mount</h2> <p><center><a href="//cdn.cppse.nl/113-4.png"><img border="0" src="//cdn.cppse.nl/113-large-thumb-4.png" /></a></center></p> <p>During compiling I noticed my IDE would hang, the only cause could be that it's somehow flooded by the enourmous lines of input it now receives from fsnotifier. Perhaps when we're working with the project files on a local disk the IDE wouldn't hang, because simple I/O (even just checking file stats) doesn't have network overhead.</p> <h2 id="solutionignoreasmuchirrelevantioaspossible">Solution, ignore as much (irrelevant) I/O as possible</h2> <p><center><a href="//cdn.cppse.nl/113-5.png"><img border="0" src="//cdn.cppse.nl/113-large-thumb-5.png" /></a></center></p> <p>Here I made the fsnotifier script--that was at first just a simple proxy (calling the real fsnotifier via ssh)--more intelligent. It now filters out intermediate files generated by the compiler (.o, .d, and some other patterns). <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <pre><code>function custom_filter { typeset -n return_val=$1 typeset cmd=$2 # i.e., DELETE/CREATE/CHANGE/... typeset file=$3 # i.e., /full/path/to/file # Ignore some files that are not interesting to my IDE if [[ $file =~ (cmd|mm)\.log$ ]] || \ [[ $file =~ deps.*\.d$ ]] || \ [[ $file =~ \.o$ ]] || \ [[ $file =~ \.o\. ]] || \ [[ $file =~ \.html$ ]] || \ [[ $file =~ core\.*([0-9])$ ]]; then return_val=false return fi return_val=true return }</code></pre> <p>Download all source code from GitHub: <a href="https://github.com/rayburgemeestre/fsnotifier-remote/">https://github.com/rayburgemeestre/fsnotifier-remote/</a>.</p> <h2 id="alternativesolutions">Alternative solutions</h2> <p>The fsnotifier script outputs it's process id to <code>/tmp/fsnotifier.pid</code> and hooks two signals, so you can enable/disable it with a signal. Disabling will simply pause outputting all updates from the real fsnotifier (that is invoked via ssh).</p> <pre><code>kill -SIGINT $(cat /tmp/fsnotifier.pid) - pause all activity kill -SIGHUP $(cat /tmp/fsnotifier.pid) - continue all activity</code></pre> <p>Another extension you may find useful would be to make the buildscript touch a file like, i.e. <code>/path/to/project/DISABLE_FSNOTIFIER</code> and make the fsnotifier script pause itself (or behave differently) during the build until it sees for example the <code>ENABLE_FSNOTIFIER</code> file.</p> <p>Simply disabling fsnotifier again doesn't fix the problem, CLion would keep nagging occasionally about conflicts with files that have changed both on disk and in memory. And when auto-generated files <em>are</em> being re-generated by the build, I want my IDE to reflect them immediately.</p> <h2 id="fine-tuningyourfilter">Fine-tuning your filter</h2> <p>The filter is just a bash/ksh function, so you can easily extend it with patterns appropriate to your project. The fun thing is you can "killall -9 fsnotifier", and Jetbrains will simply restart it. So no need to restart Jetbrains (and with that having it re-index your project). Debug the filters by tailing: <code>/tmp/fsnotifier-included.log</code> and <code>/tmp/fsnotifier-filtered.log</code>.</p> <h2 id="update:13thoctober2016">Update: 13th October 2016</h2> <p>No longer do I need to filter out <code>*.o</code> files etc. to get a better responsive IDE nowadays. The network improved (and perhaps it's something that improved in newer CLion versions). <a href="https://github.com/rayburgemeestre/fsnotifier-remote/">Another change</a> I did make to the script is based on the <code>ROOTS</code> that get registered (for monitoring the project path) use <code>fsnotifier</code> over <code>ssh</code> or not. (for local projects it would try to login via <code>ssh</code> otherwise, finding nothing and the IDE would hang at that point).</p> <p><a href="https://github.com/rayburgemeestre/fsnotifier-remote/">https://github.com/rayburgemeestre/fsnotifier-remote/commit/414e2e1f937a59a9ab11eede6b999c8170e30af0</a></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 02 Sep 2015 00:00:00 +0000 Enable fsnotifier for Jetbrains IDE's like PyCharm over NFS/SSHFS network share //blog.cppse.nl/jetbrains-fsnotifier-over-remote-connection<div class="datestamp">August 14 2015</div> <div class="h1"><a href="//blog.cppse.nl/jetbrains-fsnotifier-over-remote-connection"> <h1 id="enablefsnotifierforjetbrainsideslikepycharmovernfssshfsnetworkshare">Enable fsnotifier for Jetbrains IDE's like PyCharm over NFS/SSHFS network share</h1> </a></div> <p>This should work for all their editors, PyCharm, Intellij, CLion, PhpStorm, Webstorm, <em>etc.</em></p> <p>The editor(s) use this tool to "subscribe" to changes on the filesystem. So if you change a file that's also in a buffer in for example CLion, it will know it needs to reload that file from disk in order to show the latest changes.</p> <p>Without this tool it will fallback to periodically checking for changes or when a specific file is activated, I don't know exactly, but it's slower anyway.</p> <p>You probably started searching for a solution because you saw this error in the console or in a popup in the IDE:</p> <pre><code>trigen@baymax:/home/trigen/Downloads/clion-1.0.4&gt; FSNOTIFIER_LOG_LEVEL=info ./bin/clion.sh Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=350m; support was removed in 8.0 [ 3243] WARN - om.intellij.util.ProfilingUtil - Profiling agent is not enabled. Add -agentlib:yjpagent to idea.vmoptions if necessary to profile IDEA. [ 14166] WARN - api.vfs.impl.local.FileWatcher - Project files cannot be watched (are they under network mount?) &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</code></pre> <p>Let's fix it by having the IDE run fsnotifier over SSH on the actually server.</p> <p>I will use as an example a project named <code>MyFirstProject</code> mounted via NFS from a server named <code>DevelopmentMachine</code>:</p> <pre><code>sudo mount -t nfs DevelopmentMachine:/home/ray/projects/MyFirstProject /projects/MyFirstProject -o rw,user,hard,intr,tcp,vers=3,timeo=600,_netdev,nolock,exec</code></pre> <p>First you need fsnotifier on <code>DevelopmentMachine</code>, because that machine should be able to subscribe to the filesystem events. I downloaded and build the one from <a href="https://github.com/ThiefMaster/fsnotifier-remote">ThiefMaster/fsnotifier-remote</a>.</p> <p>Test it by starting it and adding the project like this (<code>&gt;&gt;&gt;</code> is your input, <code>&lt;&lt;&lt;</code> the output you get):</p> <pre><code>[ray@DevelopmentMachine linux]$ ./fsnotifier &gt;&gt;&gt; ROOTS &gt;&gt;&gt; /home/ray/projects/MyFirstProject &gt;&gt;&gt; # &lt;&lt;&lt; UNWATCHEABLE &lt;&lt;&lt; #</code></pre> <p>Now it's watching, trigger some changes on something in that root (<em>i.e.</em> open a <code>vim hi.txt</code>):</p> <pre><code>&lt;&lt;&lt; CREATE &lt;&lt;&lt; /home/ray/projects/MyFirstProject/.hi.txt.swp &lt;&lt;&lt; CHANGE &lt;&lt;&lt; /home/ray/projects/MyFirstProject/.hi.txt.swp &lt;&lt;&lt; STATS &lt;&lt;&lt; /home/ray/projects/MyFirstProject/.hi.txt.swp ...</code></pre> <p>In this example I work locally on <code>/projects/MyFirstProject</code>, where it's <code>/home/ray/projects/MyFirstProject</code> on the server. The super easy solution is to make sure your local path is exactly the same. In my case I made a symlink so I have <code>/home/ray/projects/MyFirstProject</code> both on my local- and remote machine.</p> <p>On the local machine I can run the above <code>./fsnotifier</code> example through ssh, lets test that (make sure you have ssh keys configured correctly for this, otherwise you will get an authentication prompt):</p> <pre><code>trigen@baymax:/projects/fsnotifier-remote[master]&gt; ssh -l ray DevelopmentMachine /home/ray/projects/fsnotifier-remote/linux/fsnotifier64 &gt;&gt;&gt; ROOTS &gt;&gt;&gt; /home/ray/projects/MyFirstProject ...</code></pre> <p>The fun thing is that the displayed files are actually already correct, so you don't need to do some any mapping. Just make sure you launch your IDE on the <code>/home/ray/projects/MyFirstProject</code> folder. (Which the beforementioned <code>fsnotifier-remote</code> script should be able to do, but I encountered multiple issues executing it under Linux and I didn't like to dive into it's Python code).</p> <p>I created a local <code>fsnotifier</code> script with the following contents:</p> <pre><code>#!/bin/ksh93 ssh -l ray DevelopmentMachine /home/ray/projects/fsnotifier-remote/linux/fsnotifier64</code></pre> <p>Then told my IDE to use this wrapper (make sure it's executable with <code>chmod +x</code>)</p> <pre><code>trigen@baymax:/home/trigen/Downloads/clion-1.0.4/bin&gt; vim idea.properties idea.filewatcher.executable.path=/projects/fsnotifier-remote/fsnotifier &lt;&lt;&lt; add this line!</code></pre> <p>You can log the communication between the IDE and fsnotifier over ssh by inserting this in the fsnotifier wrapper script: <code>strace -f -F -ttt -s 512 -o /tmp/fsnotifier-debug.log</code> (put it before the ssh command). Then you can find stuff like this in the <code>/tmp/fsnotifier-debug.log</code>:</p> <pre><code>823 3722 1439468464.159229 read(4, "ROOTS\n", 16384) = 6 833 3722 1439468464.159644 read(4, "/home/ray/projects/MyFirstProject\n", 16384) = 28 843 3722 1439468464.160011 read(4, "/home/trigen/.clion10/system/extResources\n", 16384) = 42 853 3722 1439468464.160489 read(4, "#\n", 16384) = 2</code></pre> <p>Hope this approach will help others!</p> <h3 id="update:13thoctober2016">Update: 13th October 2016</h3> <p>Having this script in place allows for some fun "performance optimizations" in case you have a <a href="//blog.cppse.nl/improve-performance-jetbrains-ide-remote-machine">slow network connection</a>.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 14 Aug 2015 00:00:00 +0000 Nagios 4 + Nagvis + Nagiosgraph + Nagios plugins Dockerfile / Docker image //blog.cppse.nl/nagios4-nagvis-nagiosgraph-docker<div class="datestamp">May 17 2015</div> <div class="h1"><a href="//blog.cppse.nl/nagios4-nagvis-nagiosgraph-docker"> <h1 id="nagios4nagvisnagiosgraphnagiospluginsdockerfiledockerimage">Nagios 4 + Nagvis + Nagiosgraph + Nagios plugins Dockerfile / Docker image</h1> </a></div> <p>Setting up Nagios + Nagvis + Nagiosgraph on Ubuntu (14.04) can be a pain in the neck.</p> <p>Default Ubuntu (14.04) ships with Nagios3, which is plain ugly and old, also the Nagvis is pretty old and less user friendly. So I created a Docker image with that install the&mdash;at the time of writing&mdash;newest versions of Nagios, Nagvis, Nagios plugins and Nagios graph. (Along with Apache2.4 + PHP 5.5 for the Web interfaces.)</p> <p><span style="color:red;">I'm new to Docker, so leaving comments/rants/improvements is appreciated <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></span></p> <h2 id="tldr">TL;DR</h2> <pre><code>docker run -P -t -i -v /your/path/to/rrd-data:/usr/local/nagiosgraph/var/rrd rayburgemeestre/nagiosnagvis docker ps # to discover port boot2docker ip # to discover host other than localhost (if you are using boot2docker on OSX) open http://host:port # you will get a webinterface pointing to nagios/nagvis or nagiosgraph</code></pre> <p><style> .smaller img { width: 275px; height: 282px; } </style> <center class="smaller"> <a href="//cdn.cppse.nl/108-1.png"><img border="0" src="//cdn.cppse.nl/108-thumb-1.png" /></a> <a href="//cdn.cppse.nl/108-2.png"><img border="0" src="//cdn.cppse.nl/108-thumb-2.png" /></a> <a href="//cdn.cppse.nl/108-3.png"><img border="0" src="//cdn.cppse.nl/108-thumb-3.png" /></a> </center></p> <h2 id="caveatswiththeinstall">Caveats with the install</h2> <p>For Nagvis you need a different broker called livestatus, where both Nagios and Nagvis need to change their configs for, and you must <a href="https://mathias-kettner.de/check_mk_werks.php?werk_id=0276&amp;HTML=yes.">specifically configure it</a> to support Nagios Version 4, otherwise you will get an error starting Nagios. Specifically this one:</p> <pre><code>Error: Could not load module '/usr/local/lib/mk-livestatus/livestatus.o' -&gt; /usr/local/lib/mk-livestatus/livestatus.o: cannot open shared object file: No such file or directory Error: Failed to load module '/usr/local/lib/mk-livestatus/livestatus.o'.</code></pre> <p>Which is fixed by this instruction from the Dockerfile:</p> <pre><code>WORKDIR /usr/local/src/check-mk-raw-${livestatusversion}.cre.demo RUN ./configure --with-nagios4 &amp;&amp; \ make &amp;&amp; \ ### specifically make mk-livestatus package /again/ with the --with-nagios4 flag, by default it's build for nagios3 which doesn't work.. \ cd ./packages/mk-livestatus/mk-livestatus-${livestatusversion} &amp;&amp; \ make clean &amp;&amp; \ ./configure --with-nagios4 &amp;&amp; \ make &amp;&amp; \ make install</code></pre> <p>In the source root the <code>--with-nagios4</code> flag is not propagated to it's sub-packages. So I just make everything and then specifically clean the mk-livestatus-xx package and re-configure with <code>--with-nagios4</code>, <code>make</code>, <code>make install</code>.</p> <p>If I had to guess the livestatus configure script probably by default tries to detect the Linux distrubition, and as Ubuntu 14.04 ships with Nagios 3 by default it probably assumes to use version 3.</p> <p>After the (re)configure, this is the normal output:</p> <pre><code>Event broker module '/usr/local/lib/mk-livestatus/livestatus.o' initialized successfully.</code></pre> <h2 id="builddockerimageyourself">Build docker image yourself</h2> <p>You can use the <a href="https://github.com/rayburgemeestre/docker-nagiosnagvis">Dockerfile on Github</a> to build the image yourself. It contains in one file all the commands you would need to execute to do everything manually.</p> <p>Build with: <code>docker build -t rayburgemeestre/nagiosnagvis .</code>.</p> <h2 id="getdockerimagefromdockerhub">Get docker image from Docker Hub</h2> <p>You can also use the image <code>rayburgemeestre/nagiosnagvis</code> as a base for your own projects using <a href="https://registry.hub.docker.com/u/rayburgemeestre/nagiosnagvis/">Docker Hub</a>.</p> <p>You can also run the base image with: <code>docker run -P -t -i rayburgemeestre/nagiosnagvis</code>.</p> <p>The -P auto-portforwards the port for Apache that runs inside (use <code>docker ps</code> to detect the port).</p> <p>[Snoop around on the container with: <code>docker run -P -t -i --entrypoint /bin/bash rayburgemeestre/nagiosnagvis</code>.]</p> <h2 id="directoriesinthecontainer">Directories in the container</h2> <p>Nagios, Nagvis and Nagiosgraph are all installed in subdirectories of <code>/usr/local</code>.</p> <p>You are likely to want <code>/your/own/rrd-data</code> directory mounted as <code>/usr/local/nagiosgraph/var/rrd</code> inside the container, so the RRD databases are not stored inside the container and retained after rebuilding/upgrading the container. This is possible with the <code>-v</code> flag: <code>docker run -P -t -i -v /your/own/rrd-data:/usr/local/nagiosgraph/var/rrd rayburgemeestre/nagiosnagvis</code></p> <p>Don't forget that the <code>docker</code> user (uid 1000) has the appropriate read-write permissions on that rrd directory.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 17 May 2015 00:00:00 +0000 Qt Applications in Browser //blog.cppse.nl/qt-programs-in-webbrowser<div class="datestamp">February 21 2015</div> <div class="h1"><a href="//blog.cppse.nl/qt-programs-in-webbrowser"> <h1 id="qtapplicationsinbrowser">Qt Applications in Browser</h1> </a></div> <p>While thinking about a Qt application that I also wanted to create a Web-version for, I experimented a bit with the &ldquo;lazy man's approach&rdquo;. I was wondering if I could simply &ldquo;bridge&rdquo; from Qt app to Browser in an easy way.</p> <p>So I created the following proof of concept (Note that this is not just an image, use your mouse to interact):</p> <p><center></p> <iframe frameborder="0" width="450" height="289" src="//cppse.nl/sockets_load.html" scrolling="no"></iframe> <p></center></p> <p>You can control above window from your browser, it doesn&rsquo;t <em>always</em> feel like a real-time experience yet though (mind that it's hosted on a very weak server). I took one of the Qt Examples (gradients), stripped it down a bit and wrote a simple plugin. This plugin once loaded, exposes the graphics through a Websocket to the browser and provides an API for handling mouse behaviours. For the graphics currently PNG images are transferred over the Websocket.</p> <p><center> <a href="//cdn.cppse.nl/94-Screen Shot 2014-11-23 at 22.44.51.png"><img border="0" src="//cdn.cppse.nl/94-thumb-Screen Shot 2014-11-23 at 22.44.51.png" /></a> &nbsp; <a href="//cdn.cppse.nl/94-Screen Shot 2014-11-07 at 20.43.50.png"><img border="0" src="//cdn.cppse.nl/94-thumb-Screen Shot 2014-11-07 at 20.43.50.png" /></a> <br/> <i>The required patch in main.cpp (for gradients) and an example running locally.</i> </center></p> <h2 id="streaming">Streaming</h2> <p>Then thinking about steps on how to improve this, I encountered some really cool Javascript stuff! My next idea was to find a way to stream the window instead of working with PNG images. I didn&rsquo;t pursue this through.</p> <p>One of my frustrations with the web is that there is still not a real standard for streaming with HTML5. Then I found someone who implemented an MPEG1 decoder in javascript and uses that for streaming: <a href="http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets">http://phoboslab.org/log/2013/09/html5-live-video-streaming-via-websockets</a></p> <p>I experimented a bit with hooking up my Raspberry Pi Camera on it and I got it working with &lt; 100ms delay, so it&rsquo;s a nice solution. Only perk is that it doesn&rsquo;t support sound.</p> <h2 id="compiledtojavascriptemscripten-qt">Compiled to Javascript (emscripten-qt)</h2> <blockquote> <p>jsmpeg is a MPEG1 Decoder, written in JavaScript. It's "hand ported", i.e. not compiled with emscripten or similar. This will probably make it obsolete with the advent of asmjs.</p> </blockquote> <p>Reading that I further investigated emscripten, <a href="https://github.com/kripken/emscripten/wiki/Porting-Examples-and-Demos">demos here</a>. And also encountered emscripten-qt(!), view some <a href="http://vps2.etotheipiplusone.com:30176/redmine/projects/emscripten-qt/wiki/Demos">demos here</a>. I didn't get it to work on my own system(s) unfortunately (OSX 10.9.4 and an Ubuntu server). I might attempt on it again later, see if I can include the gradients example compiled to the browser <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />.</p> <p>How emscripten-qt basically works is: it uses a slightly older version of Qt and patches it (I think heavily), it also requires a patched main() in the Qt app. It uses LLVM to compile your application from C++ not to a binary like you'd normally do, but to an assembly for use with <a href="http://asmjs.org/">asm.js</a>. The end result is quite awesome, with some Javascript your Qt program running in the browser, completely client-side. (Some stuff won't be supported like Network).</p> <p>asm.js was originally a Research project at Mozilla and modern browsers will support it. There is a chance that asm.js will be supported natively in the future, but it's already very fast.</p> <h2 id="downloadsourcecode">Download + sourcecode</h2> <p>See <a href="https://bitbucket.org/rayburgemeestre/qwebcontrollerserverplugin">https://bitbucket.org/rayburgemeestre/qwebcontrollerserverplugin</a>.</p> <pre><code>git clone git@bitbucket.org:rayburgemeestre/qwebcontrollerserverplugin.git cd qwebcontrollerserverplugin cmake . make make install cd example/gradients cmake . make ./gradients</code></pre> <p>Or use an IDE, QCreator should work out of the box, and one that supports CMake will probably too (I tested with <a href="https://confluence.jetbrains.com/display/CLION/Early+Access+Program">CLion</a>).</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 21 Feb 2015 00:00:00 +0000 Tweak Battle.... //blog.cppse.nl/tweakbattle<div class="datestamp">January 19 2015</div> <div class="h1"><a href="//blog.cppse.nl/tweakbattle"> <h1 id="tweakbattle....">Tweak Battle....</h1> </a></div> <p><style type="text/css"> span.smaller-image img { height: 195px; } </style></p> <p><span class="smaller-image" style="float:right;"><a href="//cdn.cppse.nl/99-2000571767.jpg"><img border="0" src="//cdn.cppse.nl/99-thumb-2000571767.jpg" /></a></span></p> <p>.... evolved from <a href="http://smashbattle.demontpx.com/video/">Smash Battle</a> and was launched by Tweakers on <a href="http://tweakers.net/nieuws/95131/tweakers-begint-gamestudio-en-brengt-tweak-battle-uit.html">April fools</a> with the title (translated): &ldquo;Tweakers releases Tweak Battle - Tech-site starts Game Studio&rdquo;. It was pretty cool, the day before all Tweakers staff changed their avatars to their &ldquo;8-bit&rdquo; style character. Why blog about this now? Well, now some Tweakers created an <a href="http://tweakers.net/nieuws/100518/tweakers-bouwen-in-48-uur-arcadekast-met-tweak-battle.html">Arcade machine</a> a few days ago for it, it turned out to be quite awesome and I also accidentally stumbled upon some stats from April-fools day.</p> <p>A while ago I added network multiplayer to Smash Battle (<a href="/smashbattle">see old blog post</a>) and then after that we/Jeroen got the idea to change all the characters into <a href="http://tweakers.net/acties/tweakbattle/characters">Tweakers editors/developers</a> and launch the game as an April fools joke. The deadline was pretty tight, we had a lot of ideas for improvements and there were many glitches but we fixed all of them. We had to work many long nights and the night- and morning before the publication of the News at 8 o'clock. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <h2 id="playstats">Play stats</h2> <p>22:00 we fixed a problem that occasionally made a server crash, also you may notice the "active" &amp; "joined" players lines to swap at that point, before that they were mixed up. The difference between the two is simply the number of spectators (you are "joined" if you connect and "active" when you play). Spectators were necessary because the game can hold a maximum of 4 players.</p> <p><center> <a href="//cdn.cppse.nl/99-gameservers-pinpoint=1396319096,1396404011.png"><img border="0" src="//cdn.cppse.nl/99-large-thumb-gameservers-pinpoint=1396319096,1396404011.png" /></a> <br/><br/><I>The only statistics we have, at the time gathered using a simple Munin plugin.</I> </center></p> <h2 id="tenbladeservers">Ten blade servers</h2> <p>Ten seriously over-the-top servers were sponsored by <a href="http://true.nl/">True</a>, and I'm sorry but I forgot the exact specs. We provisioned one of them as the main API server and the other we started nine games per (nine) server(s) on with all the <a href="http://tweakers.net/acties/tweakbattle/maps">different levels</a> evenly distributed.</p> <p>We did quite some last-minute optimizations, like removing the GUI for Servers, so running servers became a lot less CPU intensive. Previously we had to start them with xvfb (we basically used it as a <code>/dev/null</code> for the graphics). Even though I discovered by accident that SDL is so Awesome that it falls back to ncurses (see following image).</p> <p>But in retrospect, we could have ran all the servers from my laptop <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sB" />. It was surely overkill for such a simple game with not that much Network traffic. But just in case it would go world-wide-viral, we could have handled it <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sP" />.</p> <p><center> <a href="//cdn.cppse.nl/99-Untitled.png"><img border="0" src="//cdn.cppse.nl/99-large-thumb-Untitled.png" /></a> <br/><br/><I>ncurses ASCII art rendering of a Tweak Battle server with no joined players.</I> </center></p> <h2 id="furtherimprovements">Further improvements</h2> <p>Jeroen &amp; Bert pushed me to replace the TCP/IP implementation with a UDP one, and this was a good idea. It made a big difference, even more than I expected. We also had to fix some glitches/artifacts that were caused by previous refactorings, i.e. that the game now runs on elapsed time, this created some problems with powerups to disappear too fast, and animations to go to fast here and there. Jeroen also designed and implemented all the Tweakers characters, which was a lot of work, Bert helped all-round and improved server provisioning a lot.</p> <h2 id="theldquomainrdquoserver">The &ldquo;main&rdquo; server</h2> <p>The main server is written in Symfony2 as a REST API (inspired by this <a href="http://williamdurand.fr/2012/08/02/rest-apis-with-symfony2-the-right-way/">implementation</a> and <a href="https://speakerdeck.com/ijansch/scenario-driven-api-design-zendcon-edition">Scenario Driven API Design</a>). For documentation and source code check <a href="https://bitbucket.org/rayburgemeestre/smashbattleapi/">the bitbucket repo</a>.</p> <p>Maximum number of requests on <code>/server/listing</code> per second with- and without Varnish:</p> <pre><code>Requests per second: 43.34 [#/sec] (mean) Requests per second: 12209.64 [#/sec] (mean) # with Varnish</code></pre> <p>We let Varnish cache this listing every 1 second(s). We talked to it asynchronously with C++ using libcurl. Storage used by the API was Redis.</p> <h2 id="thefuture">The future</h2> <ul> <li>git <code>network_multiplayer</code> branch merged into <code>master</code>. But I would like to preserve both character sets, Smash Battle <em>and</em> Tweak Battle.</li> <li>refactoring^2</li> <li>nicknames support, online rankings.</li> <li>(for Tweak Battle:) actual Nerf Guns + Bullets.</li> <li>more game types like capture the flag, instagib, last man standing.</li> <li>intelligent bots.</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 19 Jan 2015 00:00:00 +0000 Example rendered video //blog.cppse.nl/starcry-example-rendered-video<div class="datestamp">January 18 2015</div> <div class="h1"><a href="//blog.cppse.nl/starcry-example-rendered-video"> <h1 id="examplerenderedvideo">Example rendered video</h1> </a></div> <p><center></p> <iframe src="http://player.vimeo.com/video/20219008?title=0&amp;byline=0&amp;portrait=0" width="100%" height="318" frameborder="0"></iframe> <p></center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 18 Jan 2015 00:00:00 +0000 wxhttpproxy new version 1.2 supports CLI //blog.cppse.nl/wxhttpproxy-version-1.2<div class="datestamp">January 14 2015</div> <div class="h1"><a href="//blog.cppse.nl/wxhttpproxy-version-1.2"> <h1 id="wxhttpproxynewversion1.2supportscli">wxhttpproxy new version 1.2 supports CLI</h1> </a></div> <p>Just to announce I created a new version that supports a non-GUI interface. This makes installing and running it commandline on a linux server easy.</p> <p><center> <a href="//cdn.cppse.nl/97-screenshot.png"><img border="0" src="//cdn.cppse.nl/97-thumb-screenshot.png" /></a> &nbsp; <a href="//cdn.cppse.nl/97-screenshot2.png"><img border="0" src="//cdn.cppse.nl/97-thumb-screenshot2.png" /></a> </center></p> <h2 id="cliinterface">CLI interface</h2> <pre><code>ksh$ wxhttpproxy --help Usage: wxhttpproxy [-h] [-f &lt;str&gt;] [-n &lt;num&gt;] [-p &lt;num&gt;] [-q] -h, --help displays help on the command line parameters. -f, --file=&lt;str&gt; output everything to &lt;file&gt;. -n, --number=&lt;num&gt; truncate file each &lt;num&gt; lines (default 500000). -p, --port=&lt;num&gt; bind to port &lt;port&gt; (default 8888). -q, --quiet silent output (nothing to stdout).</code></pre> <h2 id="building">Building</h2> <pre><code>trigen@Rays-MacBook-Pro.local:/tmp&gt; git clone git@bitbucket.org:rayburgemeestre/wxhttpproxy.git Cloning into 'wxhttpproxy'... remote: Counting objects: 394, done. remote: Compressing objects: 100% (391/391), done. remote: Total 394 (delta 269), reused 0 (delta 0) Receiving objects: 100% (394/394), 1.04 MiB | 225.00 KiB/s, done. Resolving deltas: 100% (269/269), done. Checking connectivity... done. trigen@Rays-MacBook-Pro.local:/tmp&gt; cd wxhttpproxy/ /tmp/wxhttpproxy trigen@Rays-MacBook-Pro.local:/tmp/wxhttpproxy[master]&gt; cmake . -- The C compiler identification is AppleClang 6.0.0.6000056 -- The CXX compiler identification is AppleClang 6.0.0.6000056 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working CXX compiler: /usr/bin/c++ -- Check for working CXX compiler: /usr/bin/c++ -- works -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Found wxWidgets: TRUE /usr/local/Cellar/cmake/3.0.2/share/cmake/Modules/UsewxWidgets.cmake -- Configuring done -- Generating done -- Build files have been written to: /tmp/wxhttpproxy trigen@Rays-MacBook-Pro.local:/tmp/wxhttpproxy[master]&gt; make Scanning dependencies of target wxhttpproxy [ 10%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/application.cpp.o [ 20%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/cache.cpp.o [ 30%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/clientdata.cpp.o [ 40%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/consoleoutputhandler.cpp.o [ 50%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/guioutputhandler.cpp.o [ 60%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/httpbuffer.cpp.o [ 70%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/httpproxywindow.cpp.o [ 80%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/logger.cpp.o [ 90%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/socketbuffer.cpp.o [100%] Building CXX object CMakeFiles/wxhttpproxy.dir/src/socketserver.cpp.o Linking CXX executable wxhttpproxy [100%] Built target wxhttpproxy trigen@Rays-MacBook-Pro.local:/tmp/wxhttpproxy[master]&gt;</code></pre> <h2 id="changelog">Changelog</h2> <p>Version 1.2: <img src="http://png-2.findicons.com/files/icons/1676/primo/128/label_blue_new.png" height="32"></p> <ul> <li>Proper Connection: keep-alive support (with reconnects when necessary)</li> <li>Support switching to different hosts/backends within a connection.</li> <li>Commandline (Non-GUI) version, with logging+truncate feature.</li> <li>An actual 1337 icon for the program!</li> </ul> <p>Version 1.1:</p> <ul> <li>HTTPS support</li> <li>OSX support</li> <li>Stability fixes</li> <li>Support for caching content and serving from cache.</li> </ul> <p>Version 1.0:</p> <ul> <li>HTTP proxy that supports CONNECT and GET syntax.</li> </ul> <h2 id="cliexampleusagehttp_proxyhttp:localhost:8888curlhttp:cppse.nltest.txt">CLI example usage (<code>http_proxy=http://localhost:8888 curl http://cppse.nl/test.txt</code>)</h2> <pre><code>ksh$ wxhttpproxy Info - Server listening on port 8888. Thread #1 - New client connection accepted Thread #1 - Connecting to host cppse.nl:80 for client. Thread #1 - Request from client: GET /test.txt HTTP/1.1 Thread #1 - Host socket connected. Thread #1 - &gt;&gt;&gt;: GET /test.txt HTTP/1.1 Thread #1 - &gt;&gt;&gt;: User-Agent: curl/7.30.0 Thread #1 - &gt;&gt;&gt;: Host: cppse.nl Thread #1 - &gt;&gt;&gt;: Accept: */* Thread #1 - &gt;&gt;&gt;: Proxy-Connection: Keep-Alive Thread #1 - &gt;&gt;&gt;: Thread #1 - Response from host. Thread #1 - &lt;&lt;&lt;: HTTP/1.1 200 OK Thread #1 - &lt;&lt;&lt;: Date: Sun, 28 Dec 2014 21:05:09 GMT Thread #1 - &lt;&lt;&lt;: Server: Apache/2.4.6 (Ubuntu) Thread #1 - &lt;&lt;&lt;: Last-Modified: Sun, 09 Feb 2014 00:48:33 GMT Thread #1 - &lt;&lt;&lt;: ETag: "c-4f1ee951f920c" Thread #1 - &lt;&lt;&lt;: Accept-Ranges: bytes Thread #1 - &lt;&lt;&lt;: Content-Length: 12 Thread #1 - &lt;&lt;&lt;: Connection: close Thread #1 - &lt;&lt;&lt;: Content-Type: text/plain Thread #1 - &lt;&lt;&lt;: Thread #1 - &lt;&lt;&lt;: Hello world Thread #1 - Host socket disconnected.</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 14 Jan 2015 00:00:00 +0000 Zend server And the Return of the Oracle Instant Client //blog.cppse.nl/zend-server-oracle-instant-client<div class="datestamp">November 28 2014</div> <div class="h1"><a href="//blog.cppse.nl/zend-server-oracle-instant-client"> <h1 id="zendserverandthereturnoftheoracleinstantclient">Zend server And the Return of the Oracle Instant Client</h1> </a></div> <h2 id="theproblem">The problem</h2> <p>The title being a reference to <a href="https://enrise.com/2011/08/zend-server-oracle-instant-client/">this article from 2011</a>, a blog post from someone who encountered a similar issue once <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />. Hopefully my blog post will prevent someone else from spending a day on this issue. We are in the middle of a migration from Oracle 11.2 to 12.1, and from PHP, Zend server more specifically, we had some connectivity problems to Oracle, the PHP function <a href="http://php.net/oci_connect">oci_connect()</a> returned:</p> <pre><code>PHP Warning: oci_connect(): ORA-28547: connection to server failed, probable Oracle Net admin error in /home/webro/test.php on line 2</code></pre> <p>Good luck googleing that Oracle error code, nothing hints in the right direction, only that it's an error that occurs <em>after</em> the connection is established. Quote from <a href="http://ora-28547.ora-code.com/">http://ora-28547.ora-code.com/</a>:</p> <blockquote> <p>A failure occurred during initialization of a network connection from a client process to the Oracle server: The connection was completed but a disconnect occurred while trying to perform protocol-specific initialization, usually due to use of different network protocols by opposite sides of the connection.</p> </blockquote> <h2 id="theprobleminourcasewaswiththecharacterset">The problem in our case was with the characterset</h2> <p>The "tl;dr" is: you may be using an Oracle "Light" client instead of the "Basic" client. In Zend Server this means that in the Zend Server lib path some libraries are missing. The Light client only supports a few charactersets. If you have some other Characterset that isn't default, that may be the problem. You need to make sure the Oracle Instant client Zend Server is using is the Basic client.</p> <p>Unfortunately you cannot tell this from the phpinfo() output. Both Light and Basic return exactly the same version information.</p> <pre><code>Oracle Run-time Client Library Version =&gt; 11.2.0.2.0 Oracle Instant Client Version =&gt; 11.2</code></pre> <h2 id="howwefoundout..">How we found out..</h2> <p>Luckily I was able to <em>succesfully</em> connect from another virtual machine to the new database server. This was an older Zend server instance, where the Oracle instant client was patched from 11.1 to 11.2. The Zend server that failed had 11.2, so we assumed patching wasn't necessary. I compared the strace outputs.</p> <p>The first observation was that during the communication the server--on the right in the following image--stopped and concludes there is a communication error.</p> <p><center><a href="//cdn.cppse.nl/93-diff-output.png"><img border="0" src="//cdn.cppse.nl/93-large-thumb-diff-output.png" /></a> <br/><i>Working VM on the left, failing server on the right.</i></center></p> <p>The second observation in the diff was that there was also a difference between libraries loaded.</p> <pre><code>- 9166 open("/usr/local/zend/lib/libociei.so", O_RDONLY) = -1 ENOENT (No such file or directory) - 9166 open("/usr/local/zend/lib/libociicus.so", O_RDONLY) = 4 + 17606 open("/usr/local/zend/lib/libociei.so", O_RDONLY) = 4</code></pre> <h2 id="moreinsightintotheproblem..">More insight into the problem..</h2> <p>We didn't specify explicitly what characterset to use for the connection, so it will try to find out after the connection is established. We use WE8ISO8859P15 in our database, and that charset is (amongst others) provided by libociei.</p> <pre><code>$ strings /usr/lib/oracle/11.2/client64/lib/libociei.so|grep WE8ISO8859P15 WE8ISO8859P15 ...</code></pre> <p>Had we specified the charset in the oci_connect parameter (fourth param) we would have seen:</p> <pre><code>PHP Warning: oci_connect(): OCIEnvNlsCreate() failed. There is something wrong with your system - please check that LD_LIBRARY_PATH includes the directory with Oracle Instant Client libraries in /home/webro/test.php on line 4 PHP Warning: oci_connect(): ORA-12715: invalid character set specified in /home/webro/test.php on line 4</code></pre> <p>That would have hinted us to the solution earlier. Also in strace there would have been no connection setup at all, as the client can now bail sooner with "Invalid character set specified". Apparently with the Light oracle client version 11.1 the error used to be more helpful (see beforementioned <a href="https://enrise.com/2011/08/zend-server-oracle-instant-client/">blog post here</a>):</p> <pre><code>ORA-12737: Instant Client Light: unsupported server character set WE8ISO8859P15</code></pre> <h2 id="thefixforzendserver">The fix for Zend server</h2> <p>Replace the Light client with the Basic client, in our case this meant adding a library to Zend Server's libs:</p> <pre><code># After installing the oracle instant client BASIC ln -s /usr/lib/oracle/11.2/client64/lib/libociei.so /usr/local/zend/lib/libociei.so</code></pre> <p>Apparently the difference between Light &amp; Basic is just this one library. The package that provides the Basic client may differ per Linux distribution, you can also download it from oracle.com.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 28 Nov 2014 00:00:00 +0000 Visualize Issue Ranks in Atlassian Jira Agile board //blog.cppse.nl/visualize-issue-ranks-atlassian-jira-agile-board<div class="datestamp">November 1 2014</div> <div class="h1"><a href="//blog.cppse.nl/visualize-issue-ranks-atlassian-jira-agile-board"> <h1 id="visualizeissueranksinatlassianjiraagileboard">Visualize Issue Ranks in Atlassian Jira Agile board</h1> </a></div> <p>In Jira's Agile Board the Ranks of the Issues are visualized underneath the Story point estimates. The highest rank is colored <span style="color:rgb(0, 255, 0);"><strong>green</strong></span>, the lower the priority becomes, the more the color changes to <span style="color:rgb(255, 0, 0);"><strong>red</strong></span>. This way it also becomes visible who is not working according to priorities. See the following screenshot.</p> <p><center> <a href="//cdn.cppse.nl/92-agile-board-with-ranks-v2.png"><img border="0" src="//cdn.cppse.nl/92-large-thumb-agile-board-with-ranks-v2.png" /></a> </center></p> <h2 id="king-servantpattern">King - Servant pattern</h2> <p>At work we use the King-Servant pattern (see screenshot I made of slide 47 from this <a href="http://www.jfokus.se/jfokus11/preso/jf11_ScrumAndXPBeyondTheTrenches.pdf">pdf</a>) to avoid too much concurrent work. This pattern tries to solve the problem that you could end up with four unfinished tickets rather than two or even one complete ticket(s).</p> <p><center> <a href="//cdn.cppse.nl/92-king-servant-pattern.png"><img border="0" src="//cdn.cppse.nl/92-large-thumb-king-servant-pattern.png" /></a> </center></p> <p>We work with remote programmers and therefore don't use a physical Scrum board. During the day and standups we view this board often and found it annoying that in order to determine the "King" ticket, you would need to switch back and forth the "Plan mode" a lot to see the Ranks. The different swimlanes often obfuscate the ranking information. With this script the king ticket is simply the one with rank "<span style="color: rgb(0,255,0)"><strong>1</strong></span>". <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <h2 id="thetampermonkeygreasemonkeyscripttogiveyouthispreciousfeature">The tampermonkey / greasemonkey script to give you this precious feature</h2> <p>Editing configuration may be required to adjust it to your board.</p> <ul> <li>You can view/copy/download it <a href="https://gist.github.com/rayburgemeestre/e2a4ed179d0820c0e8d4">from my gist at github</a> and click the <code>Raw</code> button and copy &amp; paste. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></li> <li><strong>Chrome</strong>: You can use the Tampermonkey extension, "add new script", copy &amp; paste the script and save.</li> <li><strong>FireFox</strong>: You can use the Greasemonkey add-on, add user script, and you need to explicitly add <code>http*://*.atlassian.net/secure/RapidBoard.jspa?*</code> as an include url, then copy &amp; paste the script code from clipboard and save. Sorry Greasemonkey is not very user friendly. </li> </ul> <p>[Edit 2-Nov-2014, I've changed my mind with the coloring, and some other things. The original version 1 is still available <a href="https://gist.github.com/rayburgemeestre/e2a4ed179d0820c0e8d4/28d8d5d3470ea0ab0f273999d10dc90da1fb6842">here</a>]</p> <p>[Edit 2-Jun-2015, I actually use the following script URL: <a href="//cppse.nl/public/tampermonkey_agile_board_prios.js">//cppse.nl/public/tampermonkey_agile_board_prios.js</a> and keep it up-to-date with my gist]</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 01 Nov 2014 00:00:00 +0000 PhpStorm or IntelliJ suddenly hangs / freezes / keyboard not responsive/ unresponsive while editing in Linux / Ubuntu //blog.cppse.nl/phpstorm-intellij-hangs-freezes-unresponsive-keyboard<div class="datestamp">October 31 2014</div> <div class="h1"><a href="//blog.cppse.nl/phpstorm-intellij-hangs-freezes-unresponsive-keyboard"> <h1 id="phpstormorintellijsuddenlyhangsfreezeskeyboardnotresponsiveunresponsivewhileeditinginlinuxubuntu">PhpStorm or IntelliJ suddenly hangs / freezes / keyboard not responsive/ unresponsive while editing in Linux / Ubuntu</h1> </a></div> <h2 id="problem">Problem</h2> <p>The symptom is that while editing the IDE freezes, somehow the keyboard no longer responds. Two years ago at Tweakers there was luckily someone using Ubuntu who could tell me right away how to fix it ("Just <code>killall -9 ibus-x11</code>", and it would magically continue to work). Now more recently at AutoTrack--where I now work--a collegue encountered the same issue. Luckily <em>I</em> knew the fix this time. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sP" /></p> <p>The fact that I personally know at least five different individuals who spent time fixing this makes me guess there are a lot more people still. Hence this blogpost with some keywords that will hopefully lure others into this fix...</p> <h2 id="fix">Fix</h2> <p>According to <a href="https://youtrack.jetbrains.com/issue/IDEA-80790">this bug report</a>:</p> <pre><code>killall -HUP ibus-daemon</code></pre> <p>Or use this one:</p> <pre><code>killall -9 ibus-x11</code></pre> <p><center><img border="0" src="//cdn.cppse.nl/91-399f1b785a2f05263b4f9ebc1380168fdc8f01d2ca31e606120d5bad70d6c2da.jpg" /></center></p> <p>I have no idea what ibus exactly is, and I don't really care <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /> Also it may be possible to just <a href="http://www.installion.co.uk/ubuntu/precise/main/i/ibus/en/uninstall/index.html">uninstall ibus</a>, I didn't try this, but I can imagine the following to work..</p> <pre><code>sudo apt-get remove ibus</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 31 Oct 2014 00:00:00 +0000 Deflate and Gzip compress and decompress functions //blog.cppse.nl/deflate-and-gzip-compress-and-decompress-functions<div class="datestamp">June 1 2014</div> <div class="h1"><a href="//blog.cppse.nl/deflate-and-gzip-compress-and-decompress-functions"> <h1 id="deflateandgzipcompressanddecompressfunctions">Deflate and Gzip compress and decompress functions</h1> </a></div> <h2 id="deflateandgzipcompressanddecompressfunctions">Deflate and Gzip compress and decompress functions</h2> <p>When implementing a gzip compression &amp; decompression for <a href="//blog.cppse.nl/optimizing-your-website-for-performance">my blog</a>. I stumbled upon <a href="http://panthema.net/2007/0328-ZLibString.html">two C++ functions by Timo Bingmann</a>:</p> <ul> <li>string : compress_string(string)</li> <li>string : decompress_string(string)</li> </ul> <p>Renamed these and created two gzip versions from them:</p> <ul> <li>string : compress_deflate(string)</li> <li>string : decompress_deflate(string)</li> <li>string : compress_gzip(string)</li> <li>string : decompress_gzip(string)</li> </ul> <p>These are the differences:</p> <p><center><a href="//cdn.cppse.nl/88-changes.gif"><img border="0" src="//cdn.cppse.nl/88-large-thumb-changes.gif" /></a></center></p> <h2 id="completesource">Complete source</h2> <pre style="height: 600px;" id="editor3">#include &lt;string&gt; #include &lt;sstream&gt; #include &lt;stdexcept&gt; #include &lt;string.h&gt; #include &quot;zlib.h&quot; using std::string; using std::stringstream; // Found these here http://mail-archives.apache.org/mod_mbox/trafficserver-dev/201110.mbox/%3CCACJPjhYf=+br1W39vyazP=ix //eQZ-4Gh9-U6TtiEdReG3S4ZZng@mail.gmail.com%3E #define MOD_GZIP_ZLIB_WINDOWSIZE 15 #define MOD_GZIP_ZLIB_CFACTOR 9 #define MOD_GZIP_ZLIB_BSIZE 8096 // Found this one here: http://panthema.net/2007/0328-ZLibString.html, author is Timo Bingmann // edited version /** Compress a STL string using zlib with given compression level and return * the binary data. */ std::string compress_gzip(const std::string&amp; str, int compressionlevel = Z_BEST_COMPRESSION) { z_stream zs; // z_stream is zlib's control structure memset(&amp;zs, 0, sizeof(zs)); if (deflateInit2(&amp;zs, compressionlevel, Z_DEFLATED, MOD_GZIP_ZLIB_WINDOWSIZE + 16, MOD_GZIP_ZLIB_CFACTOR, Z_DEFAULT_STRATEGY) != Z_OK ) { throw(std::runtime_error(&quot;deflateInit2 failed while compressing.&quot;)); } zs.next_in = (Bytef*)str.data(); zs.avail_in = str.size(); // set the z_stream's input int ret; char outbuffer[32768]; std::string outstring; // retrieve the compressed bytes blockwise do { zs.next_out = reinterpret_cast&lt;Bytef*&gt;(outbuffer); zs.avail_out = sizeof(outbuffer); ret = deflate(&amp;zs, Z_FINISH); if (outstring.size() &lt; zs.total_out) { // append the block to the output string outstring.append(outbuffer, zs.total_out - outstring.size()); } } while (ret == Z_OK); deflateEnd(&amp;zs); if (ret != Z_STREAM_END) { // an error occurred that was not EOF std::ostringstream oss; oss &lt;&lt; &quot;Exception during zlib compression: (&quot; &lt;&lt; ret &lt;&lt; &quot;) &quot; &lt;&lt; zs.msg; throw(std::runtime_error(oss.str())); } return outstring; } // Found this one here: http://panthema.net/2007/0328-ZLibString.html, author is Timo Bingmann /** Compress a STL string using zlib with given compression level and return * the binary data. */ std::string compress_deflate(const std::string&amp; str, int compressionlevel = Z_BEST_COMPRESSION) { z_stream zs; // z_stream is zlib's control structure memset(&amp;zs, 0, sizeof(zs)); if (deflateInit(&amp;zs, compressionlevel) != Z_OK) throw(std::runtime_error(&quot;deflateInit failed while compressing.&quot;)); zs.next_in = (Bytef*)str.data(); zs.avail_in = str.size(); // set the z_stream's input int ret; char outbuffer[32768]; std::string outstring; // retrieve the compressed bytes blockwise do { zs.next_out = reinterpret_cast&lt;Bytef*&gt;(outbuffer); zs.avail_out = sizeof(outbuffer); ret = deflate(&amp;zs, Z_FINISH); if (outstring.size() &lt; zs.total_out) { // append the block to the output string outstring.append(outbuffer, zs.total_out - outstring.size()); } } while (ret == Z_OK); deflateEnd(&amp;zs); if (ret != Z_STREAM_END) { // an error occurred that was not EOF std::ostringstream oss; oss &lt;&lt; &quot;Exception during zlib compression: (&quot; &lt;&lt; ret &lt;&lt; &quot;) &quot; &lt;&lt; zs.msg; throw(std::runtime_error(oss.str())); } return outstring; } /** Decompress an STL string using zlib and return the original data. */ std::string decompress_deflate(const std::string&amp; str) { z_stream zs; // z_stream is zlib's control structure memset(&amp;zs, 0, sizeof(zs)); if (inflateInit(&amp;zs) != Z_OK) throw(std::runtime_error(&quot;inflateInit failed while decompressing.&quot;)); zs.next_in = (Bytef*)str.data(); zs.avail_in = str.size(); int ret; char outbuffer[32768]; std::string outstring; // get the decompressed bytes blockwise using repeated calls to inflate do { zs.next_out = reinterpret_cast&lt;Bytef*&gt;(outbuffer); zs.avail_out = sizeof(outbuffer); ret = inflate(&amp;zs, 0); if (outstring.size() &lt; zs.total_out) { outstring.append(outbuffer, zs.total_out - outstring.size()); } } while (ret == Z_OK); inflateEnd(&amp;zs); if (ret != Z_STREAM_END) { // an error occurred that was not EOF std::ostringstream oss; oss &lt;&lt; &quot;Exception during zlib decompression: (&quot; &lt;&lt; ret &lt;&lt; &quot;) &quot; &lt;&lt; zs.msg; throw(std::runtime_error(oss.str())); } return outstring; } std::string decompress_gzip(const std::string&amp; str) { z_stream zs; // z_stream is zlib's control structure memset(&amp;zs, 0, sizeof(zs)); if (inflateInit2(&amp;zs, MOD_GZIP_ZLIB_WINDOWSIZE + 16) != Z_OK) throw(std::runtime_error(&quot;inflateInit failed while decompressing.&quot;)); zs.next_in = (Bytef*)str.data(); zs.avail_in = str.size(); int ret; char outbuffer[32768]; std::string outstring; // get the decompressed bytes blockwise using repeated calls to inflate do { zs.next_out = reinterpret_cast&lt;Bytef*&gt;(outbuffer); zs.avail_out = sizeof(outbuffer); ret = inflate(&amp;zs, 0); if (outstring.size() &lt; zs.total_out) { outstring.append(outbuffer, zs.total_out - outstring.size()); } } while (ret == Z_OK); inflateEnd(&amp;zs); if (ret != Z_STREAM_END) { // an error occurred that was not EOF std::ostringstream oss; oss &lt;&lt; &quot;Exception during zlib decompression: (&quot; &lt;&lt; ret &lt;&lt; &quot;) &quot; &lt;&lt; zs.msg; throw(std::runtime_error(oss.str())); } return outstring; } // Compile: g++ -std=c++11 % -lz // Run: ./a.out #include &lt;iostream&gt; int main() { std::string s = &quot;Hello&quot;; std::cout &lt;&lt; &quot;input: &quot; &lt;&lt; s &lt;&lt; std::endl; std::cout &lt;&lt; &quot;gzip compressed: &quot; &lt;&lt; compress_gzip(s) &lt;&lt; std::endl;; std::cout &lt;&lt; &quot;gzip decompressed: &quot; &lt;&lt; decompress_gzip(compress_gzip(s)) &lt;&lt; std::endl;; std::cout &lt;&lt; &quot;deflate compressed: &quot; &lt;&lt; compress_deflate(s) &lt;&lt; std::endl;; std::cout &lt;&lt; &quot;deflate decompressed: &quot; &lt;&lt; decompress_deflate(compress_deflate(s)) &lt;&lt; std::endl;; } </pre> <script type="text/javascript">var ld = ld || [];ld.push(function () { var editor = ace.edit("editor3"); editor.setTheme("ace/theme/dreamweaver"); editor.getSession().setMode("ace/mode/c_cpp");});</script> <h2 id="compile">Compile</h2> <p><code>g++ -std=c++11 % -lz</code></p> <h2 id="run">Run</h2> <p><code>./a.out</code></p> <h2 id="output">Output</h2> <p><img border="0" src="//cdn.cppse.nl/88-result.gif" /></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 01 Jun 2014 00:00:00 +0000 Optimizing your website for performance //blog.cppse.nl/optimizing-your-website-for-performance<div class="datestamp">May 29 2014</div> <div class="h1"><a href="//blog.cppse.nl/optimizing-your-website-for-performance"> <h1 id="optimizingyourwebsiteforperformance">Optimizing your website for performance</h1> </a></div> <p>Sometimes as a webdeveloper I have to work with websites where performance is not optimal and sacrifised in exchange for some other quality attribute. Perhaps that's why I "over optimize" some sites--like this blog--which probably is not even worth the effort considering the traffic it gets. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smiley sB" /> However, it's something I spend my time on and it could be useful to others. And this being a blog, I blog about it.</p> <h2 id="staticallycompile">Statically compile</h2> <p>In this blog I statically "compile" this websit with a minimalist tool I created. It was a project with a similar approach as <a href="http://octopress.org/">octopress</a> (based on <a href="http://jekyllrb.com/">Jekyll</a>). I've never used octopress, I'm not sure if it even existed back when <a href="//blog.cppse.nl/launch-of-a-new-blog">starting this blog</a>.</p> <p>A webserver likes plain file serving more than CPU intensive PHP scripts (that generate pages per request). You may not need to statically compile everything like I do, there are alternatives like using <a href="https://www.varnish-cache.org/">Varnish cache</a> to optimize your performance perhaps. <a href="http://tweakers.net">Tweakers.net</a> (and many more high performance websites) use Varnish.</p> <h2 id="yslowpagespeeddiagnostiscs">YSlow &amp; Page Speed diagnostiscs</h2> <p>These are browser plugins, but you can do the checks online via services like: <a href="http://gtmetrix.com/">GTmetrix</a> or <a href="http://www.webpagetest.org/">WebPageTest</a>. In the following screenshot are as an example some quick wins I made using GTmetrix in two ours of optimizing my blog.</p> <p><center> <a href="//cdn.cppse.nl/86-gemetrix.png"><img border="0" src="//cdn.cppse.nl/86-large-thumb-gemetrix.png" /></a> </center></p> <p>In this blog I won't go over all the Tips given on those websites, but there is a lot of useful advice in there I won't repeat. Except for maybe one more: Caching headers for clients (and (caching) proxies), you might want to make sure you get those right. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sD" /></p> <h2 id="cloudfrontasacontentdeliverynetworkcdn">Cloudfront as a Content Delivery Network (CDN)</h2> <p>In google analytics I noticed that in some countries my blog was a lot slower. Especially the loading of media. I verified this with <a href="http://www.webpagetest.org/">WebPageTest</a>, apparently my AWS server that was located in Ireland was too far away for some countries. Maybe not especially slow for the HTML, but especially for media, like images, a.k.a. "Content". Hence, CDN <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /></p> <h2 id="firsthowtosetupacdnwithcloudfront...">First how to setup a CDN with Cloudfront...</h2> <p>You start with creating an <a href="http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html">"S3" bucket</a>, which is basically a "dumb" but fast hosting for files. For every file you put there you have to set permissions, headers (if you want those headers to be returned to the client (i.e. Content-Type, etc.)). Normally a webserver like apache would do most of that stuff for you, here you have to do it yourself. All your static content should be uploaded to such a bucket.</p> <p>Your bucket is something named like <code>&lt;BUCKETNAME&gt;.s3-website-eu-west-1.amazonaws.com</code>. As you can guess from the domain name, the bucket is hosted in one location. With "one location" I mean the unique URL, but also "EU West 1", which is one region (or location).</p> <p>Files in the bucket are accessible via HTTP: <code>//cdn-cppse-nl.s3-website-eu-west-1.amazonaws.com/some/path/to/a/file.txt</code>.</p> <p>If you put Cloudfront in front of your bucket, you can make if fetch from your bucket, and "cache" it in X locations all over the world. Your cloudfront endpoint is something like <code>&lt;UNIQUEID&gt;.cloudfront.net</code>. </p> <p>When you want to use another domain in your HTML, you can define a <code>CNAME</code> (canonical name, a hostname that resolves to another hostname basically), which is a dns record that points to the cloudfront.net domain. In my case <code>cdn.cppse.nl</code> points to <code>d15zg7znhuffnz.cloudfront.net</code> (which reads from <code>cdn-cppse-nl.s3-website-eu-west-1.amazonaws.com</code>). </p> <p>Creating a CNAME has the advantage that you can at DNS level switch to another CDN. If I were to use akamai, I can make the CNAME point to something from akamai. I could also prepare a totally new bucket and/or cloudfront endpoint, and then switch to the new endpoint by changing the CNAME to another <code>&lt;UNIQUEID2&gt;.cloudfront.net</code>.</p> <h2 id="secondhowcloudfrontcdnswork...">Second how Cloudfront / CDNs work...</h2> <p>Cloudfront hostname resolves to multiple IP addresses. At DNS level the actual "edge" (a.k.a. server, in terms of it's ip address) is chosen where the actual files are to be fetched from. You can interpret all the cloudfront edges as "mirrors" for your S3 bucket. On my home PC (which is currently near Amsterdam) when I resolve <code>cdn.cppse.nl</code> (<code>d15zg7znhuffnz.cloudfront.net</code>) it is resolved to a list of IP's:</p> <pre><code>&gt; cdn.cppse.nl Server: 127.0.1.1 Address: 127.0.1.1#53 Non-authoritative answer: cdn.cppse.nl canonical name = d15zg7znhuffnz.cloudfront.net. Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.13.72 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.12.206 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.12.246 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.13.12 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.13.65 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.12.249 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.13.2 Name: d15zg7znhuffnz.cloudfront.net Address: 54.230.15.34</code></pre> <p>The fastest "edge" for my location is returned first and has the address: <code>54.230.13.72</code>. If you <em>reverse</em> DNS lookup that IP you can see "ams1" in the hostname the ip resolved too.</p> <pre><code>&gt; 54.230.13.72 Server: 127.0.1.1 Address: 127.0.1.1#53 Non-authoritative answer: 72.13.230.54.in-addr.arpa name = server-54-230-13-72.ams1.r.cloudfront.net. Authoritative answers can be found from:</code></pre> <div style="float:right;"><a href="//cdn.cppse.nl/86-edges.png"><img border="0" src="//cdn.cppse.nl/86-thumb-edges.png" /></a></div> <p>This is specific to how Amazon structures their hostnames. <code>ams1</code> in <code>server-54-230-13-72.ams1.r.cloudfront.net</code> stands for <code>Amsterdam Airport Schiphol</code>. Their coding is based on the closest <a href="http://en.wikipedia.org/wiki/International_Air_Transport_Association_airport_code">International Airport IATA code</a>. You can check how the domain resolves and what latency/packetloss is from a lot of different "checkpoints" (locations) at once, with tools like <a href="http://cloudmonitor.ca.com/en/ping.php?vtt=1401395524&amp;varghost=d15zg7znhuffnz.cloudfront.net&amp;vhost=_&amp;vaction=ping&amp;ping=start">APM Cloud Monitor</a>. In China/Hong Kong the address it resolves to is: <code>54.230.157.116</code>. The reverse dns resolution for that ip is <code>server-54-230-157-116.sin3.r.cloudfront.net</code>, where <code>SIN</code> is the code for <code>Republic of Singapore</code>. So they won't have to download my javascript/css/media there all the way from Amsterdam.</p> <p>If your website is entirely static, you could host everything this way. And hopefully people from all over the world can benefit from fast downloads from their nearest edges.</p> <h2 id="afewchallengesihadwithcloudfront">A few challenges I had with Cloudfront</h2> <p>After switching to Cloudfront I first noticed that loadtimes increased! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sA" /> I forgot that my apache used <code>mod_gzip</code> (and <code>mod_deflate</code>) to send text-based content compressed to the http client/webbrowser. But I uploaded my stuff to S3 "as is", which is plain/text and <em>not</em> gzipped.</p> <p>A webbrowser normally sends in it's request whether it supports <code>gzip</code> or <code>deflate</code> encoding. If it does, apache will compress the content in a way to the client's preference, otherwise it will serve the content "as is". S3 is simply a key-value store in a way, so this conditional behaviour based on a client's headers like <code>Accept-Encoding:gzip,deflate,sdch</code> isn't possible. In the <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/ServingCompressedFiles.html#CompressedS3">documentation</a> you see that you have to create separate files. </p> <!-- http://blog.maxcdn.com/accept-encoding-its-vary-important/ --> <p>Unfortunately Javascript doesn't have access to the browsers <code>Accept-Encoding</code> header (that one my chromium sends). So you cannot <code>document.write</code> the correct includes based on this client-side. That was my first idea.</p> <p>How I now resolved it: For the CSS and Javascript files served from Cloudfront, I upload the plain version <code>file.css</code> and a compressed version <code>file.gz.css</code>. With correct headers etc., like this:</p> <pre><code># Create expiry date expir="$(export LC_TIME="en_US.UTF-8"; date -u +"%a, %d %b %Y %H:%M:%S GMT" --date "next Year")" # Copy global.js to global.gz.js and gzip compress the copy cp -prv global.js global.gz.js gzip -9 global.gz.js # Upload the files to bucket s3cmd --mime-type=text/css \ --add-header="Expires:$expir" \ --add-header=Cache-Control:max-age=31536000 \ -P put global.js s3://cdn-cppse-nl/global.js s3cmd --mime-type=text/css \ --add-header=Content-Encoding:gzip \ --add-header="Expires:$expir" \ --add-header=Cache-Control:max-age=31536000 \ -P put global.gz.js s3://cdn-cppse-nl/global.gz.js</code></pre> <p><a href="http://s3tools.org/s3cmd">s3cmd</a> is awesome for uploading stuff to s3 buckets. It also has a very useful <code>sync</code> command.</p> <h2 id="perfectrouting">Perfect routing</h2> <p>Because I now have separate files for compressed and uncompressed javascript/css files, I cannot serve my static HTML files "blindly" from my CDN anymore. I now have to make sure I send the HTML either with references to <code>file.gz.css</code> or <code>file.css</code> based on the client's browser request headers.</p> <p>So I introduced "Perfect routing", okay, I'm kind of trolling with the "Perfect" here, but I use <a href="http://www.gnu.org/software/gperf/">"perfect hash generation"</a> with <code>gperf</code>. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" /> At compiletime I output an input file for gperf and have gperf generate a hash function that can convert an <code>article name string</code> (the Request URI) to an <code>Index</code> (or to nothing in case the request is invalid). That <code>Index</code> points directly to the position in a map that it also generates containing the filename that corresponds to the article name. In that way it can fetch the file in a single lookup, and the filesystem is never hit for invalid requests.</p> <p>My <code>routing.cgi</code> program does the following:</p> <ul> <li>Read the <code>Accept-Encoding</code> header from the client.</li> <li>If the generated hash function returns the filename: read the HTML into memory.</li> <li>If client requested gzip encoding: replace in the HTML javascript and CSS includes with a regex to their <code>.gz.js</code> versions. Compress the HTML itself too in the same encoding. </li> <li>If client requested deflate encoding: do the same with deflate encoding on the HTML, but I currently didn't implement <code>.defl.js</code> versions for the Javascript and CSS.</li> <li>Also after fixing the javascript and CSS includes, compress the HTML now in the same way (gzip or deflate).</li> <li>For fun it will add a <code>X-Compression</code> header with compression ratio info.</li> <li>If the compressed length exceeds the plain version, it will use the plain version.</li> </ul> <h3 id="routing.cgiapache2.4config">routing.cgi apache 2.4 config</h3> <p>For now routing.cgi is a simple cgi program, it could be further optimized by making it an apache module, or perhaps using fastcgi.</p> <pre><code>AddHandler cgi-script .cgi ScriptAlias /cgi-bin/ "/srv/www/vhosts/blog.cppse.nl/" &lt;Directory "/srv/www/vhosts/blog.cppse.nl/"&gt; AllowOverride None Options +ExecCGI -Includes Order allow,deny Allow from all Require all granted DirectoryIndex /cgi-bin/routing.cgi FallbackResource /cgi-bin/routing.cgi &lt;/Directory&gt;</code></pre> <h3 id="routing.cppafewcodesnippets">routing.cpp a few code snippets</h3> <p>1) Determine encoding:</p> <pre><code>char *acceptEncoding = getenv("HTTP_ACCEPT_ENCODING"); enum encodings {none, gzip, deflate}; encodings encoding = none; if (acceptEncoding) { if (strstr(acceptEncoding, "gzip")) encoding = gzip; else if (strstr(acceptEncoding, "deflate")) encoding = deflate; }</code></pre> <p>2) The hash lookup:</p> <pre><code>static struct knownArticles *article = Perfect_Hash::in_word_set(requestUri, strlen(requestUri)); if (article) { printf("X-ArticleId: %d\n", article-&gt;articleid); printf("X-ArticleName: %s\n", article-&gt;filename); printf("Content-Type: text/html; charset=utf-8\n"); .... ss &lt;&lt; in.rdbuf(); } else { printf("Status: 404 Not Found\n"); printf("Content-Type: text/html; charset=utf-8\n"); ss &lt;&lt; "404 File not found"; }</code></pre> <p>3) The regexes:</p> <pre><code>if (encoding == gzip) { //std::regex regx(".css"); //str = std::regex_replace(str, regx, string(".gz.css")); const boost::regex scriptregex("(&lt;script[^&gt;]*cdn.cppse.nl[^ ]*)(.js)"); const boost::regex cssregex("(&lt;link[^&gt;]*cdn.cppse.nl[^ ]*)(.css)"); const std::string fmt("\\1.gz\\2"); str = boost::regex_replace(str, scriptregex, fmt, boost::match_default | boost::format_sed); str = boost::regex_replace(str, cssregex, fmt, boost::match_default | boost::format_sed); cstr.assign(compress_string2(str)); }</code></pre> <p>4) Compressing to gzip or deflate:</p> <p>I simply took of Timo Bingmann his <code>{de}compress_string</code> functions, whom use deflate, and created gzip versions of these. They took me a while to get right, to find the correct parameters etc., so you may find them useful.</p> <p>You can find them here: <a href="//blog.cppse.nl/deflate-and-gzip-compress-and-decompress-functions">Deflate and Gzip Compress- and Decompress functions</a>.</p> <h2 id="moreperformancewins:deferredjavascriptloading">More "performance wins": deferred javascript loading</h2> <p>As a base for the html I use <a href="http://adapt.960.gs/">adaptiv.js</a> which provides a grid layout for a responsive design, with the following config:</p> <pre><code>// Find global javascript include file var scripts = document.getElementsByTagName('script'), i = -1; while (scripts[++i].src.indexOf('global') == -1); // See if we're using gzip compression, and define config for adaptiv.js var gzip = scripts[i].src.indexOf('.gz.js') != -1, rangeArray = gzip ? [ '0px to 760px = mobile.gz.css', '760px to 980px = 720.gz.css', '980px to 1280px = 960.gz.css', '1280px = 1200.gz.css', ] : [ '0px to 760px = mobile.css', '760px to 980px = 720.css', '980px to 1280px = 960.css', '1280px = 1200.css' ];</code></pre> <p>Only after loading the javascript will it correctly "fix" the right css include, introducing an annoying "flicker" effect. This makes it necessary to require the <code>adaptiv.js</code> javascript asap (a.k.a. in the header of the page).</p> <p>To fix this I simply added the same css includes with media queries:</p> <pre><code>&lt;link href='//cdn.cppse.nl/global.css' rel='stylesheet'&gt; &lt;link href='//cdn.cppse.nl/assets/css/960.css' media='only screen and (min-width: 980px) and (max-width: 1280px)' rel='stylesheet'&gt; &lt;link href='//cdn.cppse.nl/assets/css/720.css' media='only screen and (min-width: 760px) and (max-width: 980px)' rel='stylesheet'&gt; &lt;link href='//cdn.cppse.nl/assets/css/1200.css' media='only screen and (min-width: 1280px)' rel='stylesheet'&gt; &lt;link href='//cdn.cppse.nl/assets/css/mobile.css' media='only screen and (min-width: 0px) and (max-width: 760px)' rel='stylesheet'&gt;</code></pre> <p>Now the javascript in <code>adaptiv.js</code> is only a fallback for browsers that don't support these queries. All javascript can now be included after DOM elements are loaded/rendered. Nowadays there may be different libraries that don't have this problem. But I'm not up-to-date on that <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />. </p> <p>As long as you make sure the javascript is loaded after all elements are drawn. Personally I don't put do that in the <code>&lt;body&gt;</code>'s onload="" attribute, as that is executed after everything on your page is loaded. I prefer to put it right before the body tag closes (<code>&lt;/body&gt;</code>), as only the static DOM should have been outputted for the browser. (You may want your javascript photo-viewer loaded before the very last thumbnail is done loading for example.)</p> <pre><code>// Now start loading the javascript... var j = document.createElement('script'); j.type = 'text/javascript'; j.src = '//cdn.cppse.nl/global.' + (gzip ? 'gz.' : '') + 'js?' + version; document.body.appendChild(j);</code></pre> <p>You can also do this for certain stylesheets, like the <code>print.css</code> perhaps.</p> <h2 id="imagecompression">Image compression</h2> <p>Another huge gain is of course compressing images. I have PNG, JPG and GIF images. Before I choose a format I already try to choose the most appropriate encoding. Typically PNG or GIF for screenshots, JPG for everything else. In my static site generation, I also optimize all images.</p> <p><a href="http://x128.ho.ua/pngutils.html">TruePNG + PNGZopfli</a> are the best for lossless compression, see <a href="http://css-ig.net/png-tools-overview.html">this awesome comparison</a>. They are windows tools, but they run perfectly with wine, albeit a little slower that's how I use them. For gifs I use <a href="http://www.lcdf.org/gifsicle/">gifsicle</a> (<code>apt-get install gifsicle</code>), and for JPG <a href="http://freecode.com/projects/jpegoptim">jpegoptim</a> (<code>apt-get install jpegoptim</code>).</p> <pre><code>minimize_png: wine /usr/local/bin/TruePNG.exe /o4 "file.png" wine /usr/local/bin/PNGZopfli.exe "file.png" 15 "file.png.out" mv "file.png.out" "file.png" minimize_jpg: jpegoptim --strip-all -m90 "file.jpg" minimize_gif: gifsicle -b -O3 "file.gif"</code></pre> <p>Excerpt from <code>minimize.log</code>:</p> <pre><code>compression of file 86-large-thumb-edges.png from 96K to 78K compression of file 86-edges.png from 45K to 35K compression of file 86-thumb-edges.png from 38K to 31K compression of file 600width-result.png from 1,3M to 121K compression of file 72-large-thumb-userjourney.jpg from 100K to 29K compression of file 57-large-thumb-diff_sqrt_carmack_d3rsqrt.jpg from 557K to 95K compression of file 63-result.jpg from 270K to 137K compression of file 55-large-thumb-vim.jpg from 89K to 27K</code></pre> <h2 id="replacegooglecustomsearchwithsomethingfaster">Replace google "Custom search" with something "faster"</h2> <p>Google's <a href="https://www.google.com/cse/">custom search</a> is pretty useful and easy to setup. I did have to do a few nasty CSS hacks to integrate it in my site the way I wanted.</p> <ul> <li>The <em>advantage</em> is that is does the crawling automatically.</li> <li>The <em>disadvantage</em> is that it does the crawling automatically. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sDH" /> </li> <li>Another disadvantage is that it adds a lot of javascript to your site.</li> </ul> <p>In my case when I search for a keyword "foobar", google search would yield multiple hits:</p> <ul> <li><code>/index</code> - main page</li> <li><code>/blog</code> - category</li> <li><code>/1</code> - paging</li> <li><code>/foobar</code> - individual article</li> </ul> <p>Wanting more control I switched to <a href="http://fallabs.com/tokyodystopia/">Tokyo Dystopia</a> <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sP" />. You can see it in action when searching via the searchbox on top of the page. For this to work I now also generate a "searchdata" outputfile alongside the HTML, which is simply the "elaborated" Markdown text. With elaborated I mean code snippets and user comments included. </p> <h2 id="cssandjavascriptminification">CSS and Javascript minification</h2> <p>Not going into detail on this one. As a tool I use <a href="https://code.google.com/p/mince/">mince</a>, which by default provides css and js minification with with csstidy and jsmin. For javascript I made a small adjustment to make it use crisp (who is an old collegue of mine)'s <a href="http://crisp.tweakblogs.net/blog/6861/jsmin%2B-version-14.html">JSMin+</a>. Because it's a more advanced minifier that yields even smaller javascript files.</p> <h2 id="deferredloadingofembeddedobjectslikeyoutubevideos">Deferred loading of embedded objects like YouTube videos</h2> <p>I found YouTube/Vimeo &amp; Other flash embeds in my blog posts annoyingly slow. So what I do is put the actual embed code in a container as an HTML comment. For example: I have a custom video player for an mp4 stream like this:</p> <pre><code>&lt;p&gt;&lt;center&gt; &lt;a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/83-videothumb.png); width: 750px; height:487px; display:block;"&gt; &lt;!-- &lt;object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="487"&gt; &lt;param name="movie" value="//cdn.cppse.nl/player.swf" /&gt; &lt;param name="allowfullscreen" value="true" /&gt; &lt;param name="allowscriptaccess" value="always" /&gt; &lt;param name="flashvars" value="file=//blog.cppse.nl/videos/wxhttpproxy.mp4&amp;repeat=never&amp;shuffle=true&amp;autostart=true&amp;streamer=lighttpd&amp;backcolor=000000&amp;frontcolor=ffffff" /&gt; &lt;embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="487" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//blog.cppse.nl/videos/wxhttpproxy.mp4&amp;repeat=never&amp;shuffle=true&amp;autostart=true&amp;streamer=lighttpd&amp;backcolor=000000&amp;frontcolor=ffffff" /&gt; &lt;/object&gt; --&gt; &lt;/a&gt; &lt;/center&gt;&lt;/p&gt;</code></pre> <p>The object_holder displays a screenshot of the player, giving the illusion it's already loaded. Only when the user clicks the screenshot the commented <code>&lt;object&gt;</code> is inserted using the <code>videoInLink</code> javascript function.</p> <pre><code>function videoInLink(anchor) { anchor.innerHTML = anchor.innerHTML.replace('&lt;!--','').replace('--&gt;',''); anchor.removeAttribute('href'); anchor.onclick = null; return false; } </code></pre> <h2 id="theend">The end</h2> <p>Concluding my ramblings, I hope you may have find something of use in this post.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 29 May 2014 00:00:00 +0000 Lame PHP Parser, visualization and how cool hhvm is! //blog.cppse.nl/lame-php-parser-test-visualization-and-hhvm-benchmark<div class="datestamp">May 12 2014</div> <div class="h1"><a href="//blog.cppse.nl/lame-php-parser-test-visualization-and-hhvm-benchmark"> <h1 id="lamephpparservisualizationandhowcoolhhvmis">Lame PHP Parser, visualization and how cool hhvm is!</h1> </a></div> <p>Created a "Lame PHP Parser" that in this picture visualizes it's own sourcecode! <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>I thought the output was pretty cool.</p> <p><center> <a href="//cdn.cppse.nl/85-uml.png"><img border="0" src="//cdn.cppse.nl/85-large-thumb-uml.png" /></a> </center></p> <p>It's not the most efficient code, which is fine for my current use case. I was wondering if I could speed it up with Facebook's <a href="http://hhvm.com/">HHVM</a> (HipHop VM), a PHP virtual machine.</p> <h2 id="hiphopvmexecutesthecode582timesfaster">HipHop VM executes the code 5,82 times faster!(*)</h2> <p>Parsing the <a href="http://symfony.com/">Symfony</a> code in my 'vendor' directory, takes ~57 seconds with the PHP executable provided by Ubuntu.</p> <pre><code>PHP 5.5.3-1ubuntu2.1 (cli) (built: Dec 12 2013 04:24:35) Copyright (c) 1997-2013 The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies with Zend OPcache v7.0.3-dev, Copyright (c) 1999-2013, by Zend Technologies</code></pre> <p>Benchmarks from three successive runs:</p> <ul> <li>real 0m57,34s, user 0m56,75s, sys 0m0,29s</li> <li>real 0m57,18s, user 0m56,58s, sys 0m0,28s</li> <li>real 0m58,38s, user 0m57,72s, sys 0m0,34s</li> </ul> <p>Parsing the same directory three times with hhvm.</p> <pre><code>HipHop VM 3.0.1 (rel) Compiler: tags/HHVM-3.0.1-0-g97c0ac06000e060376fdac4a7970e954e77900d6 Repo schema: a1146d49c5ba0d6db903beb3a4ed8a3766fef182</code></pre> <p>Benchmarks from three successive runs:</p> <ul> <li>real 0m9,86s, user 0m9,04s, sys 0m0,54s</li> <li>real 0m9,85s, user 0m9,15s, sys 0m0,45s</li> <li>real 0m9,74s, user 0m9,06s, sys 0m0,44s</li> </ul> <p><br/>(*) 57.34 / 9.86 = 5,815415822</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 12 May 2014 00:00:00 +0000 PHP Benelux 2014 //blog.cppse.nl/phpbenelux-2014<div class="datestamp">April 22 2014</div> <div class="h1"><a href="//blog.cppse.nl/phpbenelux-2014"> <h1 id="phpbenelux2014">PHP Benelux 2014</h1> </a></div> <h2 id="solvingthepuzzleatphpbenelux2014">Solving the puzzle at PHPBenelux 2014</h2> <p><style type="text/css"> .div_1 { float: left; } .div_1 img { height: 145px !important; width: 200px !important; margin-right: 10px; margin-top: -5px; } </style></p> <div class="div_1"><a href="//cdn.cppse.nl/84-solving.png"><img border="0" src="//cdn.cppse.nl/84-thumb-solving.png" /></a></div> <p>Last conference at the Dutch PHP Conference (DPC) I wrote a <a href="/dutch-php-conference-2013">summary</a> (in Dutch). This time I couldn't find the time, I took notes though, but I was/am too lazy. For PHP Benelux 2014 experiences you can read on <a href="http://conference.phpbenelux.eu/2014/">other people their blogs</a>, for example the one David wrote at <a href="http://www.enrise.com/2014/01/phpbnl14-bumps-up/">his blog</a>. And if you're looking for sheets, chances are you can find them at the event's <a href="http://joind.in/event/view/1509">joind.in page</a>.</p> <p>I made one big mistake at PHPBenelux, that was missing (by accident (don't ask why :p)) Ross Tuck's talk. Was really curious about that one. Of the other talks I especially liked Bastian Hofmann's talk "<a href="http://marijnkoesen.nl/">Marrying front with back end</a>". His talk at DPC was also very good IIRC.</p> <h2 id="dutchweballiancepuzzle">Dutch web alliance puzzle</h2> <p>An old collegue of mine (<a href="http://marijnkoesen.nl/">Marijn</a>) and I decided to programmatically solve the puzzle created for the event there DWA. You can find the puzzle instructions in the photo I took (see fig.2.) We heard a rumour that you could win an ipad if you solved it. Unfortunately that wasn't true <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. In short you have to place all cubes side-by-side in such a way that rotating them combined should yield four unique labels each rotation step. There is only one possible solution.</p> <p>We decided both on different approaches and see who would solve it first. Challenge accepted! I lost unfortunately, I even had to abandon my first approach and made the switch to semi-brute-force as soon as I gained more insight in how it could be solved more easily this way. Marijn approached it brute force from the start.</p> <p>I gave each cube a number <code>{1, 2, 3, 4}</code>, and each sidename a letter <code>{a, b, c, d, x, y}</code>. Next I defined for each cube three directions <code>{a, b, c, d}</code>, <code>{x, b, y, d}</code> and <code>{x, c, y, a}</code> (see fig.1.). All these directions can also be in reverse (<code>{d, c, b, a}</code>, ...).</p> <p><center><a href="//cdn.cppse.nl/84-puzzle.png"><img border="0" src="//cdn.cppse.nl/84-thumb-puzzle.png" /></a> <a href="//cdn.cppse.nl/84-puzzle1.png"><img border="0" src="//cdn.cppse.nl/84-thumb-puzzle1.png" /></a></center> <center><i style="margin-right: 50px;">fig.1. possible directions</i><i>fig.2. puzzle instructions</i></center></p> <p>My first idea was to place a cube on a side randomly, choosing for each cube a random direction to rotate in, and try four rotation steps. If in all steps the pictures were unique that would be a solution. I made a mistake in the implementation, totally forgot that <code>{x, c, y, a}</code> is also a possible sequence. What also didn't help is that I misread the instructions at first, thinking all sides had to be <em>equal</em> (instead of not-equal), which isn't possible if you take a good look at the actual cubes. I guess beer + coding isn't always a good combination.</p> <p>My second approach was simply, generate "layouts" for each cube's three directions, i.e. for <code>{a, b, c, d}</code>:</p> <ul> <li><code>{a, b, c, d}</code></li> <li><code>{b, c, d, a}</code></li> <li><code>{c, d, a, b}</code></li> <li><code>{d, a, b, c}</code></li> <li><code>{d, c, b, a}</code></li> <li><code>{c, b, a, d}</code></li> <li><code>{b, a, d, c}</code></li> <li><code>{a, d, c, d}</code></li> </ul> <p>For each cube a, b, c, d mean different side types (the different logos, php, phpbenelux, dwa, phpelephant). I simply compare every combination, and filter out the duplicate results. You initially get eight solutions, because every solution you can start at the first, second, third or fourth "step" of the solution, and eight because of the extra "times two" multiplier you get for forward versus backward rotation.</p> <h2 id="solverinc">Solver in C++</h2> <p>Code also available on <a href="http://ideone.com/ttFYMD">http://ideone.com/ttFYMD</a>.</p> <pre style="height: 600px;" id="editor4">#include &lt;iostream&gt; #include &lt;algorithm&gt; #include &lt;stdexcept&gt; #include &lt;string&gt; #include &lt;sstream&gt; #include &lt;vector&gt; #include &lt;array&gt; #include &lt;iomanip&gt; enum class sidename { a, b, c, d, x, y }; enum class sidetype { phpbenelux, php, phpelephant, dwa }; std::string sidename_str(sidename sn) { switch (sn) { case sidename::a: return &quot;a&quot;; case sidename::b: return &quot;b&quot;; case sidename::c: return &quot;c&quot;; case sidename::d: return &quot;d&quot;; case sidename::x: return &quot;x&quot;; case sidename::y: return &quot;y&quot;; } throw std::runtime_error{&quot;invalid sidename&quot;}; } std::string sidetype_str(sidetype st) { switch (st) { case sidetype::phpbenelux: return &quot;phpbenelux&quot;; case sidetype::php: return &quot;php&quot;; case sidetype::phpelephant: return &quot;phpelephant&quot;; case sidetype::dwa: return &quot;dwa&quot;; } throw std::runtime_error{&quot;invalid sidetype&quot;}; } /** * cubes define 6 side types (or pictures) * * cubes calculate for themselves all possible layouts, meaning * if you rotate them into some direction, you get a side, followed by side+1, side+2, side+3. * there are a few possibilities: reverse order, and in each layout (or &quot;path&quot;) you can start * rotating the cube on side, side+1, side+2 or side+4 (starting point &quot;shifts&quot;). */ class cube { public: cube(sidetype a, sidetype b, sidetype c, sidetype d, sidetype x, sidetype y) : a_{a}, b_{b}, c_{c}, d_{d}, x_{x}, y_{y}, directionnames_({{ {sidename::a, sidename::b, sidename::c, sidename::d}, {sidename::x, sidename::b, sidename::y, sidename::d}, {sidename::a, sidename::y, sidename::c, sidename::x} }}), directions_({{ {a, b, c, d}, {x, b, y, d}, {a, y, c, x} }}) { for (int i=0; i&lt;4; i++) { for (auto &amp;sides : directions_) { // normal insert layouts_.push_back(sides); // reverse insert auto sidesrev = sides; std::reverse(std::begin(sidesrev), std::end(sidesrev)); layouts_.push_back(sidesrev); // shift all sidetype temp = sides[0]; for (int i=1; i&lt;=3; i++) sides[i - 1] = sides[i]; sides[3] = temp; } } } const std::vector&lt;std::array&lt;sidetype, 4&gt;&gt; &amp; layouts() { return layouts_; } private: /** * This is how I labeled each sidetype: * * X = a * X X X = x b y * X = c * X = d */ sidetype a_; sidetype b_; sidetype c_; sidetype d_; sidetype x_; sidetype y_; std::array&lt;std::array&lt;sidename, 4&gt;, 3&gt; directionnames_; std::array&lt;std::array&lt;sidetype, 4&gt;, 3&gt; directions_; std::vector&lt;std::array&lt;sidetype, 4&gt;&gt; layouts_; }; /** * helper class that can see if a given solution is a duplicate from a previous solution * * if you have a solution that is simply the same one, but rotating in a different direction * is not really a new solution. also note the four possible starting point in each layout/path. * so it will check if duplicates exist in both forward and backward directions, and for each * possible four shifts */ class solutions { public: solutions() {} bool is_dupe(std::array&lt;std::array&lt;sidetype, 4&gt;, 4&gt; temp2) { // Check if found solution isn't a duplicate bool duplicate = false; for (auto &amp;solution : solutions_) { for (int j=0; j&lt;8; j++) { duplicate = true; int sidenum = 0; for (auto &amp;side : solution) { auto &amp;temp = temp2[sidenum++]; int count = 0; int offset = j % 4; if (j &lt; 4) { // Check if they are duplicates, as we use offsets of +0, +1, +2, +3 we can // detect shifted duplicate results. for (auto i = side.begin(); i != side.end(); i++) { duplicate = duplicate &amp;&amp; temp[(count + offset) % 4] == *i; count++; } } else { // Check if they are duplicates simply in reverse order, also with the // detect for shifted duplicates. for (auto i = side.rbegin(); i != side.rend(); i++) { duplicate = duplicate &amp;&amp; temp[(count + offset) % 4] == *i; count++; } } } if (duplicate) return true; } } // Remember found solution, for duplicates checking solutions_.push_back(temp2); return false; } private: std::vector&lt;std::array&lt;std::array&lt;sidetype, 4&gt;, 4&gt;&gt; solutions_; }; int main (int argc, char *argv[]) { /* * on the sheet: * * cube 4 (sideways) * * cube 1, 2, 3 */ cube one{ sidetype::dwa, sidetype::phpelephant, sidetype::phpbenelux, sidetype::dwa, sidetype::php, sidetype::phpbenelux}; cube two{ sidetype::phpelephant, sidetype::phpbenelux, sidetype::phpbenelux, sidetype::phpbenelux, sidetype::php, sidetype::dwa}; cube three{ sidetype::phpbenelux, sidetype::dwa, sidetype::phpelephant, sidetype::php, sidetype::dwa, sidetype::phpelephant}; cube four{ sidetype::php, sidetype::phpelephant, sidetype::phpbenelux, sidetype::phpelephant, sidetype::dwa, sidetype::php}; solutions solution; for (auto &amp;cube1sides : one.layouts()) { for (auto &amp;cube2sides : two.layouts()) { for (auto &amp;cube3sides : three.layouts()) { for (auto &amp;cube4sides : four.layouts()) { // Pictures have to be unique on each four cubes to be considered a unique solution.. bool flag = false; for (int i=0; i&lt;4; i++) { // .. Also on each four rotations of course flag = flag || (!(cube1sides[i] != cube2sides[i] &amp;&amp; cube1sides[i] != cube3sides[i] &amp;&amp; cube1sides[i] != cube4sides[i] &amp;&amp; cube2sides[i] != cube3sides[i] &amp;&amp; cube2sides[i] != cube4sides[i] &amp;&amp; cube3sides[i] != cube4sides[i])); } if (!flag){ // Skip duplicate solutions if (solution.is_dupe({cube1sides, cube2sides, cube3sides, cube4sides})) { continue; } // Print the result std::cout &lt;&lt; &quot;The cube-layout for the solution:&quot; &lt;&lt; std::endl &lt;&lt; std::endl; static auto print = [](const std::string &amp;cube, decltype(cube1sides) &amp;sides) { std::cout &lt;&lt; cube &lt;&lt; &quot;: &quot; &lt;&lt; &quot; front: &quot; &lt;&lt; std::setw(15) &lt;&lt; sidetype_str(sides[0]) &lt;&lt; &quot;, &quot; &lt;&lt; &quot; up: &quot; &lt;&lt; std::setw(15) &lt;&lt; sidetype_str(sides[1]) &lt;&lt; &quot;, &quot; &lt;&lt; &quot; top: &quot; &lt;&lt; std::setw(15) &lt;&lt; sidetype_str(sides[2]) &lt;&lt; &quot;, &quot; &lt;&lt; &quot; back: &quot; &lt;&lt; std::setw(15) &lt;&lt; sidetype_str(sides[3]) &lt;&lt; std::endl; }; print(&quot;cube #1&quot;, cube1sides); print(&quot;cube #2&quot;, cube2sides); print(&quot;cube #3&quot;, cube3sides); print(&quot;cube #4&quot;, cube4sides); } }}}} } </pre> <script type="text/javascript">var ld = ld || [];ld.push(function () { var editor = ace.edit("editor4"); editor.setTheme("ace/theme/dreamweaver"); editor.getSession().setMode("ace/mode/c_cpp");});</script> <p>Output:</p> <pre> cube #1: front: php, up: phpelephant, top: phpbenelux, back: dwa cube #2: front: phpbenelux, up: dwa, top: phpelephant, back: php cube #3: front: phpelephant, up: phpbenelux, top: dwa, back: phpelephant cube #4: front: dwa, up: php, top: php, back: phpbenelux </pre> <p><center><a href="//cdn.cppse.nl/84-cubes.png"><img border="0" src="//cdn.cppse.nl/84-thumb-cubes.png" /></a></center></p> <p>Performance is on my pc 0,00202 seconds on average per run (see <a href="/84-perf.txt">perf.txt</a>). The average is 0,00181 seconds, 11.60% faster, if we stop processing when we find the 'first' solution.</p> <p>I still think my first idea should also work and I'm curious if that "random brute-force" algorithm would on average find a solution faster compared to finding the first solution in my current solver (in terms of required compares). <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Tue, 22 Apr 2014 00:00:00 +0000 Use cache in http proxy for debugging in webdevelopment //blog.cppse.nl/live-edit-cache-in-http-debugging-proxy<div class="datestamp">February 5 2014</div> <div class="h1"><a href="//blog.cppse.nl/live-edit-cache-in-http-debugging-proxy"> <h1 id="usecacheinhttpproxyfordebugginginwebdevelopment">Use cache in http proxy for debugging in webdevelopment</h1> </a></div> <h2 id="servingspecificrequestsfromamutablecache">Serving specific requests from a mutable cache</h2> <p>You can record requests now in a new tab "Cache", and you can choose to use this cache per request. Any such request will be immediately returned from the cache (instead of being proxied).</p> <p>The cool thing is that you can modify the cache, so modifying CSS / Javascript is easy for example. I wanted this feature so I can debug some stuff on environments other than development. There are other ways to do this, but I think this is very convenient.</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/83-videothumb.png); width: 750px; height:487px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="487"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//blog.cppse.nl/videos/wxhttpproxy.mp4&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="487" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//blog.cppse.nl/videos/wxhttpproxy.mp4&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <h2 id="someusecases">Some use cases</h2> <ul> <li>Change <code>&lt;script src=...</code> includes to URL's from production to your dev environment, to simply test javascript on production pages.</li> <li>Change javascript includes to their unminified / deobfuscated versions..</li> <li>Add 'debugger' / console.log's to the source.</li> <li>I've used it to more conveniently debug www.google-analytics.com/ga.js (an obfuscated javascript), by de-obfuscating it and using the step debugger in chrome.</li> </ul> <p>By the way, a cool way to set a breakpoint in Javascript on specific function calls could be this. In <code>ga.js</code> there is a <code>_gaq</code> array, with a push function <code>_gaq.push([something]);</code>, I simply added this with wxhttpproxy in ga.js:</p> <pre><code>var foo = _gaq.push; _gaq.push = function () { debugger; &lt;&lt;&lt;&lt;&lt; and the debugger will intercept, you can view the stacktrace return foo.apply(_gaq, arguments); }</code></pre> <h2 id="featuresroadmap">Features / Roadmap</h2> <p>Note that the wxhttpproxy will intelligently modify the Content-Length header in case you modify the content. Content-Encodings gzip/deflate are not yet supported, for the time-being the proxy will garble the Accept header (by replacing gzip -> nogz, and deflate -> noflate). This is a workaround, that makes sure gzip/deflate isn't used.</p> <p>Supported for chunked encoding should be added (easy), you can already edit it manually, just remove the chunked header, the hex numbers and add a Content-Length to work around it.</p> <p>Real support for gzip / deflat should be added. I already have code for gzip / deflate support laying around somewhere.</p> <h2 id="download">Download</h2> <p>As an Ubuntu/debian package <a href="/free-http-proxy-for-debugging-purposes#download">available here</a>.</p> <ul> <li>It uses wx2.8. In wx3.0 the event model has changed a bit, and I believe there is an error in the event handling w/regards to sockets. I will post some more details on this later, and if I have the time see if I can fix it..</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 05 Feb 2014 00:00:00 +0000 Install NVIDIA proprietary drivers in Ubuntu 13.10 or Linux mint 15/16 //blog.cppse.nl/nvidia-proprietary-drivers-instead-of-nouveau<div class="datestamp">December 26 2013</div> <div class="h1"><a href="//blog.cppse.nl/nvidia-proprietary-drivers-instead-of-nouveau"> <h1 id="installnvidiaproprietarydriversinubuntu13.10orlinuxmint1516">Install NVIDIA proprietary drivers in Ubuntu 13.10 or Linux mint 15/16</h1> </a></div> <p><center style="float: right;"> <a href="//cdn.cppse.nl/82-Quadro_K600_F.png"><img border="0" src="//cdn.cppse.nl/82-thumb-Quadro_K600_F.png" /></a> </center></p> <p>My super awesome <code>NVIDIA Quadro K600</code> doesn't work properly with the default video drivers in Linux mint 15, 16 or Ubuntu 13.10. Especially in mint it was especially unstable. In Ubuntu everything seems fine for a few days, until the GPU finally crashed as well.</p> <h2 id="linuxmint1516">Linux mint 15 / 16</h2> <p>You disable the default driver called nouveau, to be loaded by the kernel with <code>nouveau.blacklist=1</code>. </p> <p>In mint I've tried editing GRUB_CMDLINE_LINUX etc. in <code>/usr/share/grub/default/grub</code> and all of <code>/etc/grub.d/*</code>. Somehow <code>update-grub</code> didn't parse it, I was not so patient, so I ended up simply editting <code>/boot/grub/grub.cfg</code>.</p> <pre><code>trigen@Firefly21 ~ $ vim /boot/grub/grub.cfg ... :g/^\s*linux linux /boot/vmlinuz-3.8.0-19-generic root=UUID=60f8754f-f688-461e-b120-bf8402c1e2a9 ro nouveau.blacklist=1 linux /boot/vmlinuz-3.8.0-19-generic root=UUID=60f8754f-f688-461e-b120-bf8402c1e2a9 ro recovery nomodeset nouveau.blacklist=1 linux16 /boot/memtest86+.bin linux16 /boot/memtest86+.bin console=ttyS0,115200n8 Press ENTER or type command to continue</code></pre> <p><strong>Reboot the system. <code>CTRL+ALT+F1</code>, login, <code>sudo service mdm stop</code>, <code>./NVIDIA-Linux-x86_64-331.20.run</code> and follow instructions. Reboot.</strong></p> <h2 id="ubuntu13.10">Ubuntu 13.10</h2> <p>In ubuntu I attempted to directly edit <code>/boot/grub/grub.cfg</code> again. Adding the blacklist parameter, somehow this failed, the NVIDIA installer still complaining about nouveau being loaded.</p> <p>So I attempted the 'normal approach' again: <code>vim /etc/default/grub</code>, modified this line: <code>GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nomodeset nouveau.blacklist=1"</code>. I also googled and found <a href="http://askubuntu.com/a/210497">this answer on stackoverflow</a>, suggesting the nomodeset is necessary as well. (So I added both). <code>sudo update-grub</code> and EAT REBOOT INSTALL REPEAT.</p> <p><strong>Reboot the system. <code>CTRL+ALT+F1</code>, login, <code>sudo service lightdm stop</code>, <code>./NVIDIA-Linux-x86_64-331.20.run</code> and follow instructions. Reboot.</strong></p> <h2 id="somenotes:installgrubintospecificpartition">Some notes: Install grub into specific partition</h2> <p>This is a "Note to self", how to install grub for specific partition (<a href="http://community.linuxmint.com/tutorial/view/245">source</a>). I needed this command fixing my dual boot (linux and windows on one ssd).</p> <pre><code>sudo mount /dev/sda5 /mnt sudo grub-install --root-directory=/mnt/ /dev/sda sudo update-grub</code></pre> <p>Somehow linux mint f*cked up my boot to windows 8 partition. It had some problems recognizing my partition table or something. (At work I have the exact same setup, and there were no problems.) I ended up fixing it with the above command, and from windows (had to restore an image) using <a href="http://apcmag.com/how-to-dual-boot-windows-8-and-linux.htm">this tutorial</a> that uses <a href="http://neosmart.net/EasyBCD/">EasyBCD</a>.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 26 Dec 2013 00:00:00 +0000 A proper ksh profile with proper handling of history over subshells. //blog.cppse.nl/ksh-profile<div class="datestamp">December 25 2013</div> <div class="h1"><a href="//blog.cppse.nl/ksh-profile"> <h1 id="aproperkshprofilewithproperhandlingofhistoryoversubshells.">A proper ksh profile with proper handling of history over subshells.</h1> </a></div> <p>In shell scripting I prefer the Kornshell. A while ago I experimented with <a href="https://github.com/robbyrussell/oh-my-zsh">"oh my zsh"</a>, but I switched back to ksh. Their auto completion for program commands is really unsurpassed (tab completion on program parameters for grep for example). The auto-<strong>in</strong>correct however, is quite annoying <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. There is also a git plugin that visualized the active git branch in the <code>$PS1</code> prompt. I liked these features and I want to add them to ksh.</p> <p><center><img border="0" src="//cdn.cppse.nl/81-figure1.gif" /><br/> <em>Apparently I like old things</em></center></p> <p>I fixed a few things for ksh in my <code>.kshrc</code> at <a href="https://bitbucket.org/rayburgemeestre/superprofile/src/master/.kshrc?at=master">bitbucket</a>.</p> <h2 id="separatedhistoryamongstthedifferentkshshells.">Separated history amongst the different ksh shells.</h2> <p>My problem with the default behaviour: by default history is globally shared amongst all shells. I tend to work in a screen and do different stuff in each buffer. So it's annoying if stuff from one buffer magically appears in the other buffer. </p> <p>So what I do is I make sure there is one history file <code>~/.ksh_history</code> which contains all history from all shells. When starting a new shell I copy this file into a history file specific for that ("sub")shell, i.e. <code>~/.ksh_history_files/&lt;shellpid&gt;</code>. Each new shell does this.</p> <p>When a shell is started all history files from <code>~/.ksh_history_files/*</code> are merged back into <code>~/.ksh_history</code>. And the ones that are no longer in use (shells have exited) are removed. This is done with a simple <code>lsof</code> call.</p> <p>Commands are processed through a simply ksh function that makes sure all history lines are unique, without changing the order.</p> <p>Some funny caveats were:</p> <ul> <li>A history file should start with the character sequence <code>\x81\01</code>.</li> <li>Each command in history file should end with a <code>\x00</code> character. </li> </ul> <h2 id="apromptthatembedscurrentgitbranch">A prompt that embeds current git branch</h2> <p>If you are inside a git clone).<br/>Maybe the oh-my-zsh git integration is more advanced, no idea. Luckily this visualization in <code>$PS1</code> is very fast.</p> <!-- you can use a code tag or a pre tag* --> <pre><code data-language="bash">trigen@Firefly21:/projects> cd smashbattle trigen@Firefly21:/projects/smashbattle[network_multiplayer]> git branch master * network_multiplayer trigen@Firefly21:/projects/smashbattle[network_multiplayer]> </code></pre> <h2 id="fixcdwithnoparamstobetheequivalentofcdagain">Fix "cd" (with no params) to be the equivalent of "cd ~" again</h2> <p>Fixed with a simple alias around cd provided by <code>/usr/share/ksh/functions/dirs</code>. For <code>dirs</code> usage <a href="//blog.cppse.nl/ksh-pushd-popd-dirs#found:funpushdpopddirsbydavidg.kornhimself">check here</a>.</p> <pre><code data-language="bash">trigen@Firefly21:/usr/local/src/wxWidgets-3.0.0> cd <<<< this didn't work on Ubuntu 13.10 anyways. trigen@Firefly21:/home/trigen> </code></pre> <h2 id="integratedmylaunchertooliwroteforwindowsalongtimeago.">Integrated my "launcher tool" I wrote for windows a long time ago.</h2> <p>The browser is assumed to be "chromium-browser" (sudo apt-get install chromium-browser).</p> <p><center style="float:right;"> <a href="//cdn.cppse.nl/81-dan_flavin.png"><img border="0" src="//cdn.cppse.nl/81-thumb-dan_flavin.png" /></a> <br/><em>The result for <code>gi dan flavin</code> is opened in a new chromium tab.</em> </center></p> <ul> <li><code>i &lt;url&gt;</code> - open url in browser</li> <li><code>g &lt;search terms&gt;</code> - search with google.com</li> <li><code>gi &lt;search terms&gt;</code> - search with google images</li> <li><code>gv &lt;search terms&gt;</code> - search with google videos</li> <li><code>gnl &lt;search terms&gt;</code> - search with google.nl</li> <li><code>gs &lt;search term&gt;</code> - search google scholar</li> <li><code>w &lt;search term&gt;</code> - search wikipedia</li> <li><code>wa &lt;search terms&gt;</code> - search wolfram alpha</li> <li><code>yv &lt;search terms&gt;</code> - search youtube</li> <li><code>y &lt;search terms&gt;</code> - search yahoo</li> <li><code>yi &lt;search terms&gt;</code> - search yahoo images</li> <li><code>yv &lt;search terms&gt;</code> - search yahoo videos</li> <li><code>imdb &lt;search terms&gt;</code> - search imdb</li> <li><code>h &lt;search term&gt;</code> - search hyperdictionary</li> <li><code>v &lt;search term&gt;</code> - search vandale (dutch dictionary)</li> </ul> <p>Usage example: Open run menu (<code>ALT+F2</code>). Then type <code>gi dan flavin</code> to get the example result from screenshot.</p> <p>I had to abandon my pure-ksh-functions approach to make the launcher commands available everywhere. So you have call <code>install_launcher</code> as root to install the shortcuts as scripts in <code>/usr/local/bin/</code>. Ubuntu does not respect shell functions in run unfortunately.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 25 Dec 2013 00:00:00 +0000 Free HTTP proxy for debugging purposes with GUI //blog.cppse.nl/free-http-proxy-for-debugging-purposes<div class="datestamp">December 3 2013</div> <div class="h1"><a href="//blog.cppse.nl/free-http-proxy-for-debugging-purposes"> <h1 id="freehttpproxyfordebuggingpurposeswithgui">Free HTTP proxy for debugging purposes with GUI</h1> </a></div> <p>As a free alternative for <a href="http://www.charlesproxy.com/">Charles proxy</a> and/or <a href="http://fiddler2.com/">Fiddler</a> I developed wxHttpProxy in one (long) night! Unfortunately I had to spend the next day making it stable, and support https traffic as well <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>The mentioned alternatives are really great by the way, and have quite a few additional features, I just needed a simple http proxy that I could use in <a href="//blog.cppse.nl/meta-log-monitor">metalogmon</a>, that's why I developed my own. It is opensource, find it on <a href="https://bitbucket.org/rayburgemeestre/wxhttpproxy/">bitbucket</a>.</p> <p><center><a href="//cdn.cppse.nl/80-screenshot.png"><img border="0" src="//cdn.cppse.nl/80-large-thumb-screenshot.png" /></a></center></p> <p>The idea is to integrate this http proxy into <a href="//blog.cppse.nl/meta-log-monitor">metalogmon</a>, so that you would be able to process your output through javascript. The reason that request/response are both first class citizens is that where I currently develop software I plan to use this proxy between all subsystems communication, and here multiple services communicate with eachother using REST calls. </p> <p>I want to visualize the call sequences/hierarchy, like [A -> B -> C], [A &lt;- B &lt;- C]. And not group them together like: [A &lt;> B], [B &lt;> C].</p> <h2 id="installbinary64bitandubuntudebianbasedsystemonlyaiddownloada">Install binary (64 bit and ubuntu/debian based system only) <a id="download"></a></h2> <p>Install my key</p> <pre><code>wget -O - http://cppse.nl/apt/keyFile | sudo apt-key add -</code></pre> <p>Add my repo to apt's sources.list.</p> <pre><code>sudo sh -c "echo deb http://cppse.nl/apt/dists/stable/main/binary / &gt;&gt; /etc/apt/sources.list"</code></pre> <p>Install the package:</p> <pre><code>sudo aptitude update sudo aptitude install wxhttpproxy</code></pre> <h2 id="usage">Usage</h2> <pre><code>wxHttpProxy</code></pre> <p>Tip of the day, add a shortcut for starting the proxy (like WINKEY+2 or something)</p> <h2 id="availableshortcuts">Available shortcuts</h2> <ul> <li>alt+i - Toggle intercept mode (capture/display request/response, or be silent)</li> <li>alt+c - Clear buffer(s)</li> </ul> <p><style type="text/css"> .logsm, .logsm img {float:right; width: 150px !important; height: 70px !important;} </style></p> <div class="logsm"><a href="//cdn.cppse.nl/80-catch-logo-small.png"><img border="0" src="//cdn.cppse.nl/80-thumb-catch-logo-small.png" /></a></div> <h2 id="catchlibrary">Catch library</h2> <p>With <a href="https://github.com/philsquared/Catch">Catch</a> I developed a few <a href="https://bitbucket.org/rayburgemeestre/socketbuffer">helper classes</a> in a test-driven way, it's a lightweight single-header c++ library for creating unit tests. Really liked working with it and look forward to using it in the future some more.</p> <h2 id="theaptrepository">The apt repository</h2> <p>The shiny new apt repository was also really easy to setup, once you have made your packages, simply follow <a href="https://help.ubuntu.com/community/CreateAuthenticatedRepository">this wiki</a>.</p> <h2 id="otherplatforms">Other platforms</h2> <p>Should you wish to compile it for windows, it is <a href="http://www.wxwidgets.org/downloads/">wxWidgets 3.0.0</a> based and should already be cross-platform, just didn't test it yet. Use <a href="http://www.anthemion.co.uk/dialogblocks/">Dialogblocks</a> to compile it (and also have it compile wxWidgets for you (as that's just convenient <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />)).</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Tue, 03 Dec 2013 00:00:00 +0000 Away- nicknames coloring in nicklist mIRC //blog.cppse.nl/away-nickname-colors-mirc<div class="datestamp">October 20 2013</div> <div class="h1"><a href="//blog.cppse.nl/away-nickname-colors-mirc"> <h1 id="away-nicknamescoloringinnicklistmirc">Away- nicknames coloring in nicklist mIRC</h1> </a></div> <h2 id="implementedthisfeatureisawinxchat">Implemented this feature I saw in XChat</h2> <p>Yes, having switched to Linux and using <a href="http://xchat.org/">XChat</a>, I now first realize how slow <a href="http://www.mirc.com/">mIRC</a> is (connecting to my bouncer, joining all kinds of channels, replaying scrollback, etc.-- mirc takes a few seconds, where XChat is "instant"), but I don't care. It's still my client of choice for Windows. On Tweakers IRC network, it is custom for a lot of people to indicate their away status with <code>/nick &lt;nickname&gt;|afk</code> and such. That makes sense, but XChat does something "smart", it periodically <code>/who</code>'s all channels to know whom are away so it can color those in light grey in the nicklist. Cool feature I thought! So I "extended" <a href="//blog.cppse.nl/nickname-colouring-mirc-scripting">my nicklisting coloring mirc script</a>, by adding <code>aways.ini</code>. It's no longer necessary to keep changing your nickname with an away status (on freenode it isn't even allowed in some channels <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />).</p> <p><center> <a href="//cdn.cppse.nl/77-Screenshotfrom2013-10-2101-10-15.png"><img border="0" src="//cdn.cppse.nl/77-large-thumb-Screenshotfrom2013-10-2101-10-15.png" /></a> <br/> <i>Away nicks in light-grey in <a href="http://xchat.org/">XChat</a></i> <br/><br/> </center></p> <h2 id="thesamefeatureinmirc">The same feature in mIRC</h2> <p>It's a pity that such an inefficient implementation is required ("pull"ing /who <channel> periodically will result in a lot of extra network data), but the IRC protocol does not currently provide something that "push"es away info more intelligently. You can <a href="https://bitbucket.org/rayburgemeestre/mircscripts/src">download the script here</a>, note that <code>aways.ini</code> depends on <code>nicklist.ini</code>, also downloadable there. (<code>nicklist.ini</code> still works without <code>aways.ini</code>, it will then use light grey at random instead.)</p> <p><center> <a href="//cdn.cppse.nl/77-mode2.png"><img border="0" src="//cdn.cppse.nl/77-large-thumb-mode2.png" /></a> <br/> <i>Away nicks in light-grey</i> <br/><br/> <a href="//cdn.cppse.nl/77-mode1.png"><img border="0" src="//cdn.cppse.nl/77-large-thumb-mode1.png" /></a> <br/> <i>Away nicks in light-grey, other nicks colored.</i> <br/><br/> </center></p> <div style="display:none;"> <a href="//cdn.cppse.nl/77-Screen Shot 2014-12-05 at 00.21.45.png"><img border="0" src="//cdn.cppse.nl/77-thumb-Screen Shot 2014-12-05 at 00.21.45.png" /></a> </div> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 20 Oct 2013 00:00:00 +0000 Visual studio shortcut keys / settings //blog.cppse.nl/visual-studio-shortcut-keys<div class="datestamp">October 7 2013</div> <div class="h1"><a href="//blog.cppse.nl/visual-studio-shortcut-keys"> <h1 id="visualstudioshortcutkeyssettings">Visual studio shortcut keys / settings</h1> </a></div> <p>These are the ones I keep forgetting about:</p> <ul> <li>Enabling line numbers: Tools, Options, Text Editor, All Languages, Show Line Numbers (source: http://stackoverflow.com/questions/5905687/can-i-see-line-numbers-in-the-vs2010-editor)</li> <li>Toggle whitespace/tabs: CTRL+R, CTRL+W. (source: http://stackoverflow.com/questions/4065815/how-to-turn-off-showing-whitespace-characters-in-visual-studio-ide)</li> <li>Navigate cursor to previous+next position: CTRL+- and forward CTRL+SHIFT+-.</li> </ul> <p>These are a few you can't do without:</p> <ul> <li>Step into function: F12 (CTRL+- to return cursor to previous position)</li> </ul> <p>If you can afford it you can't do without Visual Assist X either.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 07 Oct 2013 00:00:00 +0000 Raspberry pi camera streaming with crtmpserver test //blog.cppse.nl/raspbpi<div class="datestamp">September 18 2013</div> <div class="h1"><a href="//blog.cppse.nl/raspbpi"> <h1 id="raspberrypicamerastreamingwithcrtmpservertest">Raspberry pi camera streaming with crtmpserver test</h1> </a></div> <script type="text/javascript" src="//cdn.cppse.nl/jwplayer.js"></script> <script type="text/javascript"> function f75jwpl() { jwplayer("myElement75").setup({ // file: "rtmp://cppse.nl/app/flv:/video", // works with python file: "rtmp://cppse.nl/appflv:video", file: "rtmp://cppse.nl/flvplayback/flv:video2", autostart: true, width: 800, height: 600 }); } var ld = ld || []; ld.push(function() { if (gl_react) { var old_react = gl_react; gl_react = function () { var o = document.getElementById('myElement_wrapper'); if (o) o.className = 'playerElement'; old_react();}; gl_react(); for (var i=1; i<10; i++) { setTimeout(gl_react, i * 1000); } } }); </script> <p></center></p> <p>Click the play button to start stream. (Will only work if I have the script running on my raspberry pi though. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" width="16" height="16" class="smiley sN" />)</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/68-thumb.jpg); width: 800px; height:600px; display:block;"> <!--</p> <div id="myElement75" class="playerElement">Loading the player...</div> <p><img src="//cdn.cppse.nl/does-not-exist.jpg" onerror="f75jwpl()" width=1 height=1 /> --> </a> </center></p> <h2 id="raspividffmpeg">raspivid + ffmpeg</h2> <p><center><a href="//cdn.cppse.nl/75-20130918_091244.jpg"><img border="0" src="//cdn.cppse.nl/75-thumb-20130918_091244.jpg" /></a></center></p> <pre><code># test chaining raspivid and ffmpeg raspivid -t 5000 -w 960 -h 540 -fps 25 -b 500000 -vf -o - | ffmpeg -i - -vcodec copy -an -r 25 -f flv test.flv # stream to tcp endpoint raspivid -n -t 0 -w 1920 -h 1080 -fps 25 -b 2000000 -o - | ffmpeg -i - -vcodec copy -an -f flv -metadata streamName=video2 tcp://cppse.nl:6666</code></pre> <p>Port 6666 is the same with the sample that is delivered with crtmpserver (the flvplayback.lua sample).</p> <h2 id="turnoffpicameraled">Turn off pi camera led</h2> <pre><code>#!/usr/bin/env python import time import RPi.GPIO as GPIO # Use GPIO numbering GPIO.setmode(GPIO.BCM) # Set GPIO for camera LED CAMLED = 5 # Set GPIO to output GPIO.setup(CAMLED, GPIO.OUT, initial=False) # Five iterations with half a second # between on and off #for i in range(5): # GPIO.output(CAMLED,True) # On # time.sleep(0.5) # GPIO.output(CAMLED,False) # Off # time.sleep(0.5) GPIO.output(CAMLED,False) # Off</code></pre> <p>Think I got this script from <a href="http://www.raspberrypi-spy.co.uk/2013/05/how-to-disable-the-red-led-on-the-pi-camera-module/">here</a>.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 18 Sep 2013 00:00:00 +0000 Video streaming from code with SFML and ffmpeg //blog.cppse.nl/streaming-from-code<div class="datestamp">September 1 2013</div> <div class="h1"><a href="//blog.cppse.nl/streaming-from-code"> <h1 id="videostreamingfromcodewithsfmlandffmpeg">Video streaming from code with SFML and ffmpeg</h1> </a></div> <h2 id="livevideostreaming">Live video streaming</h2> <p>I was interested in specifically the generation of H264 videos directly from code (without audio). This was something I used to do by generating separate .BMP files that I then convert with mencoder. This costs quite a lot of hdd space and was performance-wise not optimal. Of course I already figured that out, but figuring out how to encode directly to video was something on my To Do list.</p> <p>Believe it or not, I actually had to search a while for libraries and SDK's, before concluding that ffmpeg was the best option. I've had a bad experience with ffmpeg 5 years ago, so I was probably ignoring ffmpeg in the google search results. You can link to ffmpeg's libraries and use them in your own program, they also provide code samples. This made it all pretty easy to setup. So when I completed generating H264 videos from code I wondered how difficult it would be to directly stream video with ffmpeg.</p> <p>One cool application would be to use it in games, f.i. make a small livestream for actual games being played on the server. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>Ffmpeg supports a few protocols, I chose an RTMP server for my setup, which is Flash only. If that works it'll probably work for other protocols as well. The best alternative for HTML5 is probably Apple's HLS Streaming, which actually is pretty awesome, as a side-note from <a href="http://www.longtailvideo.com/support/jw-player/28856/using-apple-hls-streaming/">source</a>:</p> <blockquote> <p><i>If multiple qualities of a stream are available, the player will continuously monitor the current bandwidth and pick the next fragment from the highest quality it can load. This is called Adaptive Streaming:</i> <br/><a href="//cdn.cppse.nl/68-hls-streaming.jpg"><img border="0" src="//cdn.cppse.nl/68-thumb-hls-streaming.jpg" /></a></p> </blockquote> <p>For the videoplayer I chose <a href="http://www.longtailvideo.com/jw-player/">JWplayer</a> version 6.</p> <h2 id="settingupanrtmpserverwithjwplayer">Setting up an rtmp server with Jwplayer</h2> <p>The servers I tested: <a href="http://code.google.com/p/rtmplite/">rtmplite</a> on linux, <a href="http://www.crtmpserver.com/">crtmpserver</a> on linux and <a href="http://www.red5.org/">Red5 Media Server</a> on windows. The linux server I used was a Suse (SLED11) micro EC2 instance on Amazon AWS. But these should all work on linux and windows.</p> <p>RTMP is flash-only, traffics over port 1935.</p> <h2 id="somenotesonusingrtmplitewithjwplayer">Some notes on using rtmplite with JWplayer</h2> <p>Eventually I chose not to use rtmplite, but crtmpserver instead, as you have more output there as to what's going on in the server. rtmplite output is less useful to me, by default it outputs nothing, with verbose you get messages at packet level. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /> rtmplite used quite a bit more of my CPU compared to crtmpserver (not very accurate due to EC2, but crtmpserver at 0.3% CPU versus > 10% CPU for rtmplite) and it does not understand aspect ratio other than 4:3, so 16:9 will be "squished" into 4:3. It's not my intent to be negative about rtmplite, if you're a python guy It's probably very easy to tweak and I must say it works out-of-the-box with a simple <code>python rtmp.py</code>, ready for video at <code>rtmp://servername/app/video</code>.</p> <p>With JWplayer you can view the stream with:</p> <pre class="prettyprint lang-js"><code data-language="javascript">jwplayer(&quot;myElement&quot;).setup({ file: &quot;rtmp://localhost/appflv:video&quot;, autostart: true, width: 480, height: 320 }); </code></pre> <p>If you attempt to use that config, please note that file URL! It took me an hour to get that correct, in all JWPlayer samples you see <code>rtmp://server/app/flv:video.flv</code> or <code>rtmp://server/app/mp4:video.mp4</code>. The prefix <code>flv:</code> or <code>mp4:</code> being just for JWplayer so that it knows what encoding to expect. As the <code>.mp4</code> or <code>.flv</code> extension in URL's is not mandatory. Eventually JWplayer will strip away <code>flv:</code>. An rtmp url like <code>rtmp://server/app/video</code> consists of an application called <code>app</code> and a video called <code>video</code>. Stripping away the prefix from <code>rtmp://cppse.nl/app/flv:video</code> will provide <code>app/</code> for the application which <em>looks</em> like <code>app</code> but isn't quite the same. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <h2 id="so...crtmpserverthen">So... crtmpserver then!</h2> <p>After compiling you fire it up with default settings with: <code>./crtmpserver/crtmpserver crtmpserver/crtmpserver.lua</code>. You should see something similar to the following output.</p> <pre><code>ip-10-224-83-43:/usr/local/src/crtmpserver/builders/cmake # ./crtmpserver/crtmpserver ./crtmpserver/crtmpserver.lua /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:203 Initialize I/O handlers manager: epoll /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:206 Configure modules /usr/local/src/crtmpserver/sources/thelib/src/configuration/module.cpp:84 Module /usr/local/src/crtmpserver/builders/cmake/applications/appselector/libappselector.so loaded /usr/local/src/crtmpserver/sources/thelib/src/configuration/module.cpp:84 Module /usr/local/src/crtmpserver/builders/cmake/applications/flvplayback/libflvplayback.so loaded /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:212 Plug in the default protocol factory /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:219 Configure factories /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:225 Configure acceptors /usr/local/src/crtmpserver/sources/thelib/src/netio/epoll/iohandlermanager.cpp:100 Handlers count changed: 0-&gt;1 IOHT_ACCEPTOR /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:231 Configure instances /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:237 Start I/O handlers manager: epoll /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:240 Configure applications /usr/local/src/crtmpserver/sources/thelib/src/configuration/module.cpp:177 Application appselector instantiated /usr/local/src/crtmpserver/sources/thelib/src/configuration/module.cpp:177 Application flvplayback instantiated /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:246 Install the quit signal /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:257 +-----------------------------------------------------------------------------+ | Services| +---+---------------+-----+-------------------------+-------------------------+ | c | ip | port| protocol stack name | application name | +---+---------------+-----+-------------------------+-------------------------+ |tcp| 0.0.0.0| 1935| inboundRtmp| appselector| +---+---------------+-----+-------------------------+-------------------------+ /usr/local/src/crtmpserver/sources/crtmpserver/src/crtmpserver.cpp:258 GO! GO! GO! (19664)</code></pre> <p>With the default configuration anyone can stream a video to the RTMP server, as inbound and outbound are over port 1935. I tried separating that with the configs, but I couldn't find any configuration setting to do this, so I hacked something in the source to only allow inbound RTMP for 127.0.0.1 (see <a href="//cdn.cppse.nl/68-patch.txt">patch.txt</a>). With crtmpserver you don't have the URL problem in JWPlayer.</p> <pre class="prettyprint lang-js"><code data-language="javascript">jwplayer(&quot;myElement&quot;).setup({ file: &quot;rtmp://server/flvplayback/flv:video&quot;, autostart: true, width: 480, height: 320 }); </code></pre> <h2 id="sendingvideototheserverwithsendstream">Sending video to the server with "sendstream"</h2> <p>The proof-of-concept tool I set out to make is now a useful test tool you can use to stream a test video with optional audio at a given resolution to a given streaming server. I have compiled it for windows and linux 32 bit (opensuse 12) and also have that in a working chroot for download (~28 mb zipped).</p> <pre><code>usage: sendstream.exe &lt; RTMP URL &gt; [ &lt; width &gt; &lt; height &gt; &lt; sound &gt; &lt; max_fps &gt;] params: width default = 320, height default = 240 pixels, sound default = 1, max_fps default = 25. i.e: sendstream.exe "rtmp://localhost/app/video" 800 600</code></pre> <p>Example output for <code>sendstream.exe rtmp://cppse.nl/flvplayback/video 1920 1080 1</code>:</p> <pre><code>[libmp3lame @ 058a6320] Channel layout not specified Output #0, flv, to 'rtmp://cppse.nl/flvplayback/video': Stream #0:0: Video: flv1, yuv420p, 1920x1080, q=2-31, 100 kb/s, 90k tbn, 20 tbc Stream #0:1: Audio: mp3, 44100 Hz, 2 channels, s16p, 24 kb/s HandShake: client signature does not match! writing frame 0 at 0.044793... +V writing frame 1 at 0.112187... +A+A+A[flv @ 058a2b60] Encoder did not produce pr oper pts, making some up. +A+V writing frame 2 at 0.324518... +A+A+A+V writing frame 3 at 0.813168... +A+A+A+A+A+A+A+A+A+V writing frame 4 at 1.03129... +A+A+A+A+A+A+A+A+A+A+A+A+A+A+A+A+A+A+A+V writing frame 5 at 1.16484... +A+A+A+A+A+A+A+A+A+V writing frame 6 at 1.23609... +A+A+A+A+A+V writing frame 7 at 1.28777... +A+A+A+V writing frame 8 at 1.33955... +A+A+V writing frame 9 at 1.38883... +A+A+V writing frame 10 at 1.43901... +A+V writing frame 11 at 1.48954... +A+A+V writing frame 12 at 1.53965... +A+A+V writing frame 13 at 1.58858... +A+A+V writing frame 14 at 1.6456... +A+A+V writing frame 15 at 1.69497... +A+A+V writing frame 16 at 1.74456... +A+A+V</code></pre> <p>Audio and video are combined with interweaving and if rendering of video frames is relatively slow compared to audio it may require more audio frames in between and vica versa. In the output +A is a written audio and +V a written video frame.</p> <p>I use <a href="http://www.sfml-dev.org/">SFML</a> for generating the graphics and for audio something similar to the example code by ffmpeg. The interweaving part was the most difficult but important thing to get right. This is the easiest version of the code I could come up with that works properly. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /> Something I didn't know in the beginning was that you don't need to keep your framerate of sending frames to the server consistent at a given frames per second-rate.</p> <pre class="prettyprint lang-cpp"><code data-language="c">sfml_init(&amp;pixels, &amp;pixelSize); auto timer = TimerFactory::factory(TimerFactory::Type::WindowsHRTimerImpl); timer-&gt;start(); frame-&gt;pts = timer-&gt;end(); int64_t currentframe = 0; while (true) { sfml_generate_frame(); std::cout &lt;&lt; &quot;writing frame &quot; &lt;&lt; currentframe++ &lt;&lt; &quot; at &quot; &lt;&lt; (timer-&gt;end() / 1000.0) &lt;&lt; &quot;... &quot;; // Make sure to write enough audio frames until the audio timer lines up while (true) { double audio_pts = (audio_st) ? (double)audio_st-&gt;pts.val * audio_st-&gt;time_base.num / audio_st-&gt;time_base.den : 0.0; double video_pts = (video_st) ? (double)video_st-&gt;pts.val * video_st-&gt;time_base.num / video_st-&gt;time_base.den : 0.0; if (audio_pts &gt;= video_pts) break; std::cout &lt;&lt; &quot;+A&quot;; write_audio_frame(oc, audio_st); } // Write video frame write_video_frame(oc, video_st); std::cout &lt;&lt; &quot;+V&quot; &lt;&lt; std::endl; // Set elapsed time as frame time frame-&gt;pts = timer-&gt;end(); } </code></pre> <p>One final side-note if you are going to use sendstream on linux with <a href="http://en.wikipedia.org/wiki/Xvfb">Xvfb</a> (X virtual framebuffer). On SUSE it was a pain to get it working, I ended up installing it on an openSuse VMWare VM where it <em>did</em> work and then made a minimal chroot for it, one that includes Xvfb and sendstream and a <code>run.sh</code> script (you might want to edit that). Not all screen resolutions you can think off that work fine with RTMP server will work, i.e.: I wanted a 980 x 100 resolution video, that didn't work for Xvfb, it does work if you create a larger screen with a more common resolution like 1280 x 768. Also 980 x 100 didn't work for SFML, 1000 x 100 did, probably the OpenGL underneath complaining or something. AFAIK these problems do not exist without Xvfb.</p> <h2 id="theresult">The result</h2> <script type="text/javascript" src="//cdn.cppse.nl/jwplayer.js"></script> <script type="text/javascript"> function f68jwpl() { jwplayer("myElement").setup({ // file: "rtmp://cppse.nl/app/flv:/video", // works with python file: "rtmp://cppse.nl/appflv:video", file: "rtmp://cppse.nl/flvplayback/flv:video", autostart: true, width: 980, height: 300 }); } var gl_react = gl_react || false; if (gl_react) { var old_react = gl_react; gl_react = function () { var o = document.getElementById('myElement_wrapper'); if (o) o.className = 'playerElement'; old_react();}; gl_react(); for (var i=1; i<10; i++) { setTimeout(gl_react, i * 1000); } } </script> <p></center></p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/68-thumb.jpg); width: 980px; height:300px; display:block;"> <!--</p> <div id="myElement" class="playerElement">Loading the player...</div> <p><img src="//cdn.cppse.nl/does-not-exist.jpg" onerror="f68jwpl()" width=1 height=1 /> --> </a> </center></p> <p>Sendstream runs on my webserver capped at 2 FPS and without audio, so that it doesn't hurt the CPU too much.</p> <p>Download: <a href="//cdn.cppse.nl/sendstream-1.0.zip">sendstream-1.0.zip</a> for windows. 11.2MB</p> <p>Download: <a href="//cdn.cppse.nl/sendstream-1.0-chroot.zip">sendstream-1.0-chroot.zip</a> for linux (chroot, use: "chroot /path/to/sendstream /run.sh"). 27.2MB</p> <!-- Todo, sign these with GPG --> <p>a930805b8867232755da195d34587476 *sendstream-1.0-chroot.zip <br/> 6ed8cf5102dbe10c5f4722c80d38cbbd *sendstream-1.0.zip</p> <p>&nbsp;</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 01 Sep 2013 00:00:00 +0000 ![nl][] Dutch PHP Conference 2013 //blog.cppse.nl/dutch-php-conference-2013<div class="datestamp">June 28 2013</div> <div class="h1"><a href="//blog.cppse.nl/dutch-php-conference-2013"> <h1 id="nldutchphpconference2013"><img id="nl" src="//cdn.cppse.nl/nl.png" alt="nl" title="nl" /> Dutch PHP Conference 2013</h1> </a></div> <h2 id="hetthemavandedutchphpconference2013wasrespecteerheteco-systeem.">Het thema van de Dutch PHP Conference 2013 was <em>Respecteer het eco-systeem</em>.</h2> <p>Dag &eacute;&eacute;n begon voor mij (en mijn collega's) 7 juni en Ade Oshineye opende met het pleiten voor <em>User Journeys</em>, de flows over meerdere devices die gebruikers volgen zoals openen van een nieuwsbrief op een telefoon en naar doorklikken naar product om deze uiteindelijk te kopen op een werkstation.</p> <p><center><a href="//cdn.cppse.nl/72-userjourney.jpg"><img border="0" src="//cdn.cppse.nl/72-thumb-userjourney.jpg" /></a></center> <center><span style="font-style: italic; font-size: 80%;">User Journey</span></center></p> <p>Denk ook na over dat een website geopend in een <code>LinkedIn</code> app nog kleiner getoond wordt (aangezien het in een webview geladen wordt, soort een frame). Hoe ziet je site er dan nog uit? Hoe vaak komt het niet voor dat je vanuit een nieuwsbrief door wilt klikken naar een product om vervolgens geconfronteerd te worden met een inlogscherm? Kan dat niet gebruiksvriendelijker?</p> <p><a href="https://speakerdeck.com/mathiasverraes/unbreakable-domain-models-dpc13">Unbreakable Domain Models van Mathias Verraes</a> bevatte tips die mij doen denken aan OO in C++: goed gebruik maken van types. I.p.v. een <code>string</code> voor een e-mail echt een <code>Email</code> class maken die eventueel ook validatie regelt. Wat ik interessant vind om te zien is dat de spreker in zijn sheets gebruik maakt van exceptions in de constructor. Waar ik persoonlijk ook groot fan van ben maar veel mensen in mijn omgeving niet <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. Naar mijn mening moet je een type niet half kunnen aanmaken (<code>constructen</code>) want dan is het geen volledig type, maak dan een liever een ander type die die state representeert. Zijn slides over <a href="https://speakerdeck.com/mathiasverraes/unbreakable-domain-models-dpc13">"Encapsulate operations"</a> tonen gebruik van een <code>CustomerSpecification</code> interface en het maken van classes zo dicht mogelijk bij het domein. </p> <p><a href="https://speakerdeck.com/dzuelke/surviving-a-prime-time-tv-commercial-dpc2013-2013-06-07">Surviving a Prime Time TV Commercial - David Zuelke</a>. Zeer interessante manier van vertellen met veel tips en info, triggert mij te kijken naar e.e.a., zoals genoemde <code>supervisord</code>, <code>transactional e-mail</code> of <code>elastic search</code>. Voor hun use-case was het zinvol om zo'n beetje <em>alles</em> in <code>Elastic Search</code> op te slaan.</p> <p><a href="http://www.slideshare.net/ircmaxell/php-under-the-hood-dpc">PHP, Under The Hood - Anthony Ferrara</a>. Ook goede spreker en presentatie was humorvol. Hij heeft een PHP compiler gemaakt in PHP en wist leuke dingen te vertellen over <code>opcodes</code> en liet bijbehorende code hier en daar zien. Helaas talk maar voor de helft gezien. Dat <code>vm_execute.h</code> gegenereerd wordt tijdens compileproces door PHP vind ik niet zo schokkend, dat je heel <code>g++</code> compiled met <code>g++</code> vind ik pas schokkend! <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p><center><a href="//cdn.cppse.nl/72-mm.jpg"><img border="0" src="//cdn.cppse.nl/72-thumb-mm.jpg" /></a></center></p> <h2 id="mikado">Mikado!</h2> <p>Uncon: <a href="http://mikadomethod.org/">The Mikado method</a> - Pascal de Vink (<a href="http://joind.in/inc/img/icon-slides.gif">slides here</a>). Hele korte talk, maar wel interessant. Een methode omtrent een leuk idee, met als onderliggende toon misschien wel het <em>tegenovergestelde</em> van "Respecteer het eco-systeem":</p> <ul> <li>Formuleer een doel, <em>schrijf</em> deze op papier. </li> <li>Naief implementeren van je doel in de code, 'fuck' de details of de dingen die je stuk maakt. Gewoon coden hoe het moet worden. </li> <li>Als je dit foutloos hebt gedaan en alles werkt nog: klaar. Maaarrr waarschijnlijk heb je wel een aantal dingen stuk gemaakt, heb je een lijstje compile errors of heb je in je hoofd een aantal TODO's of FIXME's <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></li> <li>Je schrijft deze als dependencies onder je doel (alles met pen en papier). Dit zijn dan de prerequisites voor je doel in een boomstructuur. </li> <li>Je fixed ze als het simpel is of shelved je changes en gaat &eacute;&eacute;n voor &eacute;&eacute;n die prerequisites fixen en comitten. </li> <li>Je streept alles van je blaadje af en werkt zo gericht naar je doel toe. </li> </ul> <p>Idee achter deze methode is dat je wellicht code weg moet gooien of shelven, maar daar tegenover staat dat je snel je prerequisites inzichtelijk hebt. Deze kun je ook aan een andere developer geven als een duidelijkere roadmap voor implementatie van dit doel.</p> <!-- <center><a href="//cdn.cppse.nl/72-mm1.jpg"><img border="0" src="//cdn.cppse.nl/72-thumb-mm1.jpg" /></a> &nbsp; <a href="//cdn.cppse.nl/72-mm2.jpg"><img border="0" src="//cdn.cppse.nl/72-thumb-mm2.jpg" /></a></center> --> <p><a href="http://www.slideshare.net/matthiasnoback/dependency-injection-smells">Dependency Injection Smells - Matthias Noback</a>. Volgens mij vond ik dit een saaie talk, weet het niet meer zeker. Maar wel veel herkenbare issues qua design en soort van eigenaardigheden in <code>PHP</code>. Zo is in <code>PHP</code> <code>public function foo(Bar $baz)</code> alleen maar afdwingen dat <code>$baz</code> de interface <code>Bar</code> implementeert. Maar is <code>$baz</code> gewoon de implementatie, en als je niet uitkijkt programmeer je tegen de implementatie aan i.p.v. de interface. (Gelukkig is phpstorm slim genoeg om dit snel herkenbaar te maken)</p> <p><a href="http://www.slideshare.net/DRvR/getting-your-toolbox-together">Getting your toolbox together - Daan van Renterghem</a>. Was een leuke talk, ging over <a href="http://www.vagrantup.com/">Vagrant</a>. Persoonlijk nooit een hobby van me geweest het configureren van dit soort dingen, maar hij liet Vagrant zien en vertelde over <a href="http://www.opscode.com/">Chef</a> en dat was zeker wel interessant. </p> <p><center><a href="//cdn.cppse.nl/72-mariokart.jpg"><img border="0" src="//cdn.cppse.nl/72-thumb-mariokart.jpg" /></a></center> <center><span style="font-style: italic; font-size: 80%;">Mariokarten (N64)</span></center></p> <h2 id="devolgendedag...">De volgende dag...</h2> <p><span style="float:right;"><img border="0" src="//cdn.cppse.nl/72-dpc_2013.png" /></span> Growth Hacking for Humans - Eamon Leonard. (<a href="https://www.youtube.com/watch?v=hEYQoj_XqNM">video</a>) Zeer interessante keynote. Een man die behoorlijk ondernemend is en een aantal fasen heeft doorlopen die hij wilde delen met de rest. Benadrukte de belangrijke rol van communities.</p> <p><a href="https://speakerdeck.com/ijansch/scenario-driven-api-design">Scenario Driven API Design - Ivo Jansch</a>. Sta je op het punt een (<code>REST</code>) <code>API</code> te ontwikkelen, neem dan deze talk even door. Leuke anecdote, spreker: "Wie heeft er wel eens wat gehoord over <code>Scenario driven design</code>, buiten deze talk om dan natuurlijk?". E&eacute;n of twee mensen steken hun vinger op. "Oh, dat is grappig want ik heb die term dus zelf verzonnen.". <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>Introduction to Django - Travis Swicegood. Was eerlijk gezegd best nieuwsgierig naar dit framework. Omdat ik binnenkort ook eens wat met Python wil gaan doen. Wel veel goede dingen over Django gehoord maar de spreker heeft niet echt goed de kracht van het framework over weten te brengen naar mij. Bleef overigens ook allemaal erg basic en na elke slide kwam een slide met daarop: "Are there any questions?". </p> <p><a href="http://www.slideshare.net/marcello.duarte/emergent-design-with-phpspec">Emergent Design with phpspec - Marcello Duarte</a>. Deze meneer was er erg handig mee, hij is dan ook de man achter het project. Het is denk ik <code>PHPUnit</code> done right, maar dan niet zoals het ellendige <code>SVN</code> "<code>CSV</code> done right" zou moeten zijn. Het is iets breder, je werkt meer vanuit de specificaties (die geformuleerd zijn als tests). <a href="http://www.phpspec.net/">phpspec</a> is gefocust om zo zinvol mogelijke foutmeldingen/feedback te geven en slimmer te zijn. Dat laatste wil zeggen dat het gespecte methoden of classes voor je kan aanmaken die nog niet bestaan. Het ondersteund je beter in Test of Behaviour driven development.</p> <p><a href="https://speakerdeck.com/bastianhofmann/measuring-and-logging-everything-in-real-time-2">Measuring and Logging Everything in Real Time - Bastian Hofmann</a>. Veel zinvolle tips m.b.t. logging. Veel voorkomende problemen is dat je meerdere servers en dus logs hebt, welke log entries overal horen nou bij elkaar. <code>PHP</code> heeft leuke <a href="http://php.net/apache_note">apache_note</a>. Andere trefwoorden: <code>graylog2</code>, <code>elastic search</code>, <code>AMQP</code>, <code>logstash</code>, <code>graphite</code>, <code>statsd</code>, <code>boomerang.js</code>, <code>X-trace-id</code>, <code>monolog</code>, <code>error_log</code>, <code>set_error_handler</code>, <code>set_exception_handler</code>. Zat een heel verhaal omheen, wat ik hier niet zal reproduceren.</p> <h2 id="conclusie">Conclusie</h2> <p><a href="http://www.slideshare.net/jaxlondon2012/worse-is-better-for-better-or-for-worse">Worse Is Better, for Better or for Worse - Kevlin Henney</a>. Zeer boeiende spreker, kan ik echt lang naar luisteren zulke talks. Alan Kay meerdere keren geciteerd gedurende deze conference, zo ook in deze talk, zie ook <a href="http://www.slideshare.net/marcello.duarte/emergent-design-with-phpspec">phpspec slides</a>, slide 40. Het messaging aspect tussen de objecten is onderbelicht geweest, zie slides 13, 14, 15 (erg leuk). Ik vind het "jammer" dat ik geen ervaring heb met <code>COM</code>, dus zal eens een keer uit nieuwsgierheid naar kijken. Hij ging in deze talk in op een uitspraak "Worse is better":</p> <blockquote> <p>Over two decades ago, Richard P Gabriel proposed the thesis of "Worse Is Better" to explain why some things that are designed to be pure and perfect are eclipsed by solutions that are seemingly limited and incomplete. This is not simply the observation that things that should be better are not, but that some solutions that were not designed to be the best were nonetheless effective and were the better option. We find many examples of this in software development, some more provocative and surprising than others. In this talk we revisit the original premise and question in the context of software architecture.</p> </blockquote> <p>Wat is het antwoord op deze vraag? Als Smalltalk zoveel "mooier" is, waarom programmeren we nu inmiddels niet allemaal in Smalltalk? Waarom werd C++ zo populair? Het antwoord is dat C++ het ecosysteem respecteerde. In de tijd van Smalltalk waren thuis computers nog zo snel niet en smalltalk was een stuk zwaarder, daarbij draaide het afgezonderd in een virtual machine in zijn soort van eigen ideale universum. Hierbuiten smalltalk is echter een heel eco-systeem wat in dit geval een "trage" computer is met een printer, muis, toetsenbord, <em>etc.</em> De spreker vertelde over hoe practisch hij <code>Turbo C++ compiler</code> vanaf een floppy op zijn computer zette en daarmee gelijk kon programmeren en echt dingen voor elkaar kon krijgen en dat de geschreven programma's <em>snel</em> waren. Dit is zijn waarheid, maar ik denk dat de kern van het ecosysteem respecteren wel een goed punt is, en voor mij zeer herkenbaar.</p> <p>Om terug te komen op de keynote van dag &eacute;&eacute;n, waarom is het web zoals we dat nu kennen, met <code>HTTP</code> + <code>HTML</code> zo succesvol geworden? Er waren ook daar (geavanceerdere?) protocollen of in iedergeval alternatieven, zoals <a href="http://en.wikipedia.org/wiki/Gopher_%28protocol%29">gopher</a>. De rede is <em>natuurlijk</em> dezelfde, <code>HTML</code> "embraces the ecosysteem", het groeit mee met de ontwikkelingen, integreert en past zich aan. Denk aan <code>Java applets</code>, <code>Flash</code>, talloze file formats, <code>VRML</code>, <code>WebGl</code> maar ook <em>juist</em> integreert met andere protocollen zoals <code>FTP</code>--en ook al weet ik niet--vast ook <code>gopher</code>!</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 28 Jun 2013 00:00:00 +0000 ![nl][] Tweakers fotoalbum images uploader //blog.cppse.nl/tweakers-image-uploader<div class="datestamp">June 7 2013</div> <div class="h1"><a href="//blog.cppse.nl/tweakers-image-uploader"> <h1 id="nltweakersfotoalbumimagesuploader"><img id="nl" src="//cdn.cppse.nl/nl.png" alt="nl" title="nl" /> Tweakers fotoalbum images uploader</h1> </a></div> <p>Wat ik handig vind is om <em>snel</em> een plaatje te kunnen delen, op IRC bijvoorbeeld. Naar tweakers.net gaan, {inloggen, }naar fotoalbum, file upload knop, naar juiste map bladeren, plaatje selecteren en dan uploaden... is voor mij dan niet snel genoeg. Vooral als ik het plaatje voor me heb staan, negen van de tien keer in Windows Explorer&reg;&trade;.</p> <p>Ik wil gewoon de files selecteren, een sneltoets indrukken en aangeven in welke map ze moeten. Daarna wil ik de link hebben naar het plaatje en klaar. Geen frustratie. :)</p> <h2 id="voorbeeldvanuploadnaartweakers.netfotoalbum">Voorbeeld van upload naar tweakers.net fotoalbum</h2> <p>Leuke bijkomstigheid is dat je gelijk een heleboel files in &eacute;&eacute;n keer kunt uploaden op deze manier.</p> <p><center><a href="//cdn.cppse.nl/70-result.png"><img border="0" src="//cdn.cppse.nl/70-large-thumb-result.png" /></a></center></p> <h2 id="toelichting">Toelichting</h2> <p><code>sanitizer.exe</code> moet gestart zijn en luistert (standaard) o.a. op <code>CTRL + ALT + 6</code>. Zodra die hotkey ingedrukt wordt zal sanitizer (1) de geselecteerde files uit het actieve explorer venster lezen en wegschrijven naar het bestand <code>selected_files.txt</code> en (2) het php script <code>explorer_call.php</code> aanroepen die met dit bestand kan doen wat je wilt, in dit geval de files uploaden met <a href="//php.net/manual/en/book.curl.php">curl</a>.</p> <p>Uploaden naar tweakers was het doel, maar omdat ik het &uuml;berhaupt handig vind om ook andere dingen te kunnen doen met geselecteerde files in explorer heb ik het flexibel gehouden met een script. Ook vind ik het handig om allerlei dingen onder <code>ctrl+alt+{nummer}</code> te kunnen stoppen: zoals text in het clipboard of system calls uitvoeren. Dus ook dat heb ik wat ruimer opgezet en daar is <a href="//blog.cppse.nl/sanitizer-shortcut-keys">sanitizer</a> uitgekomen.</p> <h2 id="sanitizerconfiggenvoortweakers.netimageuploads">Sanitizer configgen voor Tweakers.net image uploads</h2> <p>Je moet dus wel 1337 genoeg zijn om het fotoalbum unlocked te hebben binnen tweakers, anders zul je de optie onder je profiel niet hebben. :7 Als je mij genoeg vertrouwd kun je de installer van <a href="//blog.cppse.nl/sanitizer-shortcut-keys">sanitizer</a> downloaden en uitvoeren. De installer maakt ook een <code>example_tweakers/</code> directory aan met daarin een <code>explorer_call.php</code>, die kun je bewerken en over <code>explorer_call.php</code> heenplakken die een directory hoger staat. Out of de box werkt deze dus nog niet.</p> <p>Ten eerste zitten in <code>explorer_call.php</code> ook nog wat andere probeersels van mij die je kunt negeren (opties 'c' en 's'). </p> <p>Voorbeeld output:</p> <pre><code>Listing of files: - C:\Program Files (x86)\sanitizer\example_scripts\explorer_call.php.tweakers Please select from the following list - s = move to unsorted directory with extensions as subdir - p = upload to tweakers fotoalbum public folder - r = upload to tweakers fotoalbum private folder - c = create command prompt here (other) = exit You choose (press RETURN):</code></pre> <p>Ten tweede ga ik even uit van de standaard situatie dat je een <code>private</code> en een <code>public</code> folder hebt in je fotoalbum. In mijn geval is 'r' -> upload naar m'n album 'private' en 'p' naar 'public'. Je kunt het script makkelijk uitbreiden met een extra curl request om de albums eerst dynamisch op te halen. Vergeet het dan niet in de reacties te delen ;)</p> <p>Als je dit huidige script werkend wilt maken voor jezelf moet je momenteel:</p> <p><strong>1. Een <code>TnetID</code> (sessie id) in de sourcecode op regel 29 zetten van een actieve sessie naar tweakers.net (je zou een extra sessie kunnen maken speciaal voor dit script).</strong> Dat kun je doen door in te loggen en met Firebug je cookie uit te lezen:</p> <p><center><a href="//cdn.cppse.nl/70-extract-tnet-id.png"><img border="0" src="//cdn.cppse.nl/70-large-thumb-extract-tnet-id.png" /></a></center></p> <p><strong>2. Je fotoalbum id's in de source zetten op regels 32 en 35.</strong></p> <p>Deze staan in de URL's van de albums, in het voorbeeld <code>1001</code>:</p> <p><center><a href="//cdn.cppse.nl/70-extract-album-ids.png"><img border="0" src="//cdn.cppse.nl/70-thumb-extract-album-ids.png" /></a></center></p> <p><strong>3. sanitizer.exe moet wel gestart zijn natuurlijk</strong></p> <h2 id="troubleshoot">Troubleshoot</h2> <ul> <li>Image upload werkt niet op geselecteerde files op je desktop. Open dan eerst explorer en ga via dat ding naar de desktop..</li> <li>Lees ook <a href="//blog.cppse.nl/sanitizer-shortcut-keys#troubleshoot" title="Troubleshoot">de troubleshoot van sanitizer</a>. Huidige PHP script is namelijk niet geheel fout ongevoelig opgezet. :X</li> </ul> <p><br/></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 07 Jun 2013 00:00:00 +0000 sanitizer - a shortcut key app with explorer integration //blog.cppse.nl/sanitizer-shortcut-keys<div class="datestamp">June 6 2013</div> <div class="h1"><a href="//blog.cppse.nl/sanitizer-shortcut-keys"> <h1 id="sanitizer-ashortcutkeyappwithexplorerintegration">sanitizer - a shortcut key app with explorer integration</h1> </a></div> <h2 id="hookshortcutkeysctrlalt0123456789">Hook shortcut keys Ctrl+Alt+{0,1,2,3,4,5,6,7,8,9}</h2> <p><code>sanitizer.exe</code> is a program that reads a config, and then registers the hotkeys <code>Ctrl+Alt+{number}</code>, where <code>{number}</code> is <code>{0, 1, ..., 9}</code>.</p> <p>Example contents for <code>config.ini</code>:</p> <pre><code>[1] type=command value=putty -load SomeSession [4] type=clipbard value=MyMysqlPasswd [6] type=explorer ...</code></pre> <p>(Only <code>[0]</code> through <code>[9]</code> are valid entries in the config.)</p> <p>As shown in the example, available action types are:</p> <table> <col align="left" /> <col align="left" /> <col align="left" /> <thead> <tr> <th>action types</th> <th>description</th> <th>example</th> </tr> </thead> <tbody> <tr> <td align="left">command</td> <td align="left">execute system call</td> <td align="left"><code>putty.exe -load amazon</code></td> </tr> <tr> <td align="left">clipboard</td> <td align="left">replace clipboard with text</td> <td align="left">some database password perhaps</td> </tr> <tr> <td align="left">explorer</td> <td align="left">dispatch selected files in windows explorer to script</td> <td align="left">see <code>explorer_call.php</code> source</td> </tr> </tbody> </table> <h2 id="theexploreraction">The explorer action</h2> <p>This action type is special in the config: the <code>value</code> key in the <code>config.ini</code> for it is not used, so you don't need to define it.</p> <p>Example use: If you assign it under hotkey <code>Ctrl+Alt+6</code> as I did in the example, you can use this hotkey whenever a (Windows file) explorer is active/has focus (<code>explorer.exe</code>). If you trigger the hotkey it fetches the selected files and writes them to <code>selected_files.txt</code> in the program installation directory. As long as this file exists, the hotkey won't trigger again! It will then immediately call the <code>explorer_call.php</code> PHP-script, also in the installation directory, that can be defined to do whatever you want on this list of files. As long as it removes the <code>selected_files.txt</code> afterwards.</p> <p>Contents of the default <code>explorer_call.php</code> script:</p> <pre class="prettyprint"><code data-language="php">&lt;?php $files = explode(&quot;\n&quot;, trim(file_get_contents('selected_files.txt'))); unlink('selected_files.txt'); print_r($files); system(&quot;pause&quot;); ?&gt; </code></pre> <p><center><a href="//cdn.cppse.nl/69-explorer-example.png"><img border="0" src="//cdn.cppse.nl/69-large-thumb-explorer-example.png" /></a></center></p> <p><strong>Small troubleshoot</strong> <a name="troubleshoot"></a></p> <p>If you crash the script, and <code>selected_files.txt</code> is not deleted, your shortcut key won't appear to work anymore, as the existance of this file also serves as a lock. Only when the file was processed and deleted can the explorer hotkey fire and launch the PHP script again. So when your hotkey appears non-responsive, chances are you need to delete <code>selected_files.txt</code>, or make the script more advanced <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</p> <h2 id="download">Download</h2> <p>Download setup file: <a href="sanitizer-1.0-setup.exe">sanitizer-1.0-setup.exe</a>.</p> <p>The executable is compiled with VS2012 with platform target "Visual Studio 2012 - Windows XP (v110_xp)". VS2012 x86 redistributable is included in the setup. After install make sure to edit <code>config.ini</code> first, and realize that there is no GUI, the executable will run in the background. If you want to restart it use the <code>restart_sanitizer.bat</code>. If you want to kill it, use <code>taskmgr</code> or <code>taskkill /IM sanitizer.exe</code>.</p> <p><strong>Verify</strong></p> <p>If you start it with the sample config: <code>Ctrl+Alt+4</code> should yield <code>SomeOtherPassword</code> in your clipboard; <code>Ctrl+Alt+6</code> should print selected files in Windows explorer (do this from within explorer); <code>Ctrl+Alt+1</code> should start <code>putty.exe</code> if you have that installed (and available in your <code>PATH</code>).</p> <h2 id="windows8vistadisableuac">Windows 8 (Vista?) Disable UAC</h2> <p>If your system complains about not being able to write to <code>selected_files.txt</code>, just make it run as Administrator:</p> <p><center><a href="//cdn.cppse.nl/69-run-as-administrator.png"><img border="0" src="//cdn.cppse.nl/69-thumb-run-as-administrator.png" /></a></center></p> <h2 id="explorercallusecase:tweakers.netfotouploaderscript">Explorer call use case: Tweakers.net foto uploader script</h2> <p><strong>Update 10 jun 2013:</strong></p> <p>It's included in the installer in the example directory after install, an <code>explorer_call.php</code> script that makes it possible to upload images to your Tnet foto-album. Wrote a blog-post about it in Dutch here: <a href="http://cppse.tweakblogs.net/blog/9047/tweakers-fotoalbum-images-uploader.html">http://cppse.tweakblogs.net/blog/9047/tweakers-fotoalbum-images-uploader.html</a>.</p> <p><center><a href="//cdn.cppse.nl/69-314104.png"><img border="0" src="//cdn.cppse.nl/69-thumb-314104.png" /></a></center></p> <p><strong>Update 20 oct 2013:</strong></p> <p>Made a few changes to the tweakers script again, another Tweakers user wrote a blog about this: <a href="http://perkouw.tweakblogs.net/blog/9474/t-punt-net-fotoalbum-script!.html">http://perkouw.tweakblogs.net/blog/9474/t-punt-net-fotoalbum-script!.html</a>.</p> <ul> <li>Now the only thing you need to edit is the TnetID (session id). </li> <li>It will fetch the available foto albums itself.</li> <li>I've also improved the check whether a file upload succeeded, it fetches the last known image from the fotoalbum, before and after the upload, if they match, the upload failed. </li> <li>A summary of the uploads is now opened after the last image upload, instead of opening an url every upload ;)</li> </ul> <p>(I'll update the installer later to include this new <code>explorer_call.php</code>)</p> <p><center><a href="//cdn.cppse.nl/69-324873.png"><img border="0" src="//cdn.cppse.nl/69-thumb-324873.png" /></a></center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 06 Jun 2013 00:00:00 +0000 benchmarklib: a small benchmarking library //blog.cppse.nl/benchmarklib<div class="datestamp">April 29 2013</div> <div class="h1"><a href="//blog.cppse.nl/benchmarklib"> <h1 id="benchmarklib:asmallbenchmarkinglibrary">benchmarklib: a small benchmarking library</h1> </a></div> <h2 id="background">Background</h2> <p>Yet another C++ benchmark library I guess, but I couldn't find any that fulfilled my needs properly <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /> The requirements: output to text into a format easily parsable (for generating grahps afterwards). Low coupling, non intrusive, performant, provide basic statistics, ... .</p> <p>So this one is easy to use, at first I implemented the timer using <code>boost::posix_time::microsec_clock</code>, only to discover that the resolution was not as good on windows. [Edit 15-02-2015: I now think the individual measures may be off a bit, but overall it does seem to average out to correct timings. I now added a boost::chrono based timer, which seems to offer good results on all platforms.] I don't understand why exactly, so I searched for a better timer (for windows) and found an example for an HRTimer [Edit 15-02-2015, forgot the link, but I found <a href="https://code.google.com/p/windows-package-manager/source/browse/wpmcpp/hrtimer.cpp?spec=svn7e3aca84c07959b053ea72ab61ccae33041896c9&amp;r=7e3aca84c07959b053ea72ab61ccae33041896c9">this</a>]. So now you can choose Timer implementations ^_^</p> <p>Available Timer implementations are:</p> <ul> <li><strong>BoostTimerImpl</strong> - microseconds resolution on *nix systems, at most milliseconds on windows.</li> <li><strong>BoostChronoTimerImpl</strong> - microseconds resolution on *nix systems and windows.</li> <li><strong>WindowsHRTimerImpl</strong> - microseconds resolution on windows, windows specific.</li> </ul> <p>Available Measure classes are:</p> <ul> <li><strong>MeasureCounts</strong> - group individual measures per time interval.</li> <li><strong>MeasureInterval</strong> - individual measures.</li> </ul> <p>With both classes you can set the number of measures you require before the report file should be generated.</p> <h2 id="download">Download</h2> <p>Currently compiled as a static library for platform toolset <strong>Visual Studio 2012 - Windows XP (v110_xp)</strong>. It is 32 bit, with flags /MD (Multi-threaded DLL).</p> <p>Download link: <a href="//cdn.cppse.nl/benchmarklib-1.0.zip">benchmarklib-1.0.zip</a> (md5 <code>37ce3698a5af8b25555913f5151c85c9</code>).</p> <h2 id="usingthebenchmarkandtimerclasses">Using the Benchmark and Timer classes</h2> <p><strong>Using the library</strong></p> <ol> <li>#include "benchmark.h"</li> <li>Link against benchmarklib.lib</li> </ol> <p><strong>Using the MeasureCounts class</strong></p> <pre class="prettyprint lang-cpp"><code data-language="c">MeasureCounts counter(TimerFactory::Type::WindowsHRTimerImpl); // Measure FPS counter.setDescription(&quot;number of frames&quot;); counter.setBatchSize(1000); // Measure 30 times at 500 milliseconds interval (duration of 15 seconds) counter.require(30); counter.setBatchGroupSize(500); // Results will be written to RESULT_EXAMPLE.TXT counter.setOutput(&quot;EXAMPLE&quot;); while ( ! counter.complete()) { /* do something */ counter.measure(); } </code></pre> <p><strong>Using the MeasureInterval class</strong></p> <pre class="prettyprint lang-cpp"><code data-language="c">MeasureInterval counter2(TimerFactory::Type::WindowsHRTimerImpl); counter2.setDescription(&quot;milliseconds per frame&quot;); // Measure 500 frames counter2.require(500); // Results will be written to RESULT_EXAMPLE2.TXT counter2.setOutput(&quot;EXAMPLE2&quot;); while ( ! counter2.complete()) { /* do something */ counter2.measure(); } </code></pre> <p><strong>Using the timer class</strong></p> <pre class="prettyprint lang-cpp"><code data-language="c">auto timer = TimerFactory::factory(TimerFactory::Type::WindowsHRTimerImpl); timer-&gt;start(); /* do something */ std::cout &lt;&lt; &quot;Run time CPU : &quot; &lt;&lt; timer-&gt;end() &lt;&lt; &quot; milliseconds.&quot; &lt;&lt; std::endl; </code></pre> <h2 id="exampleoutputresult_example.txt">Example output (<code>RESULT_EXAMPLE.TXT</code>)</h2> <pre><code>@@@ BENCHMARK @@@ profile "EXAMPLE" measure "number of frames" / 1000 ms. measure interval = 500 ms. @@@ STATISTICS @@@ N 30 Mean 122.5427 S.E. Mean 0.2686 Std. Dev 1.4714 Variance 62.7829 Minimum 118.8119 Maximum 124.5059 Median 122.7723 @@@ HISTOGRAM @@@ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ------------------------------ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 0. 118.8119 &lt;&gt; 119.4446 = 2 1. 119.4446 &lt;&gt; 120.0772 = 1 2. 120.0772 &lt;&gt; 120.7099 = 0 3. 120.7099 &lt;&gt; 121.3426 = 3 4. 121.3426 &lt;&gt; 121.9752 = 2 5. 121.9752 &lt;&gt; 122.6079 = 4 6. 122.6079 &lt;&gt; 123.2406 = 4 7. 123.2406 &lt;&gt; 123.8733 = 11 8. 123.8733 &lt;&gt; 124.5059 = 2 9. 124.5059 &lt;&gt; 125.1386 = 1</code></pre> <h2 id="troubleshoot">Troubleshoot</h2> <p>Use this if your compiler does not support typed enums:</p> <pre><code>auto timer = TimerFactory::factory("WindowsHRTimer");</code></pre> <p><strike>And for the benchmark classes as well..</strike> (Edit: not yet supported for the Measure classes)</p> <p><strike>MeasureCounts counter("WindowsHRTimer");</strike></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 29 Apr 2013 00:00:00 +0000 A short reminder that sequential memory I/O is faster :P //blog.cppse.nl/sequential-memory-reads-are-faster<div class="datestamp">April 8 2013</div> <div class="h1"><a href="//blog.cppse.nl/sequential-memory-reads-are-faster"> <h1 id="ashortreminderthatsequentialmemoryioisfaster:p">A short reminder that sequential memory I/O is faster :P</h1> </a></div> <p>Faster than random reads that is. I knew this to be true for harddisks and <code>SSD</code> drives because of <a href="http://sssslide.com/speakerdeck.com/krestenkrab/hanoidb-and-other-riak-hacks">this presentation</a>. (For average devices: Random I/O 2.7 MB/s vs. Sequential I/O of 213 MB/s for <code>HDD</code>'s, 60-300 MB/s vs. 293-366 MB/s for <code>SSD</code>'s).</p> <p>But I never realized it was similarly true for <code>RAM</code> access, or that the impact would be this big. Now that I do however, I realize where I must have destroyed performance in the past.. and why something else surprised me at the time in being unexpectedly fast!</p> <!-- (it was sequential where that I compared it with wasn't) :P) --> <h2 id="sfmlfastpixelreadwriteaccess">SFML fast pixel read/write access!</h2> <p>Anyway, my goal was finding the fastest possible read and write access for pixels inside an image. In this case I used <a href="http://www.sfml-dev.org/"><code>SFML</code></a> and desired for no overhead by getters or setters (providing f.i. bounds checking). Just raw access.</p> <p>Found out you can get a (read only) pointer to the underlying pixel data for an <code>sf::Image</code> object (that I force non-<code>const</code>):</p> <pre class="prettyprint lang-cpp"><code data-language="c">sf::Image image; image.create(1280, 720, sf::Color(0, 0, 0, 255)); auto *pixels = const_cast&lt;sf::Uint8 *&gt;(image.getPixelsPtr()); </code></pre> <p>Wrote a simple loop to initialize an image with fancy colors (you can probably guess where this is going.. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />):</p> <pre class="prettyprint lang-cpp"><code data-language="c">for (int x = 0; x &lt; width; x++) { for (int y = 0; y &lt; height; y++) { int index = (x + y * width) * 4; // My trademark color setup int index = x + y; pixels[index + 0] = index % 255; // R pixels[index + 1] = (index + 100) % 255; // G pixels[index + 2] = (x + 200) % 255; // B pixels[index + 3] = 80; // A } } </code></pre> <p>In the mainloop I have similar code: <em>for each pixel, increment the RGB color values a bit</em>. You can view the code in the screenshot a few paragraphs from now. The result was 42.65 FPS (frames per second).</p> <p>Measuring FPS every 0,5 seconds 30 times, results in this average of 42.65 fps with a Standard Error of 0.08. See <strong>[1]</strong> in the following table.</p> <table> <caption id="table:performanceresults"><em>Table: performance results</em></caption> <col align="left" /> <col align="left" /> <col align="left" /> <col align="left" /> <col align="left" /> <col align="left" /> <thead> <tr> <th colspan="2">[1]</th> <th colspan="2">[2]</th> <th colspan="2">[3]</th> </tr> </thead> <tbody> <tr> <td align="left">N</td> <td align="left">30</td> <td align="left">N</td> <td align="left">30</td> <td align="left">N</td> <td align="left">30</td> </tr> <tr> <td align="left">Mean</td> <td align="left">42.6518</td> <td align="left">Mean</td> <td align="left">122.4701</td> <td align="left">Mean</td> <td align="left">125.8626</td> </tr> <tr> <td align="left">S.E. Mean</td> <td align="left">0.0801</td> <td align="left">S.E. Mean</td> <td align="left">0.2189</td> <td align="left">S.E. Mean</td> <td align="left">0.3322</td> </tr> <tr> <td align="left">Std. Dev</td> <td align="left">0.4387</td> <td align="left">Std. Dev</td> <td align="left">1.1991</td> <td align="left">Std. Dev</td> <td align="left">1.8193</td> </tr> <tr> <td align="left">Variance</td> <td align="left">5.5810</td> <td align="left">Variance</td> <td align="left">41.6968</td> <td align="left">Variance</td> <td align="left">95.9866</td> </tr> <tr> <td align="left">Minimum</td> <td align="left">42.1456</td> <td align="left">Minimum</td> <td align="left">119.8428</td> <td align="left">Minimum</td> <td align="left">120.3156</td> </tr> <tr> <td align="left">Maximum</td> <td align="left">44.7471</td> <td align="left">Maximum</td> <td align="left">124.7525</td> <td align="left">Maximum</td> <td align="left">128.2051</td> </tr> <tr> <td align="left">Median</td> <td align="left">42.6357</td> <td align="left">Median</td> <td align="left">120.7921</td> <td align="left">Median</td> <td align="left">125.0000</td> </tr> </tbody> </table> <h2 id="thefix:changingtheorderofpixelaccess">The fix: Changing the order of pixel access</h2> <p>I don't have the fastest PC so initially I thought it wouldn't get that much faster, but then I ran the profiler and discovered the first write to the color values was extremely slow. Once the pointer was in position for the pixel however, successive writes to green (G) and blue (B) (of RGBA) are fast. This made me realize it was seeking for each pixel.</p> <p>So I swapped the two for loops (to first Y then X), thus aligning the loop with the memory representation of the pixels to get the much better 122.47 FPS! (see <strong>[2]</strong>).</p> <p>Another minor improvement was by making the intermediate "index" variable obsolete (see <strong>[3]</strong>).</p> <p><center> <a href="//cdn.cppse.nl/65-results.png"><img border="0" src="//cdn.cppse.nl/65-large-thumb-results.png" /></a> <br/> <em>Figure: Visual studio 2012's awesome profiler output.</em> </center></p> <p>Note that you don't <em>really</em> need two for loops if you don't do stuff with the colors related to x or y.</p> <p>This fix may seem obvious now, but for me this mistake of swapping the for loops was one easily made. I found it subtle and it resulted in unnecessarily poor performance. That's why I hope others to find this reminder useful!</p> <p><center> <a href="//cdn.cppse.nl/65-sp_1411_clip05.jpg"><img border="0" src="//cdn.cppse.nl/65-thumb-sp_1411_clip05.jpg" /></a> <br/> <em>Figure:</em> <strong><em>If you didn't want poor performance, you shouldn't have swapped the for loops!</em></strong> <em>(image source: <a href="http://www.southparkstudios.com/clips/360434/god-bless-you-captain-hindsight">here</a>)</em> </center></p> <p>Also, SFML stores the RGBA values this way, other libraries may do so differently.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 08 Apr 2013 00:00:00 +0000 ![nl][] watermarker tool //blog.cppse.nl/watermarker<div class="datestamp">March 29 2013</div> <div class="h1"><a href="//blog.cppse.nl/watermarker"> <h1 id="nlwatermarkertool"><img id="nl" src="//cdn.cppse.nl/nl.png" alt="nl" title="nl" /> watermarker tool</h1> </a></div> <h2 id="installer">Installer</h2> <p>Hier te downloaden: <a href="watermarker-1.0-setup.exe">watermarker-1.0-setup.exe</a></p> <h2 id="hoetegebruiken">Hoe te gebruiken</h2> <p>Na installatie gewoon "watermarker" starten.</p> <p>De plaatjes die je wilt watermerken kun je op het grijze vlak slepen. (Zoveel je wilt tegelijk) Je kunt zien hoe ze eruit komen te zien, en dan op "Write to images!" klikken om ze daadwerkelijk te watermerken.</p> <p>Let wel op: de plaatjes worden overschreven.</p> <p>Dus je zult wel de plaatjes eerst moeten kopieeren en dan de kopieen watermerken. Want het watermerk krijgt je er niet meer uit, en de originele foto's wil je wel behouden lijkt me <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <h2 id="voorbeeld:">Voorbeeld:</h2> <p><center> <img border="0" src="//cdn.cppse.nl/64-watermerk.jpg" /> </center></p> <h2 id="update29mei2014:installitonubuntuusingwine">Update 29 mei 2014: Install it on Ubuntu using wine!</h2> <p>Nu ik geen linux meer gebruik, heb ik gekeken of watermarker goed installeerd d.m.v. wine. De installatie duurt erg lang, en de shortcut die gemaakt werd werkte niet... <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>Het probleem was blijkbaar dat de installer niet de C++ runtime dlls heeft geinstalleerd:</p> <pre><code>trigen@Firefly21:/home/trigen/.wine/drive_c/Program Files (x86)/watermarker&gt; wine watermarker.exe err:module:import_dll Library MSVCR110.dll (which is needed by L"C:\\Program Files (x86)\\watermarker\\watermarker.exe") not found err:module:import_dll Library MSVCP110.dll (which is needed by L"C:\\Program Files (x86)\\watermarker\\watermarker.exe") not found err:module:LdrInitializeThunk Main exe initialization for L"C:\\Program Files (x86)\\watermarker\\watermarker.exe" failed, status c0000135</code></pre> <p>Deze heb ik handmatig in de watermarker map moeten plaatsen. Download ze hier: <a href="//cdn.cppse.nl/msvcp110.dll">msvcp110.dll</a> en <a href="//cdn.cppse.nl/msvcr110.dll">msvcr110.dll</a>.</p> <p>De shortcut doet het daarna wel. Het programma doet het alleen nog niet omdat convert.exe ook problemen geeft. </p> <pre><code>trigen@Firefly21:/home/trigen/.wine/drive_c/Program Files (x86)/watermarker&gt; wine convert.exe err:module:import_dll Library VCOMP100.DLL (which is needed by L"C:\\Program Files (x86)\\watermarker\\convert.exe") not found err:module:LdrInitializeThunk Main exe initialization for L"C:\\Program Files (x86)\\watermarker\\convert.exe" failed, status c0000135</code></pre> <p>Deze dll ook hier handmatig neerzetten fixed het probleem, download: <a href="//cdn.cppse.nl/vcomp100.dll">vcomp100.dll</a></p> <p>Op die manier werkt het (inclusief de drag &amp; drop functionaliteit) prima onder Ubuntu (versie 13.10).</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 29 Mar 2013 00:00:00 +0000 Fixing slow phpmyadmin by rewriting queries using mysql-proxy //blog.cppse.nl/fix-slow-phpmyadmin<div class="datestamp">March 24 2013</div> <div class="h1"><a href="//blog.cppse.nl/fix-slow-phpmyadmin"> <h1 id="fixingslowphpmyadminbyrewritingqueriesusingmysql-proxy">Fixing slow phpmyadmin by rewriting queries using mysql-proxy</h1> </a></div> <h2 id="theperformanceproblem">The performance problem</h2> <p>Opening phpmyadmin becomes quite annoying if the initial loading takes around 30 seconds, after logging in. Viewing the queries being executed by phpmyadmin with <a href="//blog.cppse.nl/meta-log-monitor">meta log monitor</a> easily shows the bottleneck is "SHOW TABLE STATUS FROM '&lt;DATABASE>'". It is the only query requiring &gt; 14 seconds in this case.</p> <p><center> <a href="//cdn.cppse.nl/63-bottleneck.jpg"><img border="0" src="//cdn.cppse.nl/63-large-thumb-bottleneck.jpg" /></a> </center></p> <p>Phpmyadmin executes this query twice: in it's navigation frame for a listing of tables and in the right frame for the detailed listing.</p> <p><center> <a href="//cdn.cppse.nl/63-phpmyadmin.jpg"><img border="0" src="//cdn.cppse.nl/63-large-thumb-phpmyadmin.jpg" /></a> </center></p> <h2 id="createcachefor:showtablestatusfromltdatabase">Create cache for: SHOW TABLE STATUS FROM &lt;DATABASE></h2> <p>The output of this query is not that accurate. Running it twice, and comparing the output shows some columns are estimates. So if we would cache it for two minutes that would probably not harm phpmyadmin.</p> <p><center> <a href="//cdn.cppse.nl/63-DEV-5429.jpg"><img border="0" src="//cdn.cppse.nl/63-large-thumb-DEV-5429.jpg" /></a> </center></p> <p>Then I created a table where to store the SHOW TABLE STATUS output into (I realize the types I used are probably overkill for some fields <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />).</p> <pre><code>CREATE TABLE showtablecache ( Database_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Name_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Engine_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Version Integer(11), Row_format_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Rows_ BigInt(20), Avg_row_length BigInt(20), Data_length BigInt(20), Max_data_length BigInt(20), Index_length BigInt(20), Data_free BigInt(20), Auto_increment_ BigInt(20), Create_time DateTime, Update_time DateTime, Check_time DateTime, Collation_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Checksum VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Create_options VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci, Comment_ VarChar(255) CHARACTER SET latin1 COLLATE latin1_swedish_ci ) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;</code></pre> <p>This doesn't work by the way: INSERT INTO showtablecache SHOW TABLE STATUS FROM '&lt;DATABASE>' (leaving the "Database_" column from above CREATE TABLE). I didn't really expect that to work, but I would have been pleasantly surprised if it did <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</p> <p>I created a resultset with similar output to the SHOW TABLE STATUS query with a SELECT query on INFORMATION_SCHEMA.TABLES. And perform INSERT's with the following cron:</p> <pre class="prettyprint"><code data-language="php">&lt;?php $res = $conn-&gt;select('SHOW DATABASES'); while ($row = $res-&gt;assoc()) { $dbname = current($row); print &quot;Caching $dbname...&quot;; $conn-&gt;startTransaction(); $conn-&gt;update('DELETE FROM showtablecache WHERE Database_ = \'' . $conn-&gt;escape($dbname) . '\''); $conn-&gt;update('INSERT INTO showtablecache SELECT \''.$conn-&gt;escape($dbname).'\' ,TABLE_NAME ,ENGINE ,VERSION ,ROW_FORMAT ,TABLE_ROWS ,AVG_ROW_LENGTH ,DATA_LENGTH ,MAX_DATA_LENGTH ,INDEX_LENGTH ,DATA_FREE ,AUTO_INCREMENT ,CREATE_TIME ,UPDATE_TIME ,CHECK_TIME ,TABLE_COLLATION ,CHECKSUM ,CREATE_OPTIONS ,TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = \'' . $conn-&gt;escape($dbname).'\''); $conn-&gt;endTransaction(true); print &quot;done.\n&quot;; } ?&gt; </code></pre> <p>I run this script every 2 minutes. You really want the transaction because you don't want the cache to be empty between the DELETE and the time the INSERT finishes.</p> <h2 id="rewritequerytouseourcacheusingmysql-proxy">Rewrite query to use our cache using mysql-proxy</h2> <p>The final step was to rewrite the SHOW TABLE STATUS queries to SELECT queries on our new cache table. This turns out to be quite simple using a neat Lua script for mysql-proxy:</p> <pre><code> 0 --- 1 -- Uses MySQL-Proxy to rewrite annoyingly slow SHOW TABLE STATUS FROM `&lt;DATABASE&gt;` queries to a SELECT from our periodic cache 2 -- 3 -- @author trigen 4 -- 5 function read_query( packet ) 6 if string.byte(packet) == proxy.COM_QUERY then 7 local query = string.sub(packet, 2) 8 9 -- attempt to match the query, and more specifically extract the requested database 10 local dbname = string.match(string.upper(query), "^SHOW TABLE STATUS FROM \`(%a*)\`") 11 if dbname then 12 local newquery = string.format('SELECT' .. 13 ' Name_ AS `Name`,' .. 14 ' Engine_ AS `Engine`,' .. 15 ' Version,' .. 16 ' Row_format_ AS `Row_format`,' .. 17 ' Rows_ AS `Rows`,' .. 18 ' Avg_row_length,' .. 19 ' Data_length,' .. 20 ' Max_data_length,' .. 21 ' Index_length,' .. 22 ' Data_free,' .. 23 ' Auto_increment_ AS `Auto_increment`,' .. 24 ' Create_time,' .. 25 ' Update_time,' .. 26 ' Check_time,' .. 27 ' Collation_ AS `Collation`,' .. 28 ' Checksum,' .. 29 ' Comment_ AS `Comment`,' .. 30 ' Create_options' .. 31 ' FROM' .. 32 ' tweakers.showtablecache' .. 33 ' WHERE Database_ = %q', dbname); 34 print('CONVERTING ' .. query .. ' ===&gt; ' .. newquery); 35 proxy.queries:append(1, string.char(proxy.COM_QUERY) .. newquery ) 36 return proxy.PROXY_SEND_QUERY 37 end 38 end 39 end</code></pre> <p>Save that script to pma.lua, start proxy with:</p> <pre><code>mysql-proxy -P :4041 --keepalive --proxy-backend-addresses=YOUR_SERVER:3306 --proxy-lua-script=pma.lua</code></pre> <p>Example output of mysql-proxy:</p> <pre><code>CONVERTING SHOW TABLE STATUS FROM `tweakers` ===&gt; SELECT Name_ AS `Name`, Engine_ AS `Engine`, Version, Row_format_ AS `Row_format`, Rows_ AS `Rows`, Avg_row_length, Data_length, Max_data_length, Index_length, Data_free, Auto_increment_ AS `Auto_increment`, Create_time, Update_time, Check_time, Collation_ AS `Collation`, Checksum, Comment_ AS `Comment`, Create_options FROM tweakers.showtablecache WHERE Database_ = "TWEAKERS" CONVERTING SHOW TABLE STATUS FROM `tweakers` ===&gt; SELECT Name_ AS `Name`, Engine_ AS `Engine`, Version, Row_format_ AS `Row_format`, Rows_ AS `Rows`, Avg_row_length, Data_length, Max_data_length, Index_length, Data_free, Auto_increment_ AS `Auto_increment`, Create_time, Update_time, Check_time, Collation_ AS `Collation`, Checksum, Comment_ AS `Comment`, Create_options FROM tweakers.showtablecache WHERE Database_ = "TWEAKERS" ...</code></pre> <p>Example output for metalogmon:</p> <p><center> <a href="//cdn.cppse.nl/63-result.jpg"><img border="0" src="//cdn.cppse.nl/63-large-thumb-result.jpg" /></a> </center></p> <h2 id="somenotes">Some notes</h2> <p>I never did any Lua scripting before, I find <a href="http://repl.it/">this website</a> quite useful as it provides a console for Lua (but for other languages as well). This way you can test some stuff without constantly restarting mysql-proxy.</p> <p>With regards to the caching, if you create a new table, this table will become visible once the cron updated the cache. So you don't want to set the delay for too long. You could do some extra scripting for encountered CREATE TABLE statements in mysql-proxy, or make a more advanced cron script that checks the faster "SHOW TABLES;" more often to see if any caching needs more priority.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 24 Mar 2013 00:00:00 +0000 ksh "pushd .", "popd" and "dirs" commands //blog.cppse.nl/ksh-pushd-popd-dirs<div class="datestamp">March 23 2013</div> <div class="h1"><a href="//blog.cppse.nl/ksh-pushd-popd-dirs"> <h1 id="kshpushd.popdanddirscommands">ksh "pushd .", "popd" and "dirs" commands</h1> </a></div> <h2 id="thenewkornshell">The New KornShell</h2> <p>I prefer ksh (Version JM 93u+ 2012-02-29) for my shell (with "set -o vi"). Not that it's <em>soooo</em> much better than bash as a cli (and it's probably pwnd by some of <a href="http://en.wikipedia.org/wiki/Z_shell#Features">zsh's features</a> like programmable autocomplete). But I do find it alot cleaner than bash for scripting. Anyway, currently I've given all machines I work with a /bin/ksh and chsh'd it for my user, but I noticed I missed bookmarking the current directory with "pushd ." for returning to it later with "popd" (after i.e. some subtask that will make you endup /nowhere/near/your/old/far/away/path).</p> <p>Sometimes you just don't want to open a new tab in screen. (And you are right, you could of course also use <a href="//blog.cppse.nl/gnu-screen-navigator-v2">goto.cpp</a> for bookmarking <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />) An alternative solution would be starting- and exiting a subshell.</p> <h2 id="found:dirstack.kshbyeddie">Found: dirstack.ksh by Eddie</h2> <p>So I googled and found <a href="http://stackoverflow.com/a/984666">this stack overflow answer</a> which has a pretty nice pushd/popd/dirs implementation. But it behaves a little different: "pushd /usr" bookmarks and changes to that directory (the normal behaviour). But what I often want is to store a directory right before I'm about to leave it. (Chances are I didn't use "pushd" but "cd" to get there in the first place.) Normally you simply use "pushd ." to put it on the stack (and ignore the useless changedir on the side <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />). But this implementation is explicitly designed so that the current directory is <em>already</em> (and always) the first (or zeroth position) on the stack and from that line of thought it would be "useless" to put it as a "duplicate" in the list. </p> <p>I still want to use pushd on $PWD, so I commented these four lines in dirstack.ksh:</p> <pre><code> # check for duplicate stack entries # current "top of stack" = ids; compare ids+dsdel to $PWD # either "ids" or "dsdel" must increment with each loop # (( ids = 1 )) # loop from bottom of stack up (( dsdel = 0 )) # no deleted entries yet while [ ids+dsdel -le sd ] ; do &gt;&gt;&gt;&gt; #if [ "${dir_stack[ids+dsdel]}" = "$PWD" ] ; then &gt;&gt;&gt;&gt; # (( dsdel = dsdel + 1 )) # logically remove duplicate &gt;&gt;&gt;&gt; #else if [ dsdel -gt 0 ] ; then # copy down dir_stack[ids]="${dir_stack[ids+dsdel]}" fi (( ids = ids + 1 )) &gt;&gt;&gt;&gt; #fi done</code></pre> <p><center> <a href="//cdn.cppse.nl/62-book.jpg"><img border="0" src="//cdn.cppse.nl/62-thumb-book.jpg" /></a> </center></p> <p>Then I remembered my book (that has caused me many jealous colleagues :+) also provided pushd and popd as examples in the appendix <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</p> <p>So I was curious to see if these were usable (the book is from 1995).</p> <h2 id="found:funpushdpopddirsbydavidg.kornhimself">Found: fun/{pushd|popd|dirs} by David G. Korn himself*</h2> <p>* by my guess</p> <p>SuSE provides these scripts in /usr/share/ksh/fun so I didn't need to type them in. If you need them, I tarballed them into <a href="//cdn.cppse.nl/kshfun.tar.gz">kshfun.tar.gz</a> (md5=7173831211d3d54f26f630f3cc720282). I was surprised by /usr/share/ksh/fun/dirs, they re-alias "cd" with a custom "_cd" function that does basically "pushd" all the time, with a stack of max. 32 dirs. That's a cool idea, you can view your stack with "dirs" or even use the "mcd" (menu change dir) command. You use this "cd" alias like "cd N" as well, where N is the index on the stack (given by "dirs" output). And pushd and popd work on this same stack.</p> <p>For your .kshrc profile:</p> <pre><code>. /usr/share/ksh/fun/popd &lt;&lt;&lt;&lt; includes pushd as well ** . /usr/share/ksh/fun/dirs &lt;&lt;&lt;&lt; if you also want the custom cd + dirs</code></pre> <p>**ls -althr /usr/share/ksh/fun:</p> <pre><code>total 16K -rwxr-xr-x 1 root root 2.5K Jun 6 2012 pushd -rwxr-xr-x 1 root root 2.3K Jun 6 2012 dirs lrwxrwxrwx 1 root root 5 Jan 19 23:43 popd -&gt; pushd &lt;&lt;&lt;&lt; symlink</code></pre> <h2 id="theseworkgreatexample">These work great! (example)</h2> <pre><code>trigen@ip-10-235-45-12:/etc/apache2&gt; cd /var/log/ /var/log trigen@ip-10-235-45-12:/var/log&gt; dirs 1) /var/log 2) /etc/apache2 3) /usr/local/src 4) ~ trigen@ip-10-235-45-12:/var/log&gt; cd 2 &lt;&lt;&lt;&lt; If you have a directory named '2', use "cd ./2" :P /etc/apache2 trigen@ip-10-235-45-12:/etc/apache2&gt; mcd 1) /etc/apache2 2) /var/log 3) /usr/local/src 4) ~ Select by number or enter a name: 3 /usr/local/src trigen@ip-10-235-45-12:/usr/local/src&gt;</code></pre> <p>or what I was talking about:</p> <pre><code>trigen@ip-10-235-45-12:/home/trigen&gt; cd /usr/local/lib /usr/local/lib trigen@ip-10-235-45-12:/usr/local/lib&gt; pushd . /usr/local/lib ~ /usr/local/lib trigen@ip-10-235-45-12:/usr/local/lib&gt; cd / / trigen@ip-10-235-45-12:/&gt; popd &lt;&lt;&lt;&lt; In this trivial example "cd -" (where - is previous dir) would also work. /usr/local/lib ~ /usr/local/lib trigen@ip-10-235-45-12:/usr/local/lib&gt;</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 23 Mar 2013 00:00:00 +0000 Generating gradients //blog.cppse.nl/gradients<div class="datestamp">January 13 2013</div> <div class="h1"><a href="//blog.cppse.nl/gradients"> <h1 id="generatinggradients">Generating gradients</h1> </a></div> <p>Code that generates following images <a href="//cdn.cppse.nl/61-test.html">script here</a></p> <p><center> <a href="//cdn.cppse.nl/61-61-gradients2.jpg"><img border="0" src="//cdn.cppse.nl/61-large-thumb-61-gradients2.jpg" /></a></p> <p><a href="//cdn.cppse.nl/61-61-gradients.jpg"><img border="0" src="//cdn.cppse.nl/61-large-thumb-61-gradients.jpg" /></a></p> <p><a href="//cdn.cppse.nl/61-61-gradients3.jpg"><img border="0" src="//cdn.cppse.nl/61-large-thumb-61-gradients3.jpg" /></a> </center></p> <p>More <a href="//cdn.cppse.nl/61-test2.html">elaborate version here</a></p> <p><center> <a href="//cdn.cppse.nl/61-61-gradients2-1.jpg"><img border="0" src="//cdn.cppse.nl/61-large-thumb-61-gradients2-1.jpg" /></a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 13 Jan 2013 00:00:00 +0000 spf13 vim distribution //blog.cppse.nl/spf13-vim-distribution<div class="datestamp">December 29 2012</div> <div class="h1"><a href="//blog.cppse.nl/spf13-vim-distribution"> <h1 id="spf13vimdistribution">spf13 vim distribution</h1> </a></div> <p>It's really nice that at least one of my creations (phpfolding.vim) is really being picked up by others. I.e.: on Stackoverflow <a href="http://stackoverflow.com/questions/792886/vim-syntax-based-folding-with-php#answer-11859632">[1]</a>, <a href="http://stackoverflow.com/questions/3173963/useful-vim-plugins-for-web-development-and-design-php-html-css-javascript">[2]</a>, in China <a href="http://www.karakaram.com/phpfolding">[1]</a>, Japan <a href="http://d.hatena.ne.jp/hchuno/20101104/1289466481">[1]</a>, <a href="http://www.ooso.net/archives/183">[2]</a>, France <a href="http://www.miximum.fr/methodes-et-outils/39-amusons-nous-avec-les-folds-dans-vim#comment-1367">[1]</a>, Holland <a href="http://andries.filmer.nl/kb/Vim-Cheatsheat-Een-geweldige-editor-met-een-lange-leer-curve/28#phpFolding">[1]</a>, someone made a <a href="https://aur.archlinux.org/packages.php/gnome.org/packages.php?ID=63478">package for archlinux</a>, creator of spf13 distribution listed as one of <a href="http://spf13.com/post/the-15-best-vim-plugins/">15 best vim plugins</a>. Also found a few forks on github which is really awesome, will check out if I should merge their changes back soon. These are just a few links I found now with a simple google search. This makes me regret being a little reluctant with reading my hotmail account.</p> <p><center><img border="0" src="//cdn.cppse.nl/59-screenshots.gif" /></center></p> <p>I'm a bit proud it's part of the excellent spf13 vim distribution. I now use spf13 myself on my linux servers, it suprised me with some features I think are extremely smart: Like the use of numbers for each line relative to your cursor, so you can easily make X line(s) jumps or yank the correct amount of lines easily! Also the CTRL + P quick navigation that works with acronyms as well, ,, + direction key for quick jumping. And the included themes are also very nice.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 29 Dec 2012 00:00:00 +0000 phpfolding.vim : Automatic folding of PHP functions, classes,.. (also folds related PhpDoc) //blog.cppse.nl/phpfolding-vim<div class="datestamp">December 29 2012</div> <div class="h1"><a href="//blog.cppse.nl/phpfolding-vim"> <h1 id="phpfolding.vim:automaticfoldingofphpfunctionsclasses..alsofoldsrelatedphpdoc">phpfolding.vim : Automatic folding of PHP functions, classes,.. (also folds related PhpDoc)</h1> </a></div> <p class="highlight"> To everybody that contacted me, and whom I didn't reply to from my hotmail address: please <a href="#hotmailsucks">read this</a>, I had problems with the Hotmail Junk filter *again* sorry! </p> <p>This script can fold PHP functions and/or classes, properties with their PhpDoc, without manually adding marker style folds ({{{ and }}}). It will fold stuff into something like the following screenshot.</p> <!-- <center><img border="0" src="//cdn.cppse.nl/58-screenshot.jpg" /></center> --> <p><center><a href="//cdn.cppse.nl/58-folding.gif"><img border="0" src="//cdn.cppse.nl/58-large-thumb-folding.gif" /></a></center></p> <h2 id="features">Features</h2> <ul> <li>It remembers fold settings. If you add functions and execute the script again, your opened folds will not be closed.</li> <li>It will not be confused by brackets in comment blocks or string literals.</li> <li>The folding of class properties with their PhpDoc comments.</li> <li>The folding of all class properties into one fold.</li> <li>Folding the original marker style folds too.</li> <li>An "**" postfixing the fold indicates PhpDoc is inside (configurable).</li> <li>An "**#@+" postfixing the fold indicates PhpDocBlock is inside (configurable).</li> <li>Empty lines postfixing the folds can be configured to be included in the fold.</li> <li>Nested folds are supported (functions inside functions, etc.)</li> </ul> <h2 id="future">Future</h2> <ul> <li>Better 'configurability' as opposed to editting the PHPCustomFolds() function and some "Script configuration" global variables.</li> </ul> <h2 id="compatibility">Compatibility</h2> <p>This script is tested successfully with Vim version >= 6.3 on windows and linux (With 6.0 it works <em>sometimes</em>, I don't recommend using it in that version)</p> <p>But I'd recommend using a new vim, at the time of writing 7.3 works fine with it!</p> <h2 id="spf13svimdistribution">spf13's vim distribution</h2> <p>You can also <a href="http://vim.spf13.com/">install spf13's vim distribution</a> (if you like it). phpfolding.vim is included in this package too. I'm testing it out at the moment, like it so far. Kind of missed my mappings though, so I still added &lt;F5>, &lt;F6> and &lt;F7> to my .vimrc.</p> <p>Also I noticed EnableFastPHPFolds gives better results now better than EnablePHPFolds, this is what I experienced at work anyway, with vim 7.3. This used to be the other way around.. </p> <h2 id="install">INSTALL</h2> <ol> <li>Put phpfolding.vim in your plugin directory (~/.vim/plugin)<br/>i.e., wget: <a target="_blank" href"http://www.vim.org/scripts/script.php?script_id=1623">http://www.vim.org/scripts/script.php?script_id=1623</a></li> <li>You might want to add the following keyboard mappings to your .vimrc: <pre> map &lt;F5> &lt;Esc>:EnableFastPHPFolds&lt;Cr> map &lt;F6> &lt;Esc>:EnablePHPFolds&lt;Cr> map &lt;F7> &lt;Esc>:DisablePHPFolds&lt;Cr> </pre></li> <li>You might want to add the following lines to php.vim in your after/ftplugin directory (~/.vim/after/ftplugin/php.vim), this will be executed after opening a .php file: <pre> " Don't use the PHP syntax folding setlocal foldmethod=manual " Turn on PHP fast folds EnableFastPHPFolds </pre></li> <li>It might be necessary that you load the plugin from your .vimrc, i.e.: <pre> let php_folding=0 (if you can't use the after directory in step 3) source ~/path/to/phpfolding.vim (if you're not using the default plugin directory) </pre></li> </ol> <h2 id="knownissues">Known issues</h2> <ol> <li>C++ style commented brackets can still interfere with the bracket matching. <BR/>For example comments like are not recognized as comments: // old: for (...) { <BR/>Whereas C-style comments are, e.g.: /* old: for (...) { */</li> <li>The following won't be matched with the Regex in FindFoldStart(): <BR/>function (..., <BR/> ...., <BR/> ...., <BR/>) { <BR/>Though Steve McConnell (writer of Code Complete) would ask why you'd need more then two lines for function parameters :)</li> </ol> <h2 id="note:myhotmailjunkfilterprobablyateasomee-mailsireceivedfromvim.organamehotmailsucksa">Note: My Hotmail Junk filter probably ate a some e-mails I received from vim.org <a name="hotmailsucks"></a></h2> <p>I use my hotmail address for accounts where my e-mail is publicaly displayed, like in my profile page on vim.org. (I don't want spam in my primary e-mail inbox). So I only check my e-mail this hotmail address every now and then, probably every 6 months and it's full of stuff spam so I could have easily missed e-mails regarding phpfolding. I discovered in 2010 that there was feedback in my Junk filter, so I turned the Junk filter off, anyways that's how I remember it (and how I wrote it down). Now in 2012 I check the Junk filter again, and there is another email. Junk is automatically deleted after X days, so who knows how many mails I might have missed.</p> <p>Really don't understand why it got into Junk, this specific person wrote hello, introduced himself, said something nice and signs off the e-mail. All in <em>normal</em> letter formatting, no URL's or e-mail addresses used, no images, just plain text. It was a valid gmail address, there were no weird Headers, and it was sent from gmail itself not some other smtp server...</p> <p>You can contact me here by commenting, or send me an e-mail to the new one I use in my <a href="http://www.vim.org/account/profile.php?user_id=10556">profile at vim.org</a>.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 29 Dec 2012 00:00:00 +0000 Visualizing (inverse) square root optimizations //blog.cppse.nl/visualizing-sqrt-approximations<div class="datestamp">December 25 2012</div> <div class="h1"><a href="//blog.cppse.nl/visualizing-sqrt-approximations"> <h1 id="visualizinginversesquarerootoptimizations">Visualizing (inverse) square root optimizations</h1> </a></div> <p>A few years ago I experimented with a few of these approximation functions, and then I decided not to use them for some reason. (I forgot--, maybe they weren't a performance gain at the time.) I remember vaguely getting weird (but cool) results with some approximation formula. </p> <p>Then I encountered <a href="https://github.com/DavidHulsmanNL/RSqrtBenchmark/blob/master/src/main.cpp">David Hulsman's RSqrtBenchmark</a> by accident: a collection of sqrt approximations he benchmarked for performance and accuracy. I found this a very cool idea, and wondered how these different sqrt approximations would visualize.. (having this faint memory of cool visual artifacts).</p> <p>But apparently all these approximations are extremely good! :P Visually indistinguishable even! In this image each row represents a different sqrt implementation, and a white to black gradient is drawn with it:</p> <p><center> <a href="//cdn.cppse.nl/57-result.jpg"><img border="0" src="//cdn.cppse.nl/57-large-thumb-result.jpg" /></a> </center></p> <p>I tried different visual representations of course, where for some functions you can see very small differences along the edges (if you swap them real fast). If you view the images side-by-side you won't see anything.</p> <p>Of course there are more differences, you see them when comparing, like for example.. (<em>left: compare sqrt &lt;> lomont's InvSqrt, right: sqrt &lt;> carmack D3RSqrt</em>)</p> <p><center> <a href="//cdn.cppse.nl/57-diff_sqrt_lomont_invsqrt.jpg"><img border="0" src="//cdn.cppse.nl/57-thumb-diff_sqrt_lomont_invsqrt.jpg" /></a> <a href="//cdn.cppse.nl/57-diff_sqrt_carmack_d3rsqrt.jpg"><img border="0" src="//cdn.cppse.nl/57-thumb-diff_sqrt_carmack_d3rsqrt.jpg" /></a> </center></p> <p>But I wanted my anomolies! :P so I tried tracking down the function I might have used back then, but I couldn't find it anywhere in my code repositories but after searching the internet for a while as well I now think it probably was something based on this <a href="http://www.azillionmonkeys.com/qed/sqroot.html#distance">very crude approximation</a>:</p> <p><center> <a href="//cdn.cppse.nl/57-metric.jpg"><img border="0" src="//cdn.cppse.nl/57-thumb-metric.jpg" /></a></p> <p>(And yes it really is a very crude approximation :P)</p> <p><a href="//cdn.cppse.nl/57-300495.jpg"><img border="0" src="//cdn.cppse.nl/57-large-thumb-300495.jpg" /></a> </center></p> <p>So I kind of wasted way too much of my time on this already, but while searching the internet I encountered a few functions and got curious how they would visualize. I added a few, and maybe i'll add them to RSqrtBenchmark later:</p> <p><center> <a href="//cdn.cppse.nl/57-results.jpg"><img border="0" src="//cdn.cppse.nl/57-large-thumb-results.jpg" /></a> </center></p> <p>Some of these do actually have visual artifacts. You can see those more clearly in the <a href="//cdn.cppse.nl/uncompressed_imgs.zip">zipped uncompressed images</a>. &lt;&lt; I included radial blur images (1024x1024 px) for all functions.</p> <p>By the way--I thought this one I generated while with a programming mistake was also pretty cool, almost fractal-like <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p><center> <a href="//cdn.cppse.nl/57-2hsieh_fred_sqrt.jpg"><img border="0" src="//cdn.cppse.nl/57-thumb-2hsieh_fred_sqrt.jpg" /></a></p> <p><!-- (<a href="//cdn.cppse.nl/57_uncompressed2hsieh_fred_sqrt.jpg">uncompressed version here</a>) --> </center></p> <p>(Edit 29-12-2012: uploaded source <a href="https://bitbucket.org/rayburgemeestre/sqrtvis">to bitbucket repository</a>)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Tue, 25 Dec 2012 00:00:00 +0000 Configure highlight weechat in screen and putty //blog.cppse.nl/highlighting-weechat-in-screen-and-putty<div class="datestamp">December 9 2012</div> <div class="h1"><a href="//blog.cppse.nl/highlighting-weechat-in-screen-and-putty"> <h1 id="configurehighlightweechatinscreenandputty">Configure highlight weechat in screen and putty</h1> </a></div> <p>Recently discovered <a href="http://weechat.org">weechat</a>, and I'm quite impressed with it!</p> <p>Out of the box nicklist, nick colouring.., it just looks awesome: <a href="http://www.weechat.org/screenshots/">http://www.weechat.org/screenshots/</a> </p> <p>I am used to my IRC client to flash it's window when my name is highlighted. You can achieve this with the script bleep.pl As qbi comments over <a href="http://unix.stackexchange.com/questions/53816/how-to-use-the-beep-pl-for-weechat">here</a>:</p> <blockquote> <p>You can download beep.pl: cd ~/.weechat/perl &amp;&amp; wget http://www.weechat.org/files/scripts/beep.pl. Now you can use in weechat: /perl load beep.pl <BR>&nbsp;</p> </blockquote> <p>If you use multiple windows in screen, and your weechat client is not the active one, you get a "Bell in window %n" message instead of an actual bell in the terminal. You can change that message with CTRL+A, :bell_msg "^G". Just as %n will be replaced with the window number, ^G is replaced with an actual bell. (source: <a href="http://www.delorie.com/gnu/docs/screen/screen_64.html">http://www.delorie.com/gnu/docs/screen/screen_64.html</a>)</p> <p>In PuTTY: Change settings->Terminal -> Bell -> Taskbar/caption indication on bell: (x) Flashing. Also don't forget to use <a href="https://puttytray.goeswhere.com/">FauxFaux build</a> of putty as it has lots of cool additional features like clickable links, ctrl+scrollwheel for changing font size, minimize to tray, ... .</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 09 Dec 2012 00:00:00 +0000 Meta log monitor (or scriptable tail with GUI) //blog.cppse.nl/meta-log-monitor<div class="datestamp">November 28 2012</div> <div class="h1"><a href="//blog.cppse.nl/meta-log-monitor"> <h1 id="metalogmonitororscriptabletailwithgui">Meta log monitor (or scriptable tail with GUI)</h1> </a></div> <p>I created the next best thing in the log monitoring world! <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /> It's like unix 'tail' but with additional features!! The tool pipes all data from the logfile through a customizable java-script before presentation. So you can change the behaviour on the fly.</p> <p>The ability to script the behaviour allows you to use the following features in your logfiles (or custom logfiles). You could for example pretty-print in the "main log" output using colors, extra indenting, or enrich it with related information. But the main idea for the tool is that you can add references or bookmarks to important parts inside the log in a separate listing, the 'meta log', and thus create a more general overview of the log's output for yourself.</p> <p><center> <img border="0" src="//cdn.cppse.nl/55-METALOGMON.jpg" /> </center></p> <p>It displays (and parses) the logfile realtime as it behaves like tail in that sense, and it's fast because it is written in C++ but also because it uses Google's V8 engine for Javascript, which is known to be a very fast implementation. It's also quite handy that you can script and reload at runtime, so you can tweak your logfile easily with real data. The program takes around 4MB of memory, initially and grows as it logs the file of couse, it doesn't remove stuff from its outputs. There are shortcuts in case you wish to flush current output (ALT+C = clear log).</p> <h2 id="example:webdevelopmentwithmysqlandotherstuff">Example: Webdevelopment with MySQL (and other stuff)</h2> <p>While developing it can be useful to see all queries that are being executed in a specific request. Especially if you are working with software you don't know very well yet. If you have a proper database layer in your system you can probably log the queries to a file from one specific place in the code. But if you don't have that, and legacy PHP code for instance uses mysql_** functions, you cannot make a centralized change. In that case you can use mysql-proxy to sit between your software and the actual server.</p> <p>You have to start it with a script like this:</p> <pre> local log_file = 'mysql.log' local fh = io.open(log_file, "a+") function read_query( packet ) if string.byte(packet) == proxy.COM_QUERY then local query = string.sub(packet, 2) fh:write(string.format("@@begin@@\n%s\n%6d\n%s\n@@end@@\n", os.date('%Y-%m-%d\n%H:%M:%S'), proxy.connection.server["thread_id"], query)) fh:flush() end end </pre> <p>To have it output log entries in the following format:</p> <pre> @@begin@@ 2012-11-25 << date 19:48:58 << time 786455 << thread id (probably useless) SELECT << query * << ,, FROM << ,, some_tabl; << ,, @@end@@ </pre> <p>So If you use my tool to tail an output file like that, with the script 'sqlonly.js' loaded. If you were to make a request that would send data to the database, It would display something like this:</p> <p><center> <a href="//cdn.cppse.nl/55-screenshot1.jpg"><img border="0" src="//cdn.cppse.nl/55-large-thumb-screenshot1.jpg" /></a> </center></p> <p>I have obfuscated the actual queries though ( line.replace(/[a-z]/g, 'x') IIRC).</p> <p>In the screenshot, in the meta log specific query types have been given different colours, by focusing on the blue text for example you can see what was executed inside a transaction. Also I hadded a column "Origin": at work I use a different script because I abuse the mysql-proxy and send it other types of data as well. Like MongoDB queries, engine calls, memcached calls and JML queries. Like this for example: $db->select('/**@mongodb &lt;some stuff>*/'); It will be proxied to MySQL, which is the dirty part, but it is considered a comment so nothing happens. But I parse this comment and so something cool with everything between the comments. You can dump a lot of stuff between the C-style comments, for example a print_r if you like, and simply add a single meta log line with "MongoDB" as the "Origin".</p> <p><center> <a href="//cdn.cppse.nl/55-PHP_ERRORS.jpg"><img border="0" src="//cdn.cppse.nl/55-thumb-PHP_ERRORS.jpg" /></a> </center></p> <p>Another thing I setup is a .htaccess files in my development environment that sets the php error_log to the same file. I write this down just to give you some ideas. I even use it for debugging now: $something->select('/* contents: ' . print_r($obj,1) . '*/'); It was not why I made this tool initially.</p> <h2 id="metalogmonusage">"metalogmon" Usage</h2> <p>Personally I prefer a quickstart link (those you can start with WINKEY+{1,2,3,...}). On Windows 7 it is really nice that if you already started to log monitor it makes the existing window active. Which allows for even easier navigation to it then alt+tab.</p> <pre> Usage: metalogmon.exe [/h] [/s <str>] /t <str> /h, --help displays help on the command line parameters /s, --script=<str> javascript file that handles the parsing /t, --tail=<str> the file to tail The value for the option 't (or tail)' must be specified. Example: "metalogmon.exe /t \\networkshare\something\mysql.log /s C:\path\to\sqlonly.js" </pre> <p>Keyboard:</p> <ul> <li>CTRL+C (copy selected lines from main log to clipboard)</li> <li>ALT+C (clear output in main- and meta log)</li> <li>ALT+F4 (exit program :P)</li> </ul> <p>Toolbar:</p> <table style="margin-left: 20px;"> <tbody> <tr><TD><img border="0" src="//cdn.cppse.nl/55-collapse_all.jpg" /></TD><TD>Currently does not do anything.</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-copy.jpg" /></TD><TD>Copy selected lines to clipboard (or CTRL+C)</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-clear.jpg" /></TD><TD>Clear all output (or ALT+C)</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-reload.jpg" /></TD><TD>Process entire logfile (default metalogmon will seek to end of log and tail there)</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-stop.jpg" /></TD><TD>Stop tailing, halts tail command.</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-vim.jpg" /></TD><TD>Open the active logfile in gvim.</TD></tr> <tr><TD><img border="0" src="//cdn.cppse.nl/55-vim.jpg" /></TD><TD>Open the active script in gvim.</TD></tr> </tbody> </table> <h2 id="features">Features</h2> <ul> <li>You can enable an 'idle' bar, if the log is idle for more than two seconds it adds a marker. For webdevelopment this is (for me anyways) good enough to separate consecutive requests. (Note that you could log a request start explicitely)</li> <li>It detects log rotation/truncation. It will seek to the beginning of the file and simply continue.</li> <li>When resizing the window it splits the main- and meta log 50/50.</li> <li>Mainlog: customizable background and foreground color per line. Lines > 512 are truncated, but when you copy &amp; paste them to clipboard they won't be truncated.</li> <li>Metalog: customizable background and foreground color per line, customizable column names and sizes. (largest column gets extra remaining space)</li> <li>Reload your script at runtime.</li> <li>Search case sensitive or insensitive in the main log.</li> </ul> <h2 id="scripts">Scripts</h2> <p>Some included scripts </p> <ul> <li>sample.js: simple example that you can use as a base for your own script.</li> <li>sqlonly.js: simple example that parsers sql queries from my log.lua mysql-proxy output.</li> <li>sqlphplog.js: sqlonly.js extended with parsing for php's error_log.</li> </ul> <p>Contents of sample.js:</p> <pre class="prettyprint lang-js"><code data-language="javascript">/** * Example script, display all lines prefixed with line number in main log, and * create a meta log entry for every 100th line. Meta log has two columns. * * $author: Ray Burgemeestre$ * * $date: 2012-12-06$ */ // Implemented API functions function getColumns() { return [['Line', 75], ['Message', 200]]; } function handleLine(num, line) { var newline = log(num + ': ' + line); if ((num % 100) == 0) metalog(newline, ['' +num, 'Shortcut to ' + num + 'th line', '']); } </code></pre> <h2 id="theapi">The API</h2> <p>Expects you to implement mandatory functions (see sample.js):</p> <ul> <li>function <strong>getColumns</strong>() <ul><li>return: (string[]) array: [[colname, size], [another, size], ...]</li></ul></li> <li>function <strong>handleLine</strong>(num, line) <ul><li>param: (int) num, current line number</li> <li>param: (string) line, the current line from log</li> <li>return: (void)</li></ul></li> </ul> <p>Optionally (see sqlonly.js):</p> <ul> <li>function <strong>getIdleColors</strong>() - implementing enables the idle bar I mentioned <ul><li>return: (string[]) array: [forground color, background color]</li></ul></li> <li>function <strong>getIdleMetaLog</strong>() - what text to display in idle bar in meta log, @message@ is replaced with "Idle for xx seconds". <ul><li>return: (string[]) array: [column value, column value, ...]</li></ul></li> </ul> <p>You have at your disposal:</p> <ul> <li>function <strong>log</strong>(message, foreground color, background color) -- add log in main log <ul><li>param: (string) message or array of format: [message, clipboard-message].</li> <li>if you want a different message to be printed in the log and another message to be copied to clipboard you can use the array version.</li> <li>param: (string) foreground color (i.e., 'red', or '#ff0000')</li> <li>param: (string) background color ,, ,,</li> <li>return: (int) the line number in the main log</li></ul></li> <li>function <strong>metalog</strong>(num, colvalues, foreground color, background color) -- add log in meta log <ul><li>param: (int) num -- line number in main log, if you click this item in the meta log, it will scroll to this line in the main log.</li> <li>param: (string[]) array: [first column value, second column value, ...].</li> <li>param: (string) foreground color (i.e., 'red', or '#ff0000')</li> <li>param: (string) background color ,, ,,</li></ul></li> <li>function <strong>md5</strong>(str) -- calculate md5 hash for given string <ul><li>param: (string) input</li> <li>return: (string) md5 hash.</li></ul></li> </ul> <h2 id="roadmap">Roadmap</h2> <p>I used to use a version of this tool to monitor a debug log for a multithreaded transaction processing system. Each thread would get their own text colour which was cool, and important parts in the log were logged in the meta part. It wasn't scriptable then, so no javascript was used, it had a few more features that may be reinstated in the future (If there is demand for it):</p> <ul> <li>Tree view for grouping log messages per thread</li> <li>Multiple meta log controls</li> <li>Toggable filters for what-not to log in meta log.</li> <li>Tail multiple files, combine output in log view.</li> <li>Tail database tables through ODBC connection.</li> </ul> <p>TODO:</p> <ul> <li>Copy &amp; paste from the meta log.</li> </ul> <h2 id="download">Download</h2> <p>Download here: <a href="//cdn.cppse.nl/metalogmon-1.0.zip">metalogmon-1.0.zip</a>.</p> <p>In order to run the executable you may need to install the <a href="http://www.microsoft.com/en-us/download/details.aspx?id=30679">Visual C++ Redistributable for Visual Studio 2012</a> from microsoft. (My guess is that you don't need to do that if you run windows 8.)</p> <p>Other platforms: both these libraries compile to windows, linux and osx. But currently I only made binaries for windows. If somebody is interested in another OS I will create the binaries, but until then I'll be lazy <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <!-- printf "metalogmon.exe" | md5sum --> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 28 Nov 2012 00:00:00 +0000 Adding network multiplayer to Smash Battle //blog.cppse.nl/smashbattle<div class="datestamp">October 21 2012</div> <div class="h1"><a href="//blog.cppse.nl/smashbattle"> <h1 id="addingnetworkmultiplayertosmashbattle">Adding network multiplayer to Smash Battle</h1> </a></div> <p><a href="http://smashbattle.condor.tv/">Smash Battle</a> is a really cool game made by Bert Hekman and Jeroen Groeneweg. Of which Jeroen is now a collegue of mine at <a href="http://tweakers.net/plan/crew/419">Tweakers.net</a>. It supports up to four players, you can use gamepads and the multiplayer is best out of five. You can get powerups in the game like extra damage, health pack, armor etc. :) (I see that in the codebase also a single player is under development!)</p> <p><center> <a href="//cdn.cppse.nl/54-screenshot_121021-191459.jpg"><img border="0" src="//cdn.cppse.nl/54-thumb-screenshot_121021-191459.jpg" /></a> <a href="//cdn.cppse.nl/54-SMASHBATTLE3.jpg"><img border="0" src="//cdn.cppse.nl/54-thumb-SMASHBATTLE3.jpg" /></a> </center></p> <h2 id="networkversion">Network version</h2> <p>I decided to add a network multiplayer gametype to it, and I develop that in a separate branch. It supports more than four players.</p> <p>Currently the network multiplayer supports only bullets and mines (your default equipment). Powerups do not yet appear. All damage to players or tiles is determined on the server. The clients are trusted though, to send their correct player positions, shots fired etc. You could theoretically cheat (up to a certain level) by modifying and compiling your own client, but it is far easier to implement a network multiplayer if I can trust clients somewhat. This can easily be rewritten though, and if you play with low lags you will probably not notice any difference. But I think you will notice if someone is cheating.</p> <h2 id="pre-alphatestrelease">Pre-alpha test release</h2> <p>My fork is <a href="https://bitbucket.org/rayburgemeestre/smashbattle">https://bitbucket.org/rayburgemeestre/smashbattle</a></p> <p>It's a pre-alpha because the gametype is not completely finished yet, if there are more than two players a normal best out of five multiplayer starts. Once the game has started, you cannot join the server anymore. You can already test it out simply install the current release of <a href="http://smashbattle.condor.tv/download/">Smashbattle</a>.</p> <p>On Windows:</p> <ul> <li>It will default install to C:\Program Files\Smash Battle\</li> <li>Extract the contents of <a href="smashbattle-pre-alpha.zip">smashbattle-pre-alpha.zip</a> into your installation dir. (Use BattleXP.exe if you're running Windows XP)</li> <li>If you changed the install location of it, modify the path in register_server.reg. </li> <li>Double click the .reg file, now smashbattle:// protocol should be available.</li> </ul> <p>On Ubuntu:</p> <ul> <li><p>Put this line in your /etc/apt/sources.list: deb http://repository.condor.tv lucid main</p></li> <li><p>apt-get update</p></li> <li>apt-get install battle</li> <li>cd /usr/share/games/smashbattle/ &lt;&lt;&lt;&lt;&lt; important! the game expects level data in ".".</li> <li>./battle</li> </ul> <p>Note that the update command might give you this if you are running 64 bit:</p> <pre><code>Ign http://us.archive.ubuntu.com quantal-backports/universe Translation-en_US Fetched 1,032 kB in 30s (33.7 kB/s) W: Failed to fetch http://repository.condor.tv/dists/lucid/main/binary-amd64/Packages 404 Not Found E: Some index files failed to download. They have been ignored, or old ones used instead.</code></pre> <p>You can ignore this error and continue installing the 32 bit version.</p> <p>The game should now run, but to use the pre-alpha, you have to replace the 'battle' binary with mine:</p> <ul> <li>rm -f battle &amp;&amp; wget http://blog.cppse.nl/battle &amp;&amp; chmod +x battle</li> <li>Edit: you may need to: apt-get install libsdl-net1.2</li> </ul> <h2 id="serverusage">Server usage</h2> <p>You can start your server with these parameters:</p> <pre><code>Battle.exe -s Example: Battle.exe -s Battle.exe -s &lt;listen port&gt; Example: Battle.exe -s 1100 Battle.exe -s &lt;levelname&gt; &lt;listen port&gt; Example: Battle.exe -s "TRAINING DOJO" 1100</code></pre> <p>In case no level is given as a parameter, the server will start with level selector. In case no port is given, default will be used (1100).</p> <h2 id="clientusage">Client usage</h2> <p>To connect to a client, you need to have registered the .reg file. You can click links like:</p> <pre><code>smashbattle://&lt;domain.tld&gt;:&lt;port&gt; Example: smashbattle://cppse.nl:1100</code></pre> <p>You could type such an url in your WINDOWS+R (Run command) or in command prompt start &lt;url>.</p> <p>If you do not like to register the .reg file, you can also give it to Battle.exe as a parameter:</p> <pre><code>Battle.exe &lt;url&gt; Example: Battle.exe smashbattle://cppse.nl:1100</code></pre> <p>After you have set a server on your machine, you should be able to connect using ---> <a href="smashbattle://localhost:1100">smashbattle://localhost:1100</a></p> <h2 id="thelevelnamesare">The level names are</h2> <pre><code>"TRAINING DOJO" "PLATFORM ALLEY" "PITTFALL" "DUCK'N'HUNT" "COMMON GROUNDS" "POGOSTICK" "LA MOUSTACHE" "THE FUNNEL" "BLAST BOWL" "PIT OF DEATH" "RABBIT HOLE" "STAY HIGH" "PIE PIT" "SLIP'N'SLIDE" "BOULDERDASH" "SNOW FIGHT"</code></pre> <h2 id="ingamecontrols">In game controls</h2> <ul> <li>F1 - toggle console (for debugging)</li> <li>F10 - toggle fullscreen</li> <li>F11 - toggle FPS</li> <li>ESCAPE - pause menu</li> </ul> <p>Default your keyboard controls are</p> <ul> <li>a, s, d, w - left, duck, right, jump.</li> <li>CONTROL - shoot</li> <li>ALT - mine</li> <li>SHIFT - run</li> </ul> <h2 id="roadmap">Roadmap</h2> <ul> <li><strike>Link gameloop to time</strike></li> <li><strike>Create client and server class</strike></li> <li><strike>Design efficient protocol</strike></li> <li><strike>Create solution for lag</strike></li> <li>Introduce nicknames</li> <li>Introduce ingame chat</li> <li><strike>Support powerups</strike></li> <li>Introduce more gametypes? <ul><li>Red team vs Blue team</li> <li>King of the Hill</li> <li>Capture the flag</li></ul></li> <li>Refactor*</li> <li>Merge with master</li> </ul> <p>Refactoring:</p> <p>While developing I sometimes put #include's above the function where I use stuff from it. This is when I feel like I might refactor the code, I can easily remove the #include again. Works for me, but it results in some stray #include's. Also I'm not sure about my design choice of making server and client singleton's (basically global classes). It was easy so I could weave the client/server code into the game rapidly, but I think it may need to integrate with the existing classes better, and use polymorphism a bit more here and there. Example: I have a few places in the code where I do different stuff based on Main::runmode static global, for server do this, for client do this.. </p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 21 Oct 2012 00:00:00 +0000 Firefox select behaviour and H264 support //blog.cppse.nl/firefox-select-behaviour-in-address-bar-linux<div class="datestamp">October 21 2012</div> <div class="h1"><a href="//blog.cppse.nl/firefox-select-behaviour-in-address-bar-linux"> <h1 id="firefoxselectbehaviourandh264support">Firefox select behaviour and H264 support</h1> </a></div> <h2 id="selectbehaviourinaddressbarinlinux">Select behaviour in address bar in Linux</h2> <p>I use <code>control + arrow keys</code> and <code>control + shift + arrow keys</code> for selecting a lot. And as a webdeveloper especially in the address bar. I think it is somehow the default under linux distributions, under OpenSuse anyways, that <em>always</em> <em>all</em> text is selected. I find that very VERY annoying. Because you cannot quickly select (a) piece(es) from the URL. But luckily I found the config setting where you can change this! <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p><center> <a href="//cdn.cppse.nl/53-about_config.jpg"><img border="0" src="//cdn.cppse.nl/53-large-thumb-about_config.jpg" /></a> </center></p> <h2 id="enableh264supportinwindows">Enable H264 support in Windows</h2> <p>In about:config, enable the value <strong>media.windows-media-foundation.enabled</strong>. Especially useful if you disable Flash. A lot of video players use a HTML5 player as fallback support only the H264 codec.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 21 Oct 2012 00:00:00 +0000 Cool way to provide multiple iterators for your class (C++) //blog.cppse.nl/cpp-multiple-iterators-with-traits<div class="datestamp">October 19 2012</div> <div class="h1"><a href="//blog.cppse.nl/cpp-multiple-iterators-with-traits"> <h1 id="coolwaytoprovidemultipleiteratorsforyourclassc">Cool way to provide multiple iterators for your class (C++)</h1> </a></div> <h2 id="multipleiteratorsthatusethesamebeginorendfunctions.">Multiple iterators that use the same begin() or end() functions.</h2> <p>In C++ you cannot differentiate based on the type-to-return. Like have two begin() methods in a class that return different iterators.</p> <pre class="prettyprint lang-cpp"><code data-language="c">class foo { public: some_iterator begin() { return some_iterator(); } // Not possible other_iterator begin() { return other_iterator(); } }; some_iterator it = fooinstance.begin(); </code></pre> <p>There is also no template syntax to implement a begin() method for this purpose. Note that you cannot use "straightforward" polymorphism because the subclasses are on the LHS of the assignment.</p> <h2 id="istillwanteditthoughandcameupwiththefollowingsolution.imgidimg1srccdn.cppse.nlsuper_mini_kao_ani_by_3dera.gifaltimg1title:">I still wanted it though and came up with the following solution. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)"></h2> <p>For example an instance of a NumberRange class provides two iterators the default "iterator" simply outputs all the numbers. The "cumulative_iterator" outputs all numbers cumulatively.</p> <pre class="prettyprint lang-cpp"><code data-language="c">int main(int argc, char **argv) { NumberRange range(1, 10); cout &lt;&lt; &quot;NumberRange::iterator:&quot; &lt;&lt; endl; for (NumberRange::iterator iter = range.begin(); iter != range.end(); iter++) cout &lt;&lt; *iter &lt;&lt; endl; cout &lt;&lt; &quot;NumberRange::cumulative_iterator:&quot; &lt;&lt; endl; for (NumberRange::cumulative_iterator iter = range.begin(); iter != range.end(); iter++) cout &lt;&lt; *iter &lt;&lt; endl; return EXIT_SUCCESS; } /** * Desired output: * ----------------------------------------------------------- * ksh$ g++ iterators.cpp &amp;&amp;./a.out * Constructing NumberRange object with numbers 1 to 10 * NumberRange::iterator: * 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 * NumberRange::cumulative_iterator: * 1 * 3 * 6 * 10 * 15 * 21 * 28 * 36 * 45 * 55 */ </code></pre> <p>I really like this as an API because if you want to change the way of iterating through the range (in this example), you <em>only</em> change NumberRange::iterator to something else.</p> <h2 id="implementationofnumberrange">Implementation of NumberRange</h2> <pre class="prettyprint lang-cpp"><code data-language="c">class NumberRange { public: NumberRange(int rangebegin, int rangeend) { cout &lt;&lt; &quot;Constructing NumberRange object with numbers &quot; &lt;&lt; rangebegin &lt;&lt; &quot; to &quot; &lt;&lt; rangeend &lt;&lt; endl; for (int i=rangebegin; i&lt;=rangeend; i++) numbers_.push_back(i); } NumberIter&lt;void&gt; begin() { return NumberIter&lt;void&gt;(numbers_, 0); } NumberIter&lt;void&gt; end() { return NumberIter&lt;void&gt;(numbers_, numbers_.size()); } typedef NumberIter&lt;Traits_Normal&gt; iterator; typedef NumberIter&lt;Traits_Cumulative&gt; cumulative_iterator; private: vector&lt;int&gt; numbers_; }; </code></pre> <p>It's a very simple implementation. It stores the numbers in a vector. The functions begin() and end() provide iterators of NumberIter. NumberIter is a templated class with traits. Possible traits that we are going to define are: void, Traits_Normal, Traits_Cumulative. I prefer to use void where the specific Trait is not yet known. I could have also have made a Traits_Null.</p> <p>NumberRange only works with NumberIter&lt;void&gt; because begin() does not know what specifc NumberIter instance to return (Traits_Normal or Traits_Cumulative). In the assignment "NumberRange::iterator iter = range.begin()" the NumberIter&lt;void&gt; is converted into a NumberIter&lt;Traits_Normal&gt;. (NumberRange::iterator is a typedef for NumberIter&lt;Traits_Normal&gt;.)</p> <h2 id="implementationofnumberiter">Implementation of NumberIter</h2> <p>This class is templated to provide multiple kinds of iterators, by using NumberIterTraits. These traits provide the implementation of the specific iterators. So this class only provides the API.</p> <pre class="prettyprint lang-cpp"><code data-language="c">template &lt;typename T, typename Traits = NumberIterTraits&lt;T&gt; &gt; class NumberIter: public std::iterator&lt; std::forward_iterator_tag, string &gt; { public: // Constructors NumberIter(const vector&lt;int&gt; &amp;numbers, size_t seq) : sequence_(seq), numbers_(numbers) {} // Copy constructor NumberIter(const NumberIter&lt;void&gt; &amp;other) { numbers_ = other.numbers_; sequence_ = other.sequence_; } // Operators const int operator*() const { return Traits::next(numbers_, sequence_); } NumberIter &amp; operator++(int) { sequence_++; return *this; } template &lt;typename N&gt; bool operator==(const NumberIter&lt;N&gt;&amp; other) { return sequence_ == other.sequence_; } template &lt;typename X&gt; bool operator!=(const NumberIter&lt;X&gt;&amp; other) { return !((*this) == other); } private: vector&lt;int&gt; numbers_; size_t sequence_; friend class NumberIter&lt;Traits_Normal&gt;; friend class NumberIter&lt;Traits_Cumulative&gt;; }; </code></pre> <ul> <li>Constructor takes a copy of the numbers vector, which is really inefficient. But I wanted to keep the example simple. Sequence parameter is the current position of the iterator.</li> <li>There is a copy constructor (used in the assignment "NumberRange::iterator iter = range.begin();")</li> <li>operator* returns the current value of the operator. Note that the traits implement different processing and return.</li> <li>operator++ increments the sequence.</li> <li>operator== and operator!= are required for "i != range.end()".</li> </ul> <h2 id="traitsclasses">Traits classes</h2> <pre class="prettyprint lang-cpp"><code data-language="c">template&lt;typename T&gt; class NumberIterTraits; template&lt;&gt; class NumberIterTraits&lt;void&gt; { public: static int next(const vector&lt;int&gt; &amp;numbers, size_t sequence) { throw logic_error(&quot;NumberIterTraits&lt;void&gt;::next should not be used.&quot;); } }; class Traits_Normal; template&lt;&gt; class NumberIterTraits&lt;Traits_Normal&gt; { public: static int next(const vector&lt;int&gt; &amp;numbers, size_t sequence) { return numbers[sequence]; } }; class Traits_Cumulative; template&lt;&gt; class NumberIterTraits&lt;Traits_Cumulative&gt; { public: static int next(const vector&lt;int&gt; &amp;numbers, size_t sequence) { if (sequence &lt; 0) return 0; int value = 0; for (int i=0; i &lt;= sequence; i++) value += numbers[i]; return value; } }; </code></pre> <p>The Traits_Normal version simply returns the number at the index. The Traits_Cumulative sums all numbers from first to current index.</p> <p>Note that to add another iterator you <em>only</em> need to add another Traits class. (Well in my case another typedef in NumberRange for consistency as well. But you could do without and omit them like "for (NumberIter&lt;Traits_Something&gt; i = range.begin(); ...)".)</p> <p>[Edit: also a friend class declaration in NumberIter. That's so that the "generated" NumberIter classes can reference internals. Personal preference over adding more class functions.]</p> <p>[Edit2: You could add a Traits_Reverse with "return numbers[numbers.size() - ++sequence];"]</p> <h2 id="finalnotes">Final notes</h2> <p>IIRC there are some compilers that require an implementation of "operator=" for the conversion in "NumberRange::iterator = range.begin()". They refuse to use the copy constructor for this statement. In that case use this on the class.</p> <pre class="prettyprint lang-cpp"><code data-language="c">NumberIter operator=(NumberIter&lt;void&gt; val) { numbers_ = val.numbers_; sequence_ = val.sequence_; return *this; } </code></pre> <p>Complete source code can be downloaded <a href="//cdn.cppse.nl/iterators.cpp.txt">here</a>. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>There are probably more alternatives for this, i.e. you could probably do without templates.</p> <p>The iterators in this example are not fully std compliant. I.e. you cannot use them in functions from #include &lt;algorithm&gt;.</p> <p>Code tested on gcc version 4.3.4 [gcc-4_3-branch revision 152973] (SUSE Linux).</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 19 Oct 2012 00:00:00 +0000 X, Y to Latitude + Longitude functions for (google) maps //blog.cppse.nl/x-y-to-lat-lon-for-google-maps<div class="datestamp">September 23 2012</div> <div class="h1"><a href="//blog.cppse.nl/x-y-to-lat-lon-for-google-maps"> <h1 id="xytolatitudelongitudefunctionsforgooglemaps">X, Y to Latitude + Longitude functions for (google) maps</h1> </a></div> <p>I have an unfinished project with--in general--some really horrible sourcecode, but with some cool functions and solutions I came up with as well. One thing I needed for example was to calculate latitude and longitude coordinates from X and Y positions on a google maps canvas, taking zoom level into account. I could not find these conversion functions (around august 2011 anyway).</p> <p>[Edit, now since May 21, 2015 Google Maps Api V3 was released, which makes it possible with the Google API. I also found an example <a href="https://gist.github.com/iamjpg/6301054">gist here</a>]</p> <h2 id="aligningmarkerstoagrid">Aligning markers to a grid</h2> <p>The reason I needed these convertion functions in the first place was for creating an align feature for (custom) markers on a google maps canvas. This is how it works before/after aligning:</p> <p><center> <a href="//cdn.cppse.nl/51-UNALIGNED.jpg"><img border="0" src="//cdn.cppse.nl/51-large-thumb-UNALIGNED.jpg" /></a></p> <p><a href="//cdn.cppse.nl/51-ALIGNED.jpg"><img border="0" src="//cdn.cppse.nl/51-large-thumb-ALIGNED.jpg" /></a> </center></p> <p>The (very simple) algorithm I came up with divides the map in slots. A grid with a width of 100 for example, only positions markers on 100, 200, 300, 400 pixels. In this example, the 'nearest' slot's width of a marker at position 220,50 pixels wouuld be 200.</p> <p>The algorithm in pseudocode:</p> <pre><code>INITIALIZE GRID HEIGHT AND WIDTH ACCORDING TO MAP'S ZOOMLEVEL /* gridwidth = 100 &lt;&lt; (21 - map.getZoom()) */ /* gridheight = 30 &lt;&lt; (21 - map.getZoom()) */ FOREACH MARKER CONVERT MARKER LAT,LON TO X,Y COORDINATES /* y = latToY(marker.getPosition().lat()); */ /* x = lonToX(marker.getPosition().lng()); */ CONVERT X,Y TO NEAREST SLOT X,Y /* slot_y = Math.round(y - (y % gridheight)) */ /* slot_x = Math.round(x - (x % gridwidth)) */ SET MARKER X,Y TO SLOT X,Y WHILE SLOT POSITION IS OCCUPIED BY ANOTHER MARKER MOVE TO NEXT SLOT POSITION /* Next slot position is according to a simple spiral movement [1] */ SET MARKER X,Y TO SLOT X,Y ENDWHILE CONVERT MARKER X,Y TO LAT,LON /* marker.setPosition(new google.maps.LatLng(lat, lon)); */ /* marker.setPosition(new google.maps.LatLng(lat, log)); */ STORE SOMEWHERE THAT MARKER IS IN THIS SLOT POSITION ENDFOREACH</code></pre> <p>[1]: The search for next slot position is according to this pattern:</p> <p>up, right, down, down, left, left, up, up, up, right, right, right, etc.</p> <ul> <li>The change of direction is continious (a spiral): {up, right, down, left, ..}</li> <li>The number of 'steps' in each direction is {1, 1, 2, 2, .... n, n}</li> </ul> <h2 id="lattoxandlontoy">latToX() and lonToY()</h2> <p>I'm not an expert in math but I was able to find some expressions online that resolved lat+lon for x+y (the other way around). I simply replaced all the constants with their values and put them in a solver to solve them for the variables I was interested in (e.g. <a href="http://www.algebrahelp.com/calculators/equation/calc.do?equation=x+%3D+268435456+%2B+%28%2885445659.4471++*+y%29+*+0.017453292519943%29&amp;solvf=y">longitude for XtoLon</a>). I probably have the sites bookmarked somewhere but I can't find them.</p> <pre class="prettyprint lang-js"><code data-language="javascript">var glOffset = 268435456; var glRadius = 85445659.4471;// offset / pi function lonToX(lon) { var p = Math.PI / 180; var b = glRadius * lon; var c = b * p; return Math.round(glOffset + c); } function XtoLon(x) { return -180 + 0.0000006705522537 * x; } function latToY(lat) { return Math.round(glOffset - glRadius * Math.log((1 + Math.sin(lat * Math.PI / 180)) / (1 - Math.sin(lat * Math.PI / 180))) / 2); } function YtoLat(y) { var e = 2.7182818284590452353602875; var a = 268435456; var b = 85445659.4471; var c = 0.017453292519943; return Math.asin(Math.pow(e,(2*a/b-2*y/b))/(Math.pow(e,(2*a/b-2*y/b))+1)-1/(Math.pow(e,(2*a/b-2*y/b))+1))/c; } </code></pre> <p>They are not pretty but I like them because they work really well <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <h2 id="edit10-aug-2015:deltalonperdeltaxdeltalatperdeltay">Edit 10-AUG-2015: deltaLonPerDeltaX(), deltaLatPerDeltaY()</h2> <p>I found out somebody <a href="http://stackoverflow.com/questions/2651099/convert-long-lat-to-pixel-x-y-on-a-given-picture/2651276#2651276">on Stackoverflow elaborated my functions</a> with a deltaLonPerDeltaX() and deltaLatPerDeltaY(). The original poster's image is no longer available, so I'm not sure if I understand the question correctly, and therefore these additional functions. But there is a nice extra info cited from Google, which I will copy here:</p> <pre><code>At zoom level 1, the map consists of 4 256x256 pixels tiles, resulting in a pixel space from 512x512. At zoom level 19, each x and y pixel on the map can be referenced using a value between 0 and 256 * 2^19</code></pre> <p>(See [https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates][https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates])</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 23 Sep 2012 00:00:00 +0000 PhpStorm and Ideavim {Escape,C-c,C-[} responsiveness //blog.cppse.nl/phpstorm-ideavim-escape-key-responsiveness<div class="datestamp">July 30 2012</div> <div class="h1"><a href="//blog.cppse.nl/phpstorm-ideavim-escape-key-responsiveness"> <h1 id="phpstormandideavimescapec-cc-responsiveness">PhpStorm and Ideavim {Escape,C-c,C-[} responsiveness</h1> </a></div> <p>Just one thing that was annoying me for a long while, and how I fixed it. I tend to switch back and forth between insert and command mode in vim. And somehow PhpStorm with IdeaVim plugin enabled felt non-responsive. I press escape, start hitting :wq, and I have :wq in my code.</p> <p>I got accustomed hitting Escape twice, and later even three times, by default so that I was more certain I was out of insert mode. I also tried Control+C, and Control+[, but they have the same problem.</p> <p>I know the 'problem' always occured when i.e. PhpStorm started rendering an Intellisense popup: press '.' somewhere, in a large file it may take a few moments before that popup appears (maybe due to parsing etc.), so you don't see it. Assuming you are now in command mode, the escape press was actually consumed by the popup. Then of course you <em>do</em> escape to command, and try to undo, but it undo's a lot more than the chars you now accidentally sprayed in the code (also not exactly the same behaviour as Vim, but alas :D)</p> <h2 id="fix">Fix</h2> <p>Right mouse click -> Remove Escape:</p> <p><center> <a href="//cdn.cppse.nl/50-50_1.jpg"><img border="0" src="//cdn.cppse.nl/50-large-thumb-50_1.jpg" /></a> </center></p> <p>Go to Plug-ins -> IdeaVIM -> </p> <p><center> <a href="//cdn.cppse.nl/50-50_2.jpg"><img border="0" src="//cdn.cppse.nl/50-large-thumb-50_2.jpg" /></a> </center></p> <p>Find the row with all the keybindings on it.. right click on it -> Add Keyboard Shortcut</p> <p>Hit escape, save that. -> Apply -> Ok.</p> <p>Annnnnd you're done!</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 30 Jul 2012 00:00:00 +0000 Tweakers logo test //blog.cppse.nl/tweakers-logo<div class="datestamp">July 12 2012</div> <div class="h1"><a href="//blog.cppse.nl/tweakers-logo"> <h1 id="tweakerslogotest">Tweakers logo test</h1> </a></div> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/49_tweakers.jpg); width: 750px; height:445px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="445"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//blog.cppse.nl/videos/tweakers.mp4&image=//cdn.cppse.nl/tweakers.jpg&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="445" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//blog.cppse.nl/videos/tweakers.mp4&image=//cdn.cppse.nl/tweakers.jpg&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 12 Jul 2012 00:00:00 +0000 Firebird IBPP use in non-unicode project //blog.cppse.nl/firebird-ibpp-non-unicode-project<div class="datestamp">May 12 2012</div> <div class="h1"><a href="//blog.cppse.nl/firebird-ibpp-non-unicode-project"> <h1 id="firebirdibppuseinnon-unicodeproject">Firebird IBPP use in non-unicode project</h1> </a></div> <p>I posted this in a comment <a href="http://tnt64.blogspot.com/2007/04/ibppagain.html">here</a>, a long while ago. I forgot about it but yesterday someone posted in the same topic, and therefore I received an e-mail.</p> <p>As my comment somehow isn't visible on the blog (maybe it was never moderated?) i'll post it here. I was using it in a non-unicode project myself, so I encountered the same problem. According to the mail I recvd this is what I wrote:</p> <pre><code>Nice fixes. How I get the sourcecode to work in my unicode program however, without modifying the source is as follows. Simply don't compile the file all_in_one.cpp (or all ibpp/core/*.cpp files individually) with the defines/"preprocessor definitions" _UNICODE and UNICODE enabled. I didn't look at the Flamerobin source, but my guess is that they do the same. Posted by rayburgemeestre to Untouched at 6:05 PM </code></pre> <h2 id="ibppinaqtcreatorproject">IBPP in a Qt Creator project</h2> <p>Add the following in your .pro file:</p> <p>DEFINES += IBPP_WINDOWS=value</p> <p>LIBS += Advapi32.lib</p> <p>the lib is for fixing</p> <pre><code>all_in_one.obj : error LNK2019: unresolved external symbol __imp__RegCloseKey@4 referenced in function "public: struct ibpp_internals::GDS * __thiscall ibpp_internals::GDS::Call(void)" (?Call@GDS@ibpp_internals@@QAEPAU12@XZ) all_in_one.obj : error LNK2019: unresolved external symbol __imp__RegOpenKeyExA@20 referenced in function "public: struct ibpp_internals::GDS * __thiscall ibpp_internals::GDS::Call(void)" (?Call@GDS@ibpp_internals@@QAEPAU12@XZ) all_in_one.obj : error LNK2019: unresolved external symbol __imp__RegQueryValueExA@24 referenced in function "public: struct ibpp_internals::GDS * __thiscall ibpp_internals::GDS::Call(void)" (?Call@GDS@ibpp_internals@@QAEPAU12@XZ)</code></pre> <p>Also added the following two #undefs to all_in_one.cpp.</p> <pre><code>#undef _UNICODE #undef UNICODE</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 12 May 2012 00:00:00 +0000 Automatic nickname in channel and nicklist colouring in mIRC //blog.cppse.nl/nickname-colouring-mirc-scripting<div class="datestamp">April 18 2012</div> <div class="h1"><a href="//blog.cppse.nl/nickname-colouring-mirc-scripting"> <h1 id="automaticnicknameinchannelandnicklistcolouringinmirc">Automatic nickname in channel and nicklist colouring in mIRC</h1> </a></div> <p>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.</p> <p><center> <a href="//cdn.cppse.nl/46-blog1.jpg"><img border="0" src="//cdn.cppse.nl/46-thumb-blog1.jpg" /></a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="//cdn.cppse.nl/46-blog2.jpg"><img border="0" src="//cdn.cppse.nl/46-thumb-blog2.jpg" /></a> </center></p> <p>(more-begin)</p> <p>The script</p> <pre><code>;;; ;;; 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 &lt;channel&gt; response(s) raw 353:*: { var %names = $4- var %i = 1 var %n = $gettok(%names,0,32) while (%i &lt;= %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 &lt;nickname&gt; 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 &lt;nickname&gt; alias uncolor_nick { .cnick -r $1 }</code></pre> <p>Copy &amp; paste it in your remote (open with alt + r). </p> <p>You may need to enable nicklist colouring in general. Use alt + b, Nick colors, choose "Enable".</p> <h2 id="update">Update!!!</h2> <p>Note that I have a new version of this available, <a href="/away-nickname-colors-mirc#wherewasi......mircandaways.ini.">see this blogpost</a>.. It also provides a script that makes nicks marked as away light-grey!</p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 18 Apr 2012 00:00:00 +0000 Allegro 5 separate process for rendering //blog.cppse.nl/shared-memory-boost-interprocess<div class="datestamp">April 8 2012</div> <div class="h1"><a href="//blog.cppse.nl/shared-memory-boost-interprocess"> <h1 id="allegro5separateprocessforrendering">Allegro 5 separate process for rendering</h1> </a></div> <h2 id="thefollowingimagewasrenderedbyanotherprocess">The following image was rendered by another process</h2> <p>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.</p> <p><center> <a href="//cdn.cppse.nl/45-sharedmem.jpg"><img border="0" src="//cdn.cppse.nl/45-thumb-sharedmem.jpg" /></a> </center></p> <p>(more-begin)</p> <p>Probably a noobish moment, but I never realized the "stack" was this limited. I tried declaring something like</p> <pre><code>struct structw800h600 { ... Pixels pixels[800 * 600]. }; message_queue mq (create_only, "pixels", 1, //max message number sizeof(structw800h600)); //max message size structw800h600 img; memset(&amp;img, 0x00, sizeof(structw800h600));</code></pre> <p>This code caused an exception while constructing the object that declared an instance of the struct on the stack:</p> <pre><code>Unhandled exception at 0x003E5017 in Starcry.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00702000).</code></pre> <p>Shows break here in chkstk.asm (because I am in debug mode):</p> <pre><code>[...] ; Find next lower page and probe cs20: sub eax, _PAGESIZE_ ; decrease by PAGESIZE test dword ptr [eax],eax ; probe page. &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; here jmp short cs10 _chkstk endp end</code></pre> <p>I did not find out the <em>exact</em> 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.</p> <p>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: <code>Pixel *pixels = new Pixel[800 * 600]</code>;</p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 08 Apr 2012 00:00:00 +0000 Allegro 5 and wxWidgets example //blog.cppse.nl/allegro5-and-wxwidgets-example<div class="datestamp">April 8 2012</div> <div class="h1"><a href="//blog.cppse.nl/allegro5-and-wxwidgets-example"> <h1 id="allegro5andwxwidgetsexample">Allegro 5 and wxWidgets example</h1> </a></div> <p>I have posted on using allegro 4 with wxWidgets before. Allegro 5 is more easy.</p> <p>Just the stuff I encountered and how to fix</p> <p>(more-begin)</p> <h2 id="fixmainconflict">Fix main conflict</h2> <pre class="prettyprint lang-cpp"><code data-language="c">#define ALLEGRO_USE_CONSOLE 1 Avoids the following error. 1&gt;MSVCRTD.lib(crtexe.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup #define ALLEGRO_USE_CONSOLE 1 #include &lt;allegro5/allegro.h&gt; #include &lt;allegro5/allegro_image.h&gt; #include &lt;allegro5/allegro_primitives.h&gt; #include &lt;allegro5/allegro_font.h&gt; #include &lt;allegro5/allegro_ttf.h&gt; </code></pre> <h2 id="drawallegro_bitmaponstaticcanvas">Draw ALLEGRO_BITMAP on static canvas</h2> <p>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</p> <p>Just borrow local_draw_to_hdc from there and use it in the paint event.</p> <pre class="prettyprint lang-cpp"><code data-language="c">staticbitmap-&gt;Connect(wxID_STATIC, wxEVT_PAINT, wxPaintEventHandler(SharedMemoryTest::OnPaint), NULL, this); void SharedMemoryTest::OnPaint( wxPaintEvent&amp; 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); } </code></pre> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 08 Apr 2012 00:00:00 +0000 Proxytunnel HOWTO //blog.cppse.nl/apache-proxytunnel-ssh-tunnel<div class="datestamp">January 26 2012</div> <div class="h1"><a href="//blog.cppse.nl/apache-proxytunnel-ssh-tunnel"> <h1 id="proxytunnelhowto">Proxytunnel HOWTO</h1> </a></div> <p>If you are behind a firewall, chances are you can tunnel through it with <a href="http://proxytunnel.sourceforge.net/">Proxytunnel</a>. This post does not describe anything new, but I think is still useful because it includes configuration of apache and ssh client examples.</p> <p>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.</p> <p>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.</p> <p>(more-begin)</p> <p>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.</p> <p>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 <em>can</em> 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.</p> <h2 id="inshorthowitworks:">In short how it works:</h2> <p>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.</p> <p>Once connected to your SSH server you can simply use the Port forwarding stuff that the SSH protocol supports. </p> <h2 id="exampleconfig">Example config</h2> <p>I will be using this hosts throughout the post, you will have to replace these.</p> <p><style type="text/css"> table th { text-align: left; } table td { padding-right: 10px; } </style></p> <table> <col /> <col /> <col /> <thead> <tr> <th>Ip</th> <th>Host</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>46.51.179.218</td> <td>ext.cppse.nl</td> <td>My server machine, runs the apache @ port 80 and destination ssh @ 42</td> </tr> <tr> <td>NA</td> <td>whatismyipaddress.com</td> <td>Some website @ port 80 that displays remote host (optional for testing)</td> </tr> <tr> <td>172.18.12.11</td> <td>NA</td> <td>The firewall @ port 8080, accepts only connections to ports 80,443.</td> </tr> </tbody> </table> <!-- [Prototype table] --> <h2 id="configureproxyonsomeapacheserver">Configure proxy on some Apache server</h2> <p>You need mod_proxy, mod_proxy_http, mod_proxy_connect modules enabled in Apache. (Not 100% sure about mod_proxy_http.)</p> <p>Create a VirtualHost like this:</p> <pre><code>&lt;VirtualHost *:80&gt; 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... &lt;Proxy *&gt; Order deny,allow Deny from all &lt;/Proxy&gt; # This directive defines which servers can be connected to. # Access is controlled here via standard Apache user authentication. &lt;ProxyMatch (46\.51\.179\.218|ext.cppse.nl|whatismyipaddress.com|www.whatismyipaddress.com)&gt; Order deny,allow Allow from all #You should replace the above two rules with something like this: # Deny from all # Allow from &lt;some_host&gt; # Allow from &lt;some_host&gt; &lt;/ProxyMatch&gt; &lt;/VirtualHost&gt;</code></pre> <p>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. </p> <ul> <li>Add this VirtualHost as the <em>first</em> virtual host. Loading it /after/ other vhosts made the proxy deny all CONNECT's on my machine.</li> <li>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.</li> <li>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.</li> <li>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.</li> </ul> <h2 id="testifthisproxyworks">Test if this proxy works</h2> <p>You might want to test this from some location where you are not behind the firewall. Configure it as a proxy in your browser:</p> <p><center> <img border="0" src="//cdn.cppse.nl/43-1 vhost config test.jpg" /> </center></p> <p>This is why I added [www.whatismyipaddress.com][] and port 80 in the Virtual Host, open it:</p> <p><center> <img border="0" src="//cdn.cppse.nl/43-2 in browser.jpg" /> </center></p> <ul> <li>You can also test the SSH connection if your client supports usage of an HTTP proxy.</li> <li>You also might want to replace the default allow by the default deny config in the vhost.</li> <li>You might want to remove port 80 from the AllowConnect parameter in the vhost, and the whatismyipaddress domain(s).</li> </ul> <h2 id="configureproxytunnelforputty">Configure proxytunnel for PuTTY</h2> <p>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 <em>-P "username:password"</em> parameter on proxytunnel. Also see the help for more available options.)</p> <p><center> <a href="//cdn.cppse.nl/43-4 huidige proxy.jpg"><img border="0" src="//cdn.cppse.nl/43-thumb-4 huidige proxy.jpg" /></a> </center></p> <h2 id="installproxytunnelonwindows">Install proxytunnel on windows</h2> <p><strike>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.</strike> You can download <a href="https://puttytray.goeswhere.com/">PuTTY Tray</a> a version of PuTTY that supports local proxy and some more very nice additional features!!</p> <p>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).</p> <p>This is a command you can use for testing at commandline:</p> <pre><code>C:\proxytunnel&gt;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: -&gt; CONNECT 46.51.179.218:80 HTTP/1.0 -&gt; Proxy-Connection: Keep-Alive -&gt; User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n &lt;- HTTP/1.1 200 Connection established Tunneling to 46.51.179.218:42 (destination) Communication with remote proxy: -&gt; CONNECT 46.51.179.218:42 HTTP/1.0 -&gt; Proxy-Connection: Keep-Alive -&gt; User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n &lt;- HTTP/1.0 200 Connection Established &lt;- Proxy-agent: Apache/2.2.12 (Linux/SUSE) Tunnel established. SSH-2.0-OpenSSH_5.1</code></pre> <p>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.</p> <h2 id="thisishowyouconfigureputty">This is how you configure PuTTY</h2> <p><center> <img border="0" src="//cdn.cppse.nl/43-5 putty 1.jpg" /> </center></p> <p><center> <img border="0" src="//cdn.cppse.nl/43-6 putty 2.jpg" /> </center></p> <p>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.</p> <p><center> <img border="0" src="//cdn.cppse.nl/43-7 putty 3.jpg" /> </center></p> <p><center> <img border="0" src="//cdn.cppse.nl/43-8 putty tunnel.jpg" /> </center></p> <p>You can configure all kinds of portforwarding.</p> <h2 id="installproxytunnelonlinux">Install proxytunnel on linux</h2> <p>Download <a href="http://proxytunnel.sourceforge.net/">proxytunnel</a> 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.</p> <pre><code>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</code></pre> <p>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.</p> <pre><code>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</code></pre> <p>Connecting with openssh should yield something like:</p> <pre><code>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: -&gt; CONNECT 46.51.179.218:80 HTTP/1.0 -&gt; Proxy-Connection: Keep-Alive -&gt; User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n &lt;- HTTP/1.1 200 Connection established Tunneling to 46.51.179.218:42 (destination) Communication with remote proxy: -&gt; CONNECT 46.51.179.218:42 HTTP/1.0 -&gt; Proxy-Connection: Keep-Alive -&gt; User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Win32)\n &lt;- HTTP/1.0 200 Connection Established &lt;- 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&gt; </code></pre> <p>After the "Tunnel established" you continue as with any other SSH connection.</p> <h2 id="usingsshportforwarding">Using SSH port forwarding</h2> <p>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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. 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 <em>at all</em>.. the SSL communication would be encrypted twice!</p> <p>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.</p> <p>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 &amp; port. </p> <p>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: <a href="http://ray.blog.burgemeestre.net/proxy-securely-through-any-proxy-firewall">curlprox</a>[cURLproxy].</p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 26 Jan 2012 00:00:00 +0000 Proxy securely through ANY corporate proxy/firewall //blog.cppse.nl/proxy-securely-through-any-proxy-firewall<div class="datestamp">January 14 2012</div> <div class="h1"><a href="//blog.cppse.nl/proxy-securely-through-any-proxy-firewall"> <h1 id="proxysecurelythroughanycorporateproxyfirewall">Proxy securely through ANY corporate proxy/firewall</h1> </a></div> <p>DISCLAIMER: Okay, probably still <em>almost</em> 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" <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</p> <h2 id="whenisthisuseful">When is this useful?</h2> <p><a href="http://proxytunnel.sourceforge.net/">ProxyTunnel</a> 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.</p> <p>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. </p> <p>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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /> but then this proxy solution won't work). Do not expect it to be very performant with broadband type of stuff by the way.</p> <p>(more-begin)</p> <h2 id="howitworksinshort">How it works in short</h2> <p>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).</p> <p>The curl proxy (as I call it, because I use the cURL extension in PHP) retrieves HTML pages like this:</p> <pre><code>Example of packet with data "PONG :leguin.freenode.net", is sent as the following HTML: &lt;PACKET&gt;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=".</code></pre> <p>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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />). All proxy.php does is write and read files from a directory, nothing more.</p> <p>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. </p> <h2 id="graphicalexplanation">Graphical explanation</h2> <p>You should follow the arrows in the same order as presented in the Legend. Click to enlarge the image.</p> <p><center> <a href="//cdn.cppse.nl/42-illustV2.jpg"><img border="0" src="//cdn.cppse.nl/42-large-thumb-illustV2.jpg" /></a> </center></p> <h2 id="designdecisions">Design decisions</h2> <p>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. </p> <ul> <li>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.</li> <li>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.</li> <li>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.</li> </ul> <h2 id="encryptionused">Encryption used</h2> <pre class="prettyprint"><code data-language="php">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); } </code></pre> <p>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).</p> <p>I found my "ASCII key &rarr; md5 &rarr; 16 hexadecimal display chars &rarr; actual binary" a pretty cool find by the way. Did you notice it? <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <!-- ## Configure the server * Get PHP5 with extensions enabled: cURL, sockets and mcrypt. Mcrypt is probably enabled by default. * Make sure you edit the crypt_key in all three PHP scripts. Important! * Configure proxy.php, server.php and loop.sh (or loop.bat on windows) to use the same intermediate directory. It's where they communicate with files. * Verify that opening proxy.php in the browser should show: "Server preconditions succeeded". Linux specific: * Make sure php is callable from commandline (php-cli). * Make sure the loop.sh script runs in the background. Windows specific: * Put everything in for example C:\proxy, I've included php.exe and all required dll's, and even a php.ini. Everything can be run from that directory. But if you prefer to download your own PHP, and version of the tools I included (used by loop.bat), like find2 (find from UnxUtils), pskill (from sysinternals), etc., you can download them yourself if you don't trust my binaries. * Run start_server.bat (and to stop, stop_server.bat) ## Configure the client * Put everything in for example C:\proxy, etc. Then edit start_service.bat, it contains the configuration. * Start all listeners with running start_service.bat * Stop all of them with running stop_service.bat --> <h2 id="demonstration">Demonstration</h2> <p>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.</p> <p><strong>Remote desktop</strong> 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.</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/42_play_video.jpg); width: 750px; height:621px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="621"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//blog.cppse.nl/proxy_demo.mp4&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="621" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//blog.cppse.nl/proxy_demo.mp4&repeat=never&shuffle=true&autostart=true&streamer=lighttpd&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <h2 id="sourcecodedownloads">Sourcecode &amp; downloads</h2> <p>Put it here on bitbucket: <a href="https://bitbucket.org/rayburgemeestre/curlproxy">https://bitbucket.org/rayburgemeestre/curlproxy</a> 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.</p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 14 Jan 2012 00:00:00 +0000 GNU Screen Navigator V2 //blog.cppse.nl/gnu-screen-navigator-v2<div class="datestamp">November 25 2011</div> <div class="h1"><a href="//blog.cppse.nl/gnu-screen-navigator-v2"> <h1 id="gnuscreennavigatorv2">GNU Screen Navigator V2</h1> </a></div> <h2 id="workinprogress...">Work in progress...</h2> <p>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).</p> <p>No makefile, just a g++ goto.cpp -o goto -lncurses</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/41_play_video.jpg); width:750px; height:370px; display:block;"> <!-- <object id="player2" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="370"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/41_output.flv&image=//cdn.cppse.nl/frame_0_00185.jpg&repeat=always&shuffle=false&autostart=true" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="370" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/41_output.flv&image=//cdn.cppse.nl/frame_0_00185.jpg&repeat=always&shuffle=false&autostart=true" /> </object> --> </a> </center></p> <p>Get the source code <a href="https://bitbucket.org/rayburgemeestre/goto">here</a></p> <p>(more-begin)</p> <p>P.S. I added colours:</p> <p><center> <a href="//cdn.cppse.nl/41-screenshot.jpg"><img border="0" src="//cdn.cppse.nl/41-thumb-screenshot.jpg" /></a> </center></p> <h3 id="updates">Updates</h3> <p>24-feb-2013: Now listens for ncurses KEY_RESIZE event so changing window size will redraw.</p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 25 Nov 2011 00:00:00 +0000 Some CGI "tips" //blog.cppse.nl/some-cgi-tips<div class="datestamp">November 20 2011</div> <div class="h1"><a href="//blog.cppse.nl/some-cgi-tips"> <h1 id="somecgitips">Some CGI "tips"</h1> </a></div> <h2 id="bashwrapperscript">Bash wrapper script</h2> <p>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:</p> <pre><code>#!/bin/bash printf "Content-type: text/html\n\n" /path/to/actual_appl</code></pre> <p>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.</p> <h2 id="runningapplicationinchroot">Running application in chroot</h2> <p>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:</p> <pre><code>#!/bin/bash sudo -E /usr/bin/chroot /usr/local/src/some_jail /usr/bin/some_appl 2&gt;&amp;1</code></pre> <p>The -E flag means "preserve environment". To allow this you have to configure sudoers properly (visudo). Something like this:</p> <pre><code>wwwrun ALL=(ALL) SETENV: ALL, NOPASSWD : /usr/bin/chroot</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 20 Nov 2011 00:00:00 +0000 Inline printf compatible with char * //blog.cppse.nl/inline-printf-compatible-with-char-array<div class="datestamp">November 11 2011</div> <div class="h1"><a href="//blog.cppse.nl/inline-printf-compatible-with-char-array"> <h1 id="inlineprintfcompatiblewithchar">Inline printf compatible with char *</h1> </a></div> <p>This is no rocket science but I thought this was a really cool solution to the problem. <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <p>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.</p> <pre class="prettyprint lang-cpp"><code data-language="c">char *Xprintf(const char *format, ...); // This function works in the following situations foo1(Xprintf(&quot;Hello world: %d&quot;, 1001)); // void foo1(char *); foo2(Xprintf(&quot;Hello world: %d&quot;, 1001)); // void foo2(const char *); foo3(Xprintf(&quot;Hello world: %d&quot;, 1001)); // void foo3(const string); foo4(Xprintf(&quot;Hello world: %d&quot;, 1001)); // void foo4(const string &amp;); foo5(Xprintf(&quot;Hello world: %d&quot;, 1001), Xprintf(&quot;...&quot;, ...)); // void foo5(char *, char *); </code></pre> <p>Xprintf cannot use just one buffer because the case of 'foo5' would fail (it would get the same pointer twice).</p> <p>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 <em>const</em> char *, only explicit casting through .c_str(). The call to foo1 would become: foo1(const_cast<char *>(Xprintf("").c_str())), which is kind of ugly!</p> <p>The following fixes it, creating a tmp_str class that extends std::string and simply provides the implicit cast:</p> <pre class="prettyprint lang-cpp"><code data-language="c">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&lt;char *&gt;(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); } </code></pre> <p>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 > &amp;" cannot be initialized with an rvalue of type "tmp_str".). )</p> <p>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.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 11 Nov 2011 00:00:00 +0000 Render on top of mplayer using custom window //blog.cppse.nl/render-on-top-of-mplayer-using-custom-window<div class="datestamp">October 27 2011</div> <div class="h1"><a href="//blog.cppse.nl/render-on-top-of-mplayer-using-custom-window"> <h1 id="renderontopofmplayerusingcustomwindow">Render on top of mplayer using custom window</h1> </a></div> <p>mplayer can easily be instructed to render on a custom window with the <code>-wid (window handle)</code> parameter. </p> <pre class="prettyprint lang-cpp"><code data-language="c">// On windows long targetWindowId = reinterpret_cast&lt;long&gt;(canvas-&gt;GetHWND()); // On Linux long targetWindowId = GDK_WINDOW_XWINDOW(canvas-&gt;GetHandle()-&gt;window); </code></pre> <p>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 <em>when</em> 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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. This meant that I could no longer use my preferred video renderer on windows <code>-vo direct3d</code> because somehow that setting doesn't actually draw <em>on</em> 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 <code>-vo directx:noaccel</code> in order to properly read it.</p> <h2 id="fixoverlapproblem">Fix overlap problem</h2> <p>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: </p> <p><center> <a href="//cdn.cppse.nl/37-screenshot1.jpg"><img border="0" src="//cdn.cppse.nl/37-large-thumb-screenshot1.jpg" /></a> </center></p> <p>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.</p> <h2 id="fixlinuxsupport">Fix linux support</h2> <p>On Linux I use the <code>-vo X11</code> 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.</p> <p><center> <a href="//cdn.cppse.nl/37-screenshot2.jpg"><img border="0" src="//cdn.cppse.nl/37-large-thumb-screenshot2.jpg" /></a> </center></p> <p>(more-begin)</p> <h2 id="result">Result</h2> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/37-play_video.jpg); width:660px; height:324px; display:block;"> <!-- <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="660" height="324" id="mplayer" align="middle"> <param name="allowScriptAccess" value="sameDomain" /> <param name="movie" value="//cdn.cppse.nl/mplayer.swf" /><param name="quality" value="high" /><param name="bgcolor" value="#ffffff" /><embed src="//cdn.cppse.nl/mplayer.swf" quality="high" bgcolor="#ffffff" width="660" height="324" name="mplayer" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> --> </a> </center></p> <p>The code is available on <a href="https://bitbucket.org/rayburgemeestre/poc_mplayer">bitbucket</a> and works on Windows (tested Windows vista with aero theme) and Linux (openSUSE 11.4). Makefile and Visual studio project files included.</p> <p><a name="studiovideo"></a></p> <h2 id="whereiusedthisfor..">Where I used this for..</h2> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/37-play_video2.jpg); width: 750px; height:448px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="448"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/37-video.flv&image=//cdn.cppse.nl/37-video-thumb.jpg&repeat=never&shuffle=true&autostart=true" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="448" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/37-video.flv&image=//cdn.cppse.nl/37-video-thumb.jpg&repeat=never&shuffle=true&autostart=true" /> </object> --> </a> </center></p> <p>All texts and images are rendered on top of the background video.</p> <p><center> <img border="0" src="//cdn.cppse.nl/37-sprite.jpg" /> </center></p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 27 Oct 2011 00:00:00 +0000 Starcry rendered videos //blog.cppse.nl/starcry-rendered-videos-pt2<div class="datestamp">September 18 2011</div> <div class="h1"><a href="//blog.cppse.nl/starcry-rendered-videos-pt2"> <h1 id="starcryrenderedvideos">Starcry rendered videos</h1> </a></div> <p><center></p> <!-- PROOF OF CONCEPT WORKED, BUT I'M DISABLING IT BECAUSE ITS 5 MB :') orig = width="700" height="191" --> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_demo.jpg); width: 750px; height:205px; display:block;"> <!-- <center> <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0" width="750" height="205" id="Untitled-1" align="middle"> <param name="allowScriptAccess" value="sameDomain" /> <param name="movie" value="//cdn.cppse.nl/Untitled-1.swf" /><param name="quality" value="high" /><param name="bgcolor" value="#ffffff" /><embed src="//cdn.cppse.nl/Untitled-1.swf" quality="high" bgcolor="#ffffff" width="750" height="205" name="Untitled-1" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> </center> --> </a> <!-- --> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_video0.jpg); width: 750px; height:191px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="191"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/@site_Test_001.scp.flv&image=//cdn.cppse.nl/@site_Test_001.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="191" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/@site_Test_001.scp.flv&image=//cdn.cppse.nl/@site_Test_001.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_video1.jpg); width: 750px; height:191px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="191"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/@site_Flower.scp.flv&image=//cdn.cppse.nl/@site_flower.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="191" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/@site_Flower.scp.flv&image=//cdn.cppse.nl/@site_flower.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_video2.jpg); width: 750px; height:191px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="191"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/@site_demo2.scp.flv&image=//cdn.cppse.nl/@site_demo2.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="191" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/@site_demo2.scp.flv&image=//cdn.cppse.nl/@site_demo2.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_video3.jpg); width: 750px; height:191px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="191"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/@site_HEADER.scp.flv&image=//cdn.cppse.nl/@site_HEADER.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="191" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/@site_HEADER.scp.flv&image=//cdn.cppse.nl/@site_HEADER.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/36-play_video4.jpg); width: 750px; height:191px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="191"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/@site_Project1.scp.flv&image=//cdn.cppse.nl/@site_Project1.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="191" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/@site_Project1.scp.flv&image=//cdn.cppse.nl/@site_Project1.scp.jpg&repeat=always&shuffle=false&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> </center> --> </a></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 18 Sep 2011 00:00:00 +0000 Behaviours engine //blog.cppse.nl/starcry-behaviours-engine<div class="datestamp">May 22 2011</div> <div class="h1"><a href="//blog.cppse.nl/starcry-behaviours-engine"> <h1 id="behavioursengine">Behaviours engine</h1> </a></div> <p><center></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/34-play-video.jpg); width: 620px; height:480px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="620" height="480"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/milestone3.flv&image=//cdn.cppse.nl/milestone3.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="620" height="480" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/milestone3.flv&image=//cdn.cppse.nl/milestone3.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <p>(more-begin)</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/34-play-yt.jpg); width: 398px; height:318px; display:block;"> <!-- <object width="398" height="318"><param name="movie" value="http://www.youtube-nocookie.com/v/6dJA53ofFgg?fs=1&hl=en_GB&hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube-nocookie.com/v/6dJA53ofFgg?fs=1&hl=en_GB&hd=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="398" height="318"></embed></object> --> </a> </center></p> <h2 id="milestone1">Milestone 1</h2> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/34-play-video-2.jpg); width: 620px; height:480px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="620" height="480"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/milestone1.flv&image=//cdn.cppse.nl/milestone1.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="620" height="480" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/milestone1.flv&image=//cdn.cppse.nl/milestone1.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <h2 id="milestone2">Milestone 2</h2> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/34-play-video-3.jpg); width: 620px; height:480px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="620" height="480"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/milestone2.flv&image=//cdn.cppse.nl/milestone2.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="620" height="480" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/milestone2.flv&image=//cdn.cppse.nl/milestone2.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> --> </a> </object></p> <p><a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/34-play-video-4.jpg); width: 620px; height:480px; display:block;"> <!-- <object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="620" height="480"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/milestone2.2.flv&image=//cdn.cppse.nl/milestone2.2.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="620" height="480" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/milestone2.2.flv&image=//cdn.cppse.nl/milestone2.2.jpg&repeat=never&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 22 May 2011 00:00:00 +0000 DialogBlocks Howto //blog.cppse.nl/dialogblocks-howto<div class="datestamp">April 30 2011</div> <div class="h1"><a href="//blog.cppse.nl/dialogblocks-howto"> <h1 id="dialogblockshowto">DialogBlocks Howto</h1> </a></div> <h3 id="puttingsourcefilesinseparatedirectory">Putting source files in separate directory</h3> <p><center> <div id="video1"> <a href="javascript:void(0);" onclick="this.parentNode.innerHTML = getvideo1();"> <img border="0" src="//cdn.cppse.nl/youtube_icon_jpg.png"/> </a> <br/> View youtube video<br/> (don't forget to enable 720p and fullscreen)</p> <script type="text/javascript"> function getvideo1() { return '<iframe width="640" height="510" src="http://www.youtube.com/embed/KTPAIXfiMxY?hd=1" frameborder="0" allowfullscreen></iframe>'; } </script> <p></center></p></p> <ul> <li>How to put source and image files in subdirectories in DialogBlocks..</li> <li>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.</li> </ul> <!-- <center> <iframe width="640" height="510" src="http://www.youtube.com/embed/KTPAIXfiMxY?hd=1" frameborder="0" allowfullscreen></iframe> </center> --> <h3 id="seperateimplementationfilesonafewpanels">Seperate implementation files on a few panels</h3> <p><center> <div id="video1"> <a href="javascript:void(0);" onclick="this.parentNode.innerHTML = getvideo2();"> <img border="0" src="//cdn.cppse.nl/youtube_icon_jpg.png"/> </a> <br/> View youtube video<br/> (don't forget to enable 720p and fullscreen)</p> <script type="text/javascript"> function getvideo2() { return '<iframe width="640" height="510" src="http://www.youtube.com/embed/6lIPhgIxqYA?hd=1" frameborder="0" allowfullscreen></iframe>'; } </script> <p></center></p></p> <ul> <li>We put two panels (with their contents) into separate implementation files.</li> <li>Show some examples you can do with it (or more easily than before).</li> <li>Events and other functions can be nicely grouped into that implementation.</li> </ul> <!-- <center> <iframe width="640" height="510" src="http://www.youtube.com/embed/6lIPhgIxqYA?hd=1" frameborder="0" allowfullscreen></iframe> </center> --> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 30 Apr 2011 00:00:00 +0000 Compiling with DialogBlocks //blog.cppse.nl/dialogblocks-compiling<div class="datestamp">April 30 2011</div> <div class="h1"><a href="//blog.cppse.nl/dialogblocks-compiling"> <h1 id="compilingwithdialogblocks">Compiling with DialogBlocks</h1> </a></div> <h3 id="compilingwithmingw">Compiling with MinGW</h3> <p><center> <div id="video1"> <a href="javascript:void(0);" onclick="this.parentNode.innerHTML = getvideo3();"> <img border="0" src="//cdn.cppse.nl/youtube_icon_jpg.png"/> </a> <br/> View youtube video<br/> (don't forget to enable 720p and fullscreen)</p> <script type="text/javascript"> function getvideo3() { return '<iframe width="640" height="510" src="http://www.youtube.com/embed/VeZ8PDeuEfc?hd=1" frameborder="0" allowfullscreen></iframe>'; } </script> <p></center></p></p> <ul> <li>How to add configuration for MinGW</li> </ul> <h3 id="compilingwithvisualccompilerwithprojectfiles">Compiling with Visual C++ compiler with project files</h3> <p><center> <div id="video1"> <a href="javascript:void(0);" onclick="this.parentNode.innerHTML = getvideo4();"> <img border="0" src="//cdn.cppse.nl/youtube_icon_jpg.png"/> </a> <br/> View youtube video<br/> (don't forget to enable 720p and fullscreen)</p> <script type="text/javascript"> function getvideo4() { return '<iframe width="640" height="510" src="http://www.youtube.com/embed/HZXvyRKVvgY?hd=1" frameborder="0" allowfullscreen></iframe>'; } </script> <p></center></p></p> <ul> <li>Shows how to set the platform SDK.</li> <li>Some specific errors you can get w/ DialogBlocks.</li> <li>How to use generated project file(s) for visual studio. <ul><li>How they handle file changes</li> <li>Why to use: autocomplete, debugger, <em>etc.</em></li></ul></li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 30 Apr 2011 00:00:00 +0000 ![nl][] Urenregistratie //blog.cppse.nl/urenregistratie<div class="datestamp">April 17 2011</div> <div class="h1"><a href="//blog.cppse.nl/urenregistratie"> <h1 id="nlurenregistratie"><img id="nl" src="//cdn.cppse.nl/nl.png" alt="nl" title="nl" /> Urenregistratie</h1> </a></div> <h2 id="werkzaamhedentijdenreverseengineering">Werkzaamheden tijden reverse engineering</h2> <p>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, <em>enz.</em></p> <p><center> <a href="//cdn.cppse.nl/30-urenregistratie2.jpg"><img border="0" src="//cdn.cppse.nl/30-thumb-urenregistratie2.jpg" /></a> </center></p> <p>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.</p> <p>Indien tevreden, kun je het hieruit overkopieeren. Mij heeft het al meerdere malen geholpen <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" /></p> <h2 id="download">Download</h2> <p>Executable <a href="https://github.com/rayburgemeestre/Binaries/tree/master/Urenregistratie">hier</a> te downloaden.</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 17 Apr 2011 00:00:00 +0000 Git, github, Mercurial, bitbucket //blog.cppse.nl/git-and-hg<div class="datestamp">April 16 2011</div> <div class="h1"><a href="//blog.cppse.nl/git-and-hg"> <h1 id="gitgithubmercurialbitbucket">Git, github, Mercurial, bitbucket</h1> </a></div> <p>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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. 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).</p> <p><center> <a href="//cdn.cppse.nl/29-git_vs_hg.jpg"><img border="0" src="//cdn.cppse.nl/29-large-thumb-git_vs_hg.jpg" /></a> </center></p> <p>Some notes, small annoyances I encountered on my system and how I fixed them.</p> <ul> <li>Git usage on Windows Vista notes</li> <li>Git removing contents permanently from repository</li> <li>Converting from Git to Mercurial</li> <li>Mercurial through proxy</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 16 Apr 2011 00:00:00 +0000 SuperMouser - mouseless navigation //blog.cppse.nl/super-mouser<div class="datestamp">April 8 2011</div> <div class="h1"><a href="//blog.cppse.nl/super-mouser"> <h1 id="supermouser-mouselessnavigation">SuperMouser - mouseless navigation</h1> </a></div> <p>I'm actually using <a href="http://lifehacker.com/#!217420/hack-attack-more-on-mouseless-navigation">Adam Pash' mouser</a> 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.</p> <p><center> <a href="//cdn.cppse.nl/27-screenshot.jpg"><img border="0" src="//cdn.cppse.nl/27-large-thumb-screenshot.jpg" /></a> </center></p> <p>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).</p> <p><strike>The only thing missing is multiple-monitor support at this moment, but give us a few days. Having only one monitor isn't helping <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />.</strike></p> <p>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). </p> <p>Marijn Koesen is collaborating and also adding OSX support. The GTK2 version is coming soon. <strike>And multiple-monitor support currently has highest priority.</strike></p> <p>It's all on <a href="https://github.com/rayburgemeestre/SuperMouser">github</a> for download. The <a href="https://github.com/rayburgemeestre/Binaries/tree/master/SuperMouser">binary</a> for windows is on github as well.</p> <p>Roadmap:</p> <ul> <li>80% Multiple monitor support (working on it)</li> <li>100% <strike>Undo history key (for correcting mistakes)</strike></li> <li>0% Remember settings in config.</li> <li>80% OSX compatibility</li> <li>60% GTK2 compatibility</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 08 Apr 2011 00:00:00 +0000 Collection of wallpapers //blog.cppse.nl/collection-of-wallpapers<div class="datestamp">March 27 2011</div> <div class="h1"><a href="//blog.cppse.nl/collection-of-wallpapers"> <h1 id="collectionofwallpapers">Collection of wallpapers</h1> </a></div> <p><center> <a href="//cdn.cppse.nl/26-visual studio 2010.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-visual studio 2010.jpg" /></a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="//cdn.cppse.nl/26-2760540654_68db0ac054_b.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-2760540654_68db0ac054_b.jpg" /></a></p> <!-- <a href="//cdn.cppse.nl/26-bootsplash-1280x1024.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-bootsplash-1280x1024.jpg" /></a> --> <!-- <a href="//cdn.cppse.nl/26-bootsplash-1920x1200.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-bootsplash-1920x1200.jpg" /></a> --> <p><a href="//cdn.cppse.nl/26-silent-1280x1024.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-silent-1280x1024.jpg" /></a>&nbsp;&nbsp;&nbsp;&nbsp;<a href="//cdn.cppse.nl/26-silent-1920x1200.jpg"><img border="0" src="//cdn.cppse.nl/26-thumb-silent-1920x1200.jpg" /></a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 27 Mar 2011 00:00:00 +0000 BASE64 COMMANDLINE //blog.cppse.nl/base64-commandline-converter<div class="datestamp">March 2 2011</div> <div class="h1"><a href="//blog.cppse.nl/base64-commandline-converter"> <h1 id="base64commandline">BASE64 COMMANDLINE</h1> </a></div> <p>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(). </p> <pre><code>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)</code></pre> <p>(more-begin)</p> <p>Here you have it:</p> <pre class="prettyprint lang-cpp"><code data-language="c">/* 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 &lt;iostream&gt; static const char base64_chars[] = &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot; &quot;abcdefghijklmnopqrstuvwxyz&quot; &quot;0123456789+/&quot;; 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] &amp; 0xfc) &gt;&gt; 2; char_array_4[1] = ((char_array_3[0] &amp; 0x03) &lt;&lt; 4) + ((char_array_3[1] &amp; 0xf0) &gt;&gt; 4); char_array_4[2] = ((char_array_3[1] &amp; 0x0f) &lt;&lt; 2) + ((char_array_3[2] &amp; 0xc0) &gt;&gt; 6); char_array_4[3] = char_array_3[2] &amp; 0x3f; for (i = 0; i &lt; 4; i++) b64str[s++] = base64_chars[char_array_4[i]]; i = 0; } } if (i) { for (j = i; j &lt; 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] &amp; 0xfc) &gt;&gt; 2; char_array_4[1] = ((char_array_3[0] &amp; 0x03) &lt;&lt; 4) + ((char_array_3[1] &amp; 0xf0) &gt;&gt; 4); char_array_4[2] = ((char_array_3[1] &amp; 0x0f) &lt;&lt; 2) + ((char_array_3[2] &amp; 0xc0) &gt;&gt; 6); char_array_4[3] = char_array_3[2] &amp; 0x3f; for (j = 0; j &lt; i + 1; j++) b64str[s++] = base64_chars[char_array_4[j]]; while (i++ &lt; 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-- &amp;&amp; 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 &lt; 4; i++) char_array_4[i] = strchr(base64_chars, char_array_4[i]) - base64_chars; char_array_3[0] = (char_array_4[0] &lt;&lt; 2) + ((char_array_4[1] &amp; 0x30) &gt;&gt; 4); char_array_3[1] = ((char_array_4[1] &amp; 0xf) &lt;&lt; 4) + ((char_array_4[2] &amp; 0x3c) &gt;&gt; 2); char_array_3[2] = ((char_array_4[2] &amp; 0x3) &lt;&lt; 6) + char_array_4[3]; for (i = 0; (i &lt; 3); i++) output[r++] = char_array_3[i]; i = 0; } } if (i) { for (j = i; j &lt;4; j++) char_array_4[j] = 0; for (j = 0; j &lt;4; j++) char_array_4[j] = strchr(base64_chars, char_array_4[j]) - base64_chars; char_array_3[0] = (char_array_4[0] &lt;&lt; 2) + ((char_array_4[1] &amp; 0x30) &gt;&gt; 4); char_array_3[1] = ((char_array_4[1] &amp; 0xf) &lt;&lt; 4) + ((char_array_4[2] &amp; 0x3c) &gt;&gt; 2); char_array_3[2] = ((char_array_4[2] &amp; 0x3) &lt;&lt; 6) + char_array_4[3]; for (j = 0; (j &lt; 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 &lt;string&gt; #include &lt;fstream&gt; using namespace std; void display_usage(const char *programname) { cerr &lt;&lt; &quot;Usage: &quot; &lt;&lt; programname &lt;&lt; &quot; [OPTION] [SOURCE] [DEST]&quot; &lt;&lt; endl &lt;&lt; &quot; where [OPTION] is either --to-base64 or --from-base64,&quot; &lt;&lt; endl &lt;&lt; &quot; [SOURCE] is the file to convert&quot; &lt;&lt; endl &lt;&lt; &quot; [DEST] is the target/output file (will be overwritten)&quot; &lt;&lt; endl &lt;&lt; &quot;&quot; &lt;&lt; endl &lt;&lt; &quot;Examples: base64conv.exe --to-base64 somefile.exe somefile.txt&quot; &lt;&lt; endl &lt;&lt; &quot; base64conv.exe --from-base64 somefile.txt somefile.exe&quot; &lt;&lt; endl &lt;&lt; &quot;(something like ./base64conv on linux)&quot; &lt;&lt; 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&lt;int&gt;(sourcedataLen)]; sourcefile.seekg(0, ios::beg); sourcefile.read(sourcedata, sourcedataLen); sourcefile.close(); cout &lt;&lt; &quot;source file &quot; &lt;&lt; argv[SOURCE] &lt;&lt; &quot; loaded&quot; &lt;&lt; endl; } else { cerr &lt;&lt; &quot;unable to open source file, &quot; &lt;&lt; argv[SOURCE] &lt;&lt; endl; return -1; } if (strcmp(argv[OPTION], &quot;--to-base64&quot;) == 0) { cout &lt;&lt; &quot;converting to base64&quot; &lt;&lt; endl; char *base64string = base64_encode(reinterpret_cast&lt;unsigned char *&gt;(sourcedata), static_cast&lt;int&gt;(sourcedataLen)); ofstream outputfile(argv[DEST], ios::out|ios::binary|ios::ate); if (outputfile.is_open()) { outputfile.write (base64string, strlen(base64string)); outputfile.close(); cout &lt;&lt; &quot;base64 string written to dest file, &quot; &lt;&lt; argv[DEST] &lt;&lt; endl; } else { cerr &lt;&lt; &quot;unable to open dest file, &quot;, argv[DEST]; return -1; } } else if (strcmp(argv[OPTION], &quot;--from-base64&quot;) == 0) { cout &lt;&lt; &quot;converting from base64&quot; &lt;&lt; endl; int outlen = 0; unsigned char *decodedbase64string= base64_decode(sourcedata, static_cast&lt;int&gt;(sourcedataLen), &amp;outlen); ofstream outputfile(argv[DEST], ios::out|ios::binary|ios::ate); if (outputfile.is_open()) { outputfile.write(reinterpret_cast&lt;const char *&gt;(decodedbase64string), outlen); outputfile.close(); cout &lt;&lt; &quot;decoded base64 string written to dest file, &quot; &lt;&lt; argv[DEST] &lt;&lt; endl; } else { cerr &lt;&lt; &quot;unable to open dest file, &quot;, argv[DEST]; return -1; } } else { display_usage(argv[PROGRAM]); cerr &lt;&lt; &quot;@@ invalid [OPTION] specified.&quot;; return -1; } delete[] sourcedata; } } </code></pre> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 02 Mar 2011 00:00:00 +0000 Singleton notepad.exe //blog.cppse.nl/singleton-notepad-exe-replacement<div class="datestamp">February 27 2011</div> <div class="h1"><a href="//blog.cppse.nl/singleton-notepad-exe-replacement"> <h1 id="singletonnotepad.exe">Singleton notepad.exe</h1> </a></div> <p>If you need to protect yourself from yourself, like I needed to do. Then this simple notepad replacement executable may be for you <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. <a href="//blog.cppse.nl/files/notepad-replacement-v1.1.zip">Click here to download.</a> What it does is: </p> <ul> <li>Always instantiates exactly <em>one</em> blank/new/untitled notepad instance at a time.</li> <li>The untitled stuff is kept in a user configurable file. So you are not constantly bothered with a Save as dialog..</li> <li>Easily 'dispatch' selections within your notepad to separate files.</li> <li>When called to open a file, does open a new editor for that file. That editor is configurable.</li> <li>Supports better CTRL+Z, CTRL+Y than default notepad.</li> <li>Supports CTRL+F and search next with F3. </li> <li>(For goto-line with CTRL+G you'll have to dispatch the text to another editor!)</li> </ul> <p>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).</p> <p><center> <a href="//cdn.cppse.nl/22-screenshot.jpg"><img border="0" src="//cdn.cppse.nl/22-large-thumb-screenshot.jpg" /></a> </center></p> <p>(more-begin)</p> <p>To install it you need to do the following:</p> <ul> <li>Disable UAC (on Vista for example)</li> <li>Reboot the system</li> <li>Execute a batch file like the following. Got this copy from <a href="http://mattberther.com/2006/11/19/replacing-notepad-in-windows-vista">here</a>.</li> </ul> <p>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:</p> <pre><code>@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</code></pre> <p>You can read the INSTALL file in the .zip bundle for details.</p> <p>Example configuration</p> <pre><code>; configuration for notepad.exe replacement editor=C:\Program Files\Vim\vim72\gvim.exe file=C:\temp.txt title=Untitled maximized=0</code></pre> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 27 Feb 2011 00:00:00 +0000 Starcry rendered frame images //blog.cppse.nl/starcry-rendered-frame-images<div class="datestamp">February 24 2011</div> <div class="h1"><a href="//blog.cppse.nl/starcry-rendered-frame-images"> <h1 id="starcryrenderedframeimages">Starcry rendered frame images</h1> </a></div> <p>These are some examples of rendered frames by the starcry rendering engine. </p> <p><center> <a href="//cdn.cppse.nl/20-screenshot.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-screenshot.jpg" /></a></p> <p><a href="//cdn.cppse.nl/20-screenshot2.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-screenshot2.jpg" /></a></p> <p><a href="//cdn.cppse.nl/20-frame00098.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-frame00098.jpg" /></a> </center></p> <p>(more-begin)</p> <p><center> <a href="//cdn.cppse.nl/20-frame00525.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-frame00525.jpg" /></a></p> <p><a href="//cdn.cppse.nl/20-upload.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-upload.jpg" /></a></p> <p><a href="//cdn.cppse.nl/20-origineel.jpg"><img border="0" src="//cdn.cppse.nl/20-thumb-origineel.jpg" /></a> </center></p> <p>(more-end)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 24 Feb 2011 00:00:00 +0000 GNU Screen Navigator //blog.cppse.nl/gnu-screen-navigator<div class="datestamp">January 15 2011</div> <div class="h1"><a href="//blog.cppse.nl/gnu-screen-navigator"> <h1 id="gnuscreennavigator">GNU Screen Navigator</h1> </a></div> <p>When navigating directories, every now and then <code>pushd .</code> and <code>popd</code> just aren't sufficient. When directory structures tend to become quite deep, I get really annoyed with typing <code>change-directory</code>&rsquo;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 <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />!</p> <h2 id="createbookmarksfile">Create bookmarks file</h2> <pre> // 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"} ]} ] </pre> <h2 id="useinscreenwithc-x">Use in screen with C-x</h2> <p><center> <small> <a href="//cdn.cppse.nl/15-screen1.jpg"><img border="0" src="//cdn.cppse.nl/15-large-thumb-screen1.jpg" /></a> </small> </center></p> <p>Choosing a <em>directory</em> in the menu opens it in a new screen tab. Choosing a <em>file</em> opens a new menu which allows you to dispatch it to specific editors or e-mail it (options currently all hardcoded in the source).</p> <h2 id="installation">Installation</h2> <p>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 <a href="https://github.com/rayburgemeestre/GNUScreenNavigator">github</a> for download.</p> <p>It installs <code>/usr/local/bin/launcher</code> and two helper bash scripts (<code>/usr/local/bin/launch_scr.sh</code> and <code>/usr/local/bin/launch_scr2.sh</code>). You bind it to screen in your <code>.screenrc</code> with this line: <code>bindkey ^x exec /usr/local/bin/launch_scr.sh</code>.</p> <p>(Also for your <code>.screenrc</code> I recommend using a caption if you don't already with: <code>caption always "%{Yb} %D %Y-%02m-%02d %0c %{k}|%{G} %l %{k}|%{W} %-w%{+b}%n %t%{-b}%+w"</code>)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sat, 15 Jan 2011 00:00:00 +0000 PHP debugging in practice! //blog.cppse.nl/php-debugging-in-practice<div class="datestamp">January 14 2011</div> <div class="h1"><a href="//blog.cppse.nl/php-debugging-in-practice"> <h1 id="phpdebugginginpractice">PHP debugging in practice!</h1> </a></div> <p>Just wanted to use the 'in practice' part because it sounds so cool <img id="img1" src="//cdn.cppse.nl/Super_Mini_KAO_ANI_by_3dera.gif" alt="img1" title=":)" />. 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.</p> <h2 id="recordandre-createrequests..">Record and re-create requests..</h2> <pre class="prettyprint"><code data-language="php">/** * 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'], &quot;\n&quot;))); fclose($fd); } else { if (!isset($_GET['set_state'])) { // Show index/listing of states $indexFileLines = array_reverse(explode(&quot;\n&quot;, file_get_contents($indexFile))); foreach ($indexFileLines as $line) { if (empty($line)) continue; list($filename, $requestUri) = explode(&quot; &quot;, $line); printf(&quot;&lt;a href=\&quot;%s%s__STATE_DEBUGGER__&amp;set_state=%s\&quot;&gt;%s&lt;/a&gt;&lt;br/&gt;\n&quot;, $requestUri, (strpos($requestUri, &quot;?&quot;) === FALSE ? &quot;?&quot; : &quot;&amp;&quot;), $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/'); </code></pre> <p>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.</p> <p>(Add <code>__STATE_DEBUGGER__</code> 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 <code>$_GET, $_POST, $_SESSION</code>, ...)</p> <p>(By the way I didn't bother to make this very secure (see <code>file_get_contents($_GET['set_state'])</code> 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...)</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 14 Jan 2011 00:00:00 +0000 Photoshop blenders for allegro //blog.cppse.nl/photoshop-blending-types-for-allegro<div class="datestamp">November 26 2010</div> <div class="h1"><a href="//blog.cppse.nl/photoshop-blending-types-for-allegro"> <h1 id="photoshopblendersforallegro">Photoshop blenders for allegro</h1> </a></div> <p>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.</p> <p>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.</p> <p>That's why I did a quick search and found someone who already collected macro's for all photoshop blending types. <a href="http://www.nathanm.com/photoshop-blending-math/">By Nathan Moinvaziri</a>. 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.</p> <p><center> <small> <a href="//cdn.cppse.nl/13-Screenshot.jpg"><img border="0" src="//cdn.cppse.nl/13-thumb-Screenshot.jpg" /></a> </small> </center></p> <p>I put it on <a href="https://github.com/rayburgemeestre/AllegroBlenders">github</a> with a demo program. From the README:</p> <pre><code>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 &amp;basecolor, int &amp;blendcolor)); Example: put_blended_pixel(screen, x, y, somecolor, blender_lighten);</code></pre> <p>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,</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 26 Nov 2010 00:00:00 +0000 wxWidgets code snippets //blog.cppse.nl/wxwidgets-code-snippets<div class="datestamp">November 26 2010</div> <div class="h1"><a href="//blog.cppse.nl/wxwidgets-code-snippets"> <h1 id="wxwidgetscodesnippets">wxWidgets code snippets</h1> </a></div> <p>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.</p> <h3 id="clearingasizer">Clearing a sizer</h3> <p>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:</p> <pre><code>while (theSizer-&gt;GetChildren().GetCount() &gt; 0) { theSizer-&gt;GetItem(static_cast&lt;size_t&gt;(0))-&gt;DeleteWindows(); theSizer-&gt;Remove(0); } // populate the sizer again maybe sizerChangelogItems-&gt;Layout();</code></pre> <p>You might want to surround the code with Freeze() and Thaw() for performance.</p> <h3 id="hidingandshowingapanelonanauiframe">Hiding and showing a panel on an AUI frame</h3> <p>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:</p> <pre><code>someWindow-&gt;GetAuiManager().GetPane(someWindow-&gt;splitterwindow1).Show(false); someWindow-&gt;GetAuiManager().GetPane(someWindow-&gt;panel1).Show(true); someWindow-&gt;GetAuiManager().Update();</code></pre> <!-- ### Splitting / unsplitting a splitterwindow // lost the snippet somehow, i tend to use the AUI framework more nowadays. // todo: search --> <h3 id="usingdropdownswithobjectdata.">Using dropdowns with object data.</h3> <p>I use wxChoice a lot, I especially like that the items themselves can refer to anything. Typically how I initialize a choicebox:</p> <pre><code>wxChoice *choice = ...; Object *someObject = ...; for (...) { choice-&gt;Append("object 1", (void *)someObject); } choice-&gt;SetStringSelection("object 1");</code></pre> <p>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:</p> <pre><code>void SomeWindow::OnChoiceSelected( wxCommandEvent&amp; event ) { wxChoice *choice = static_cast&lt;wxChoice *&gt;(event.GetEventObject()); Object *someObject = static_cast&lt;Object *&gt;(choice-&gt;GetClientData(choice-&gt;GetSelection())); if (someObject != NULL) { //... } }</code></pre> <h3 id="directoryfunctions">Directory functions</h3> <pre class="prettyprint lang-cpp"><code data-language="c">wxString resdir(RESOURCE_DIR); if (!::wxDirExists(resdir)) { throw std::runtime_error(&quot;resources dir not found&quot;); } wxDir dir(resdir); if ( !dir.IsOpened() ) { throw std::runtime_error(&quot;resources directory could not be opened&quot;); } wxString filename; bool cont = dir.GetFirst(&amp;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(&quot;cpp&quot;))) { ... } else { ... } } cont = dir.GetNext(&amp;filename); } </code></pre> <h3 id="writetofilefunction">Write to file function</h3> <pre><code>wxFile article(markdownfilename, wxFile::write); if (!article.IsOpened()) { throw std::runtime_error("main.md could not be opened for writing"); } article.Write(textctrlArticleBody-&gt;GetValue()); article.Close();</code></pre> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 26 Nov 2010 00:00:00 +0000 Launch of yet another blog ii //blog.cppse.nl/launch-of-a-new-blog-ii<div class="datestamp">November 19 2010</div> <div class="h1"><a href="//blog.cppse.nl/launch-of-a-new-blog-ii"> <h1 id="launchofyetanotherblogii">Launch of yet another blog ii</h1> </a></div> <p>Today I moved from my "127 machine" (localhost) to an Amazon EC2 server.</p> <p>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.</p> <p>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.</p> <p>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!</p> <p><small> <center> <a href="//cdn.cppse.nl/11-screenshot1.jpg"><img border="0" src="//cdn.cppse.nl/11-large-thumb-screenshot1.jpg" /></a> </center> </small></p> <p>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 ;)</p> <p>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 <em>love</em> my weird blogging system though!</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Fri, 19 Nov 2010 00:00:00 +0000 Common Lisp wallpaper //blog.cppse.nl/common-lisp-desktop-background<div class="datestamp">November 17 2010</div> <div class="h1"><a href="//blog.cppse.nl/common-lisp-desktop-background"> <h1 id="commonlispwallpaper">Common Lisp wallpaper</h1> </a></div> <p>The <a href="http://pocket7878.deviantart.com/art/Common-Lisp-Wallpaper-158480701">original wallpaper by ~Pocket7878 on devianART</a> 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 <a href="http://lisperati.com/">Conrad Barski</a> (author of Land of lisp)</p> <p>Check it! <small> <center> <a href="//cdn.cppse.nl/9-lisp-bg.jpg"><img border="0" src="//cdn.cppse.nl/9-thumb-lisp-bg.jpg" /></a> </center> </small></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 17 Nov 2010 00:00:00 +0000 Improving the outline for the Adornment of the Middle Way //blog.cppse.nl/improving-the-outline-for-the-adornment-of-the-middle-way<div class="datestamp">November 17 2010</div> <div class="h1"><a href="//blog.cppse.nl/improving-the-outline-for-the-adornment-of-the-middle-way"> <h1 id="improvingtheoutlinefortheadornmentofthemiddleway">Improving the outline for the Adornment of the Middle Way</h1> </a></div> <h2 id="thisishowthetextisstructuredexcerpt">This is how the text is structured (excerpt)</h2> <blockquote> <p><small> The Commentary 147<br/> 1. The exposition of the root verses 147<br/> 2. The meaning of the title 147<br/> 2. The homage of the translator 150<br/> 2. The text itself 151<br/> 3. An examination and establishment of the two truths 151<br/> 4. The two truths identified 151<br/> 5. A demonstration that no entities exist on the ultimate level 151<br/> 6. The main argument of the Madhyamakalankara (stanza I) 151<br/> 7. An investigation of the subject of the probative argument 152<br/> 7. An investigation of the argument 153<br/> 8. A Prasangika or a Svatantrika argument? 153<br/> </small></p> </blockquote> <p>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.</p> <!-- Halfway through the book (few years have passed) I finally decided I needed an indented view. One that showed the structure using indenting (depth visualized with i.e.. spaces), and with a parameterizable maximum of levels for display (view only 1, 2, ..., n levels). And for the future browsable, like you would browse through your hierarchical filesystem. And even for a further future, make it linkable to pages in a pdf version?? But I'm wandering off here. --> <!-- ## Parsing the outline! For this I photographed the pages from the book, converted my crappy-scewed-low-light pictures using the first online OCR website I could find. This only worked a little, had to fix a lot of errors, and had to (re)type a lot of the stuff manually. Then wrote a script in PHP to convert the text into indented output, I did that actually a few weeks ago, but today I implemented the same thing in Lisp. (I'm already getting the hang of it a little ;) I really like the incremental way of developing functions, and how you can easily keep sections for evaluating to see what's happening.) --> <p>I wrote a little Lisp program to parse this outline lines-of-text. And output it with proper indenting. Output for <code>(print-tree 4)</code></p> <blockquote> <p><small> The Commentary 147<br/> &nbsp;&nbsp;&nbsp;1&nbsp; The exposition of the root verses 147<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; The meaning of the title 147<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; The homage of the translator 150<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; The text itself 151<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp; An examination and establishment of the two truths 151<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; The two truths identified 151<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; Answers to objections made to this distinction of the two truths 293<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; The benefits of understanding the two truths correctly 337<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp; The conclusion: a eulogy of this approach to the two truths 361<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; An outline of the tradition in which the Chittamatra and Madhyamaka (..)<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4&nbsp; In praise of this path 363<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2&nbsp; The conclusion 377<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp; The author's colophon 377<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp; The colophon of the translators 377<br/> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3&nbsp; Colophon of Mipham Rinpoche 381<br/> &nbsp;&nbsp;&nbsp;1&nbsp; The necessity for the explanation of the root verses 378<br/> </small></p> </blockquote> <p>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.</p> <p>I am considering testing either <a href="http://www.webtoolkit.eu/wt">Wt, the C++ Web toolkit</a> or <a href="http://cppcms.sourceforge.net/wikipp/en/page/main">CppCMS, a C++ Web Development Framework</a> 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!).</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 17 Nov 2010 00:00:00 +0000 Using allegro with wxWidgets //blog.cppse.nl/use-allegro-with-wxwidgets<div class="datestamp">November 10 2010</div> <div class="h1"><a href="//blog.cppse.nl/use-allegro-with-wxwidgets"> <h1 id="usingallegrowithwxwidgets">Using allegro with wxWidgets</h1> </a></div> <h2 id="mytwofavouritelibrariesever">My two favourite libraries ever</h2> <blockquote> <p>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. (...) <a href="http://www.talula.demon.co.uk/allegro/">source</a></p> </blockquote> <p>&nbsp;</p> <blockquote> <p>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+. (...) <a href="http://www.wxwidgets.org">source</a></p> </blockquote> <h2 id="therequiredcodesummarized">The required code summarized</h2> <p>I include allegro before wxWidgets, not sure if that matters, but what <em>does</em> 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):</p> <pre class="prettyprint lang-cpp"><code data-language="c">#define ALLEGRO_NO_MAGIC_MAIN #define RGB AL_RGB #include &lt;allegro.h&gt; #include &lt;winalleg.h&gt; #undef RGB </code></pre> <p>Then I initialize allegro like this in OnCreate() of your wxWidgets application:</p> <pre class="prettyprint lang-cpp"><code data-language="c">install_allegro(SYSTEM_NONE, &amp;errno, NULL); set_palette(desktop_palette); // example set_color_depth(32); // example </code></pre> <p>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.</p> <pre class="prettyprint lang-cpp"><code data-language="c">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 &quot;BITMAP *&quot; </code></pre> <p>It took me a while to figure out how I could use draw_to_hdc like this. It's not that obvious (see <a href="http://www.allegro.cc/forums/thread/598305">allegro forums</a>). Others have been struggling with drawing allego bitmaps on wxStaticBitmaps. This method will still yield you quite a high fps count.</p> <h2 id="knownissues">Known issues</h2> <p><strong>wxWidgets eventloop--or apparently random--crashes</strong> 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 <em>after</em> 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.)</p> <p>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).</p> <p><strong>Assembly optimized {get|put}pixel crashes</strong> 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.</p> <p><strong>Non-assembly optimized {get|put}pixel crashes</strong> 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. </p> <p>[Edit 22-nov-2010: Just remembered this, but didn't verify it. It may have something to do with calling <code>drawing_mode(..)</code>, and not setting a specific blending mode like <code>set_trans_blender(..)</code>. Setting one of those, or instead of drawing_mode and set_trans_blender a call to solid_mode(), would fix it.]</p> <p>[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]</p> <!-- Will post a demo-program soon. --> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Wed, 10 Nov 2010 00:00:00 +0000 Launch of yet another blog //blog.cppse.nl/launch-of-a-new-blog<div class="datestamp">November 8 2010</div> <div class="h1"><a href="//blog.cppse.nl/launch-of-a-new-blog"> <h1 id="launchofyetanotherblog">Launch of yet another blog</h1> </a></div> <h2 id="whatsnewaboutthisblog">What's new about this blog??</h2> <p>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's quite minimal.</p> <p>How does it work? I have to run <a href="http://sourceforge.net/projects/xming/">Xming</a> on my windows box, and request from the administration panel of the blog a management console. This opens a C++ program developed using <a href="http://www.dialogblocks.com">DialogBlocks</a> (my best software-buy ever!!) / <a href="http://www.wxwidgets.org/">wxWidgets</a>. 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.</p> <p>Why do I do it like this? First of all I don't like to write HTML. That's why I can define a (simple) site template with a <a href="http://haml-lang.com/">HAML</a> and <a href="http://sass-lang.com/">SASS</a>, and add some markers in it for replacement.</p> <p><small> <center> <a href="//cdn.cppse.nl/4-screenshot.jpg"><img border="0" src="//cdn.cppse.nl/4-large-thumb-screenshot.jpg" /></a> </center> </small></p> <p>Demo snippet from the screenshot:</p> <pre class="prettyprint lang-cl"><code data-language="scheme">(defun factorial (n) (if (&lt;= n 1) 1 (* n (factorial (- n 1))))) </code></pre> <p>The editor simply has a listing of articles, which are stored in <a href="http://fletcherpenney.net/multimarkdown/">multimarkdown</a> 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 other code-snippets (they can be editted in separate files). Using <a href="http://code.google.com/p/google-code-prettify/">this code prettifyer</a> 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 <a href="http://www.grinninglizard.com/tinyxml/">TinyXML</a>.</p> <p>In the editor I can request gvim or xemacs to edit the markdown (or a snippet), or use the one build-in. </p> <p>Lastly, I can (re)generate (parts of- or the entire-)website. It will convert HAML files to HTML. Markdown to HTML. Merge the snippets, merge articles with main html. And the website is updated.</p> <h2 id="update19-7-2012">Update 19-7-2012</h2> <ul> <li>Now it processes all JPG's through imageconvert to compress them better, something similar for PNG's</li> <li>Minifies the CSS and Javascript (also combines them where possible)</li> </ul> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 08 Nov 2010 00:00:00 +0000 Motion blur //blog.cppse.nl/starcry-motion-blur<div class="datestamp">November 8 2010</div> <div class="h1"><a href="//blog.cppse.nl/starcry-motion-blur"> <h1 id="motionblur">Motion blur</h1> </a></div> <!-- [UNUSED ARTICLE] --> <p>The engine supports <em>correct</em> rendering of motionblur for objects. (view more about it at <a href="http://freespace.virgin.net/hugo.elias/graphics/x_motion.htm">TGLTLSBFSSP: Motion Blur</a>) The implementation is now pretty efficient, and works well for all kinds of objects with all kinds of textures, <em>etc.</em>. It isn't fast enough for realtime rendering, you'd probably need to use hardware acceleration for that, or <em>false</em> motionblur.</p> <p><center> <a class="object_holder" href="#" onclick="videoInLink(this)" style="background:url(//cdn.cppse.nl/3-play-video.jpg); width: 750px; height:324px; display:block;"> <!--</p> <p><object id="player" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" name="player" width="750" height="324"> <param name="movie" value="//cdn.cppse.nl/player.swf" /> <param name="allowfullscreen" value="true" /> <param name="allowscriptaccess" value="always" /> <param name="flashvars" value="file=//cdn.cppse.nl/output.flv&image=//cdn.cppse.nl/output.jpg&repeat=always&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> <embed type="application/x-shockwave-flash" id="player2" name="player2" src="//cdn.cppse.nl/player.swf" width="750" height="324" allowscriptaccess="always" allowfullscreen="true" flashvars="file=//cdn.cppse.nl/output.flv&image=//cdn.cppse.nl/output.jpg&repeat=always&shuffle=true&autostart=true&backcolor=000000&frontcolor=ffffff" /> </object> --> </a> </center></p> <p>The settings used for the object in the movie:</p> <p><center> <a href="//cdn.cppse.nl/3-motionblur0.jpg"><img border="0" src="//cdn.cppse.nl/3-thumb-motionblur0.jpg" /></a> </center></p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Mon, 08 Nov 2010 00:00:00 +0000 Functional programming //blog.cppse.nl/lisp-resources-ebooks-and-links<div class="datestamp">November 7 2010</div> <div class="h1"><a href="//blog.cppse.nl/lisp-resources-ebooks-and-links"> <h1 id="functionalprogramming">Functional programming</h1> </a></div> <p>Quote from <a href="http://en.wikipedia.org/wiki/Functional_programming">Wikipedia</a>:</p> <blockquote> <p>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]</p> <p>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.</p> </blockquote> <h2 id="lispe-books">Lisp e-books</h2> <ul> <li><a href="http://www.gigamonkeys.com/book/">Practical Common Lisp</a> by Peter Seibel.</li> <li><a href="http://www.cs.cmu.edu/~dst/LispBook/">Common Lisp: A Gentle Introduction to Symbolic Computation</a> by David S. Touretzky.</li> <li><a href="http://www.psg.com/~dlamkins/sl/">Succesful Lisp</a> by David B. Lamkins..</li> </ul> <p>I've started with <a href="http://www.cs.cmu.edu/~dst/LispBook/">gentle</a> --actually a really old book--and have planned to read <a href="http://www.gigamonkeys.com/book/">pcl</a> second. [Edit 23-11-2010, now actually bought a used copy as well!]</p> <!-- [Edit 29-01-2012, almost read both now. Tried out Lispworks as I really prefer a proper GUI library. It Lisp-works (lame joke I know) really nice, but it costs a lot of money and as I can create everything in C++ maybe just as fast, maybe the GUI stuff faster. I have yet to encounter something where CL would be a better choice (for me)] --> <!-- ## My first Lisp program Really lame!! f30392c7702ec9d840c6acfae504e21a --> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Sun, 07 Nov 2010 00:00:00 +0000 Enable wake-on-lan on Linux Debian (4.0) //blog.cppse.nl/enable-wake-on-lan-on-debian4.0<div class="datestamp">December 18 2008</div> <div class="h1"><a href="//blog.cppse.nl/enable-wake-on-lan-on-debian4.0"> <h1 id="enablewake-on-lanonlinuxdebian4.0">Enable wake-on-lan on Linux Debian (4.0)</h1> </a></div> <h2 id="enablewake-on-lanonlinuxdebian">Enable wake-on-lan on Linux Debian</h2> <p>Source = <a href="http://www.oger-partners.be/?q=node/60">http://www.oger-partners.be/?q=node/60</a> I have an asus motherboard with an integrated NIC. Integrated NICs on recent motherboards have WOL capability. I hope yours has too, otherwise you may have to obtain some kind of wire to connect the NIC to the motherboard.</p> <ul> <li>Read up on WOL on wikipedia.</li> <li>Download ethtool (f.e. apt-get install ethtool)</li> <li>The command 'ethtool eth0' should yield the output 'Supports Wake-on: g'. 'g' means the NIC can read the magic packet.</li> <li>Make a shell script to enable WOL with ethtool, put this command in it: /usr/sbin/ethtool -s eth0 wol g</li> <li>Change your /etc/network/interfaces so this script is executed each time the interface is brought up.</li> <li>The tricky part was finding out why the settings set with ethtool were thrown away with each reboot. The answer lies in the /etc/init.d/halt script. Remove the '-i' from the halt command at the end of the script so the interfaces are not affected.</li> </ul> <p>Good luck. :-)</p> <h2 id="toolusedinwindows">Tool used in windows</h2> <p>Leeched from= <a href="http://www.depicus.com/download.aspx?product=gui">http://www.depicus.com/download.aspx?product=gui</a></p> <p>View attachments for word document I made with instructions for starting up ILLYRIA</p> rayburgemeestre@no-spam.tld (Ray Burgemeestre) Thu, 18 Dec 2008 00:00:00 +0000