Tim's Web Log #3 : technical
Thoughts and opinions of an opinionated person

Mon, 06 Mar 2006

Vim Tricks
Being a command-line dinosaur, I use gvim for all of my editing, both on Windows and Linux. I handle projects for a number of different clients, and each of those clients has their own standards for tabbing, indentation, line length, and so on. One of the things I have missed in vim is the ability to have project-specific settings that cover an entire directory tree.

Well, with a few minutes of searching today, I discovered a way to do this. Vim has the ability to have certain commands executed based on the file name pattern; that's how it does syntax coloring, for instance. It turns out that this pattern matching is flexible enough to handle my case. I added this to my global _vimrc:

autocmd BufRead,BufNewFile c:/dev/client1/* so /dev/client1/_vimrc
autocmd BufRead,BufNewFile c:/dev/client2/* so /dev/client2/_vimrc
autocmd BufRead,BufNewFile c:/dev/client3/* so /dev/client3/_vimrc

Works as pretty as you please.


Tue, 08 Nov 2005

Hidden Window Capture
This week, I had an opportunity to look in depth at the problem of capturing the contents of a Windows window, even when that window is partially or totally covered.

This is something that the original designers of Windows made difficult, as a byproduct of the way they did their drawing code. When a window receives a WM_PAINT message, it is supposed to go redraw all the parts of its window. The operating system will make sure to skip drawing whatever parts do not need drawing, just for speed.

To do drawing in Windows, a program has to have a "device context". That's the magic structure that tells, among other things, where drawing will be going (that is, to the screen, to a printer, to a bitmap, etc.). The drawing code doesn't care where it is drawing; the DC takes care of that.

So, if there were a way to trigger the WM_PAINT processing, but substitute a DC that pointed to a bitmap, you could fool a window into repainting itself onto a bitmap. If Microsoft had allowed the DC to be used to be specified with the WM_PAINT message, it would be easy. However, they didn't do that. Instead, there is a pair of APIs called BeginPaint and EndPaint that are used for exactly this purpose, and no other: inside a WM_PAINT message, BeginPaint returns to the app the DC that it should use for drawing. That means I cannot substitute my own DC unless I intercept the application's calls to BeginPaint and EndPaint.

Microsoft realized this "flaw", but too late. They recently added a WM_PRINT message, which is the same as WM_PAINT, except that it accepts a DC as one of the message parameters. That would be great if applications obeyed it, but they don't. The standard Windows controls all honor WM_PRINT, but that's all. Applications ignore it. Plus, you cannot send WM_PRINT to a different process; an application can only ask it of itself.

Today, I managed to get this all mostly working. It's quite a hack job. I started with some old code by Feng Yuan that shows how to support WM_PRINT in your own applications.

Here's the basics of what I tried. I have a DLL. I call an entry point in that DLL which installed a WH_CALLWNDPROC window hook. This means that my hook routine will get called every time any window receives a message. The cool thing about this is that, in order to do so, my DLL has to be running as a part of those processes. The operating system basically "injects" my DLL into all other processes.

My hook lies in wait until it sees a special message that says "I want a snapshot of your window." The hook then patches BeginPaint and EndPaint on the fly in this remote process, so that they jump into code in my DLL. I then send a WM_PRINT to the top-level window. WM_PRINT draws all of the window decorations (borders, title bars, menus), and then sends a WM_PRINTCLIENT message to all of the child windows of the application. This message eventually makes its way to my WH_CALLWNDPROC hook again. At that point, I send a WM_PAINT to the window instead. When the window calls BeginPaint, it triggers my patch, and receives MY DC. When the capture is done, I unpatch BeginPaint and EndPaint, and continue on.

This does some things quite well. I can always capture the window decorations and the standard Windows controls (and remember, this works even if the window is completely hidden by other windows). However, for many apps, the main window area always comes back blank. This turns out to be because those apps simply violate the rules.

Gvim is one example. I get everything except the text being edited. It turns out that gvim does not use the BeginPaint DC to do its drawing. It creates its own DC when it creates the text box, and uses that DC to respond to WM_PAINT. It's doing the painting, but it's not giving it to me.

Word is another example. I get everything except the text of the document. I have not traced through it yet, but I suspect it will be the exact same explanation: a CS_OWNDC.

So, I have code which does exactly what I wanted it to do, and it is almost worthless.


Wed, 26 Oct 2005

Virtual Desktops
I typically have a lot of applications open at once. A dozen is typical, two dozen is not uncommon. A few years ago, I went on a quest to find a virtual desktop application to make that a bit easier to manage. I've tried many of them, and I think I have finally found my winner.

One of the things I've learned is that you have to figure out what you want from a desktop. Many people want to be able to hover their mouse at a screen edge and automatically pan into the next desktop over. I find that annoying. Many people seem to find it natural to think about desktops directionally: go up, go left, go right. I did not find that natural. I want to organize my desktops by function (e-mail desktop, web desktop, project A development, project B development). When I want to go to my e-mail desktop, I don't care where I am right now.

I started out with jsPager, which is freeware. I have included a link, but it no longer works. As near as I can tell, the author and home page have now evaporated from the face of the earth. jsPager presents your virtual desktops as a rectangular array, from 1x1 to 3x3. It allows mouse edge panning, but fortunately it can be disabled. It allows definable hotkeys to move left, right, up, and down, which I found useless. It does put up a miniature map of the entire universe, with mini versions of all of the windows in their relative locations, and allows you to click on one to switch to that desktop. I found that very natural. However, it did not have the ability to assign a particular hotkey to a particular desktop. I had to use the mouse.

jsPager works by moving the window coordinates. Windows on non-visible desktops have negative or large positive coordinates, placing them well offscreen. This works well with most apps, although it does not work for Microsoft PowerPoint.

One of the side effect of this is that ALL of the windows on ALL of the desktops always appear on the task bar at all times. Clicking on a taskbar icon was usually the quickest way to change desktops, although the taskbar does get a bit crowded.

I did get used to jsPager, and I have been using it at work for about 3 years, despite a crash here and there. However, for some reason, it does not work for me at home. I work rather differently at home; most of my home work is monitoring newsgroups and surfing. At home, I tried the Microsoft Virtual Desktop powertoy. This uses an entirely different philosophy, using the more obscure Windows XP "desktop" feature. It has a minimalist feature set. It always creates exactly 4 desktops, no more no less. It adds tray icons to select one of the four, and creates (fixed) hotkeys to switch directly to them. It has been working well for me at home, where 4 desktops is enough.

This week, I encountered goScreen. I have fallen in love. I have uninstalled jsPager and turned off the Microsoft PowerToy. goScreen is shareware -- about $29 -- but it is worth every penny. The author, Andrew Guryanov, has done a fabulous job with the details in this app. The program is extremely configurable, so that you can make it work like almost any of the other tools. It allows up to 40 virtual desktops. It u can display its window map either as a docked (and hideable) toolbar at a screen edge, or as a rectangular map like jsPager. Or, if you prefer, it can show the universe as a simple listbox. It uses a simple hide/show paradigm for switching desktops, which seems to be faster than either of the other choices. It also supports the assignment of specific hotkeys to specific desktops. It allows you to "lock" certain applications to certain desktops. It allows you to have some windows that always appear on all desktops. It allows you to change window locations by dragging and dropping, or by using a simple right-click on the window map.

It even has some weird features that might be useful to some people. One of the problems with a full stack of apps is that it can be difficult to pick the app you want. goScreen has a feature called "packing", in which it rearranges your windows so that the title bars are all visible in a neatly spaced stack. When you click on one of the apps, the windows rearrange themselves so that the other title bars are still visible. I had not seen this in a desktop manager before.

I strongly recommend goScreen.


Wed, 21 Sep 2005

Macrovision in Windows Media Center
Over the past several months, I have been doing battle with Windows Media Center Edition, in an attempt to support the reporting of Macrovision encoding from our capture driver to the rest of the capture graph. Support for Macrovision is required for capture devices sold in the US, so this is relatively important. Microsoft supposedly documents the requirements in a document entitled "Designing Video Capture Boards for Use with the Microsoft ActiveX Video Control", but there just wasn't enough information in there to enable us to get the answer. We have now solved the problem, and I wanted to share a bit of what we learned.

There are basically three cases for Macrovision support. The easiest case is when your analog capture hardware does MPEG compression. The Macrovision support is, then, implemented by setting the copy protection bits directly in the MPEG headers. This self-contained solution is the one Microsoft strongly recommends, and they imply in some places that they ONLY support analog solutions with onboard MPEG compression, but it's a painful fact of life that only a few of the analog TV capture boards do so.

The other two cases involve software MPEG codecs. One such case is when you have your own MPEG encoder, or at least have a close relationship with a decoder vendor. In that case, one possible solution is to implement some private communication scheme between the capture driver and the MPEG encoder, possibly involving special bits in the frame headers, that pass the detection status downstream. The MPEG encoder can then use that information to embed the copy protection state in the MPEG headers, as before.

The final case is the one we found ourselves in: a capture driver using relatively off-the-shelf MPEG encoders (like Main Concept). In this case, there is no private communcation scheme, and it hardly benefits the vendors to implement something specifically for one vendor. So, it's up to the driver.

The mechanism for implementing Macrovision in this third case is to broadcast a property setting to all filters in the current graph. The real target is Microsoft's "XDS Encoder". Now, if the XDS Encoder was smart, it would poll the capture driver to find out the Macrovision status. Unfortunately, it isn't smart. It just exposes the property and waits for someone, somewhere, to send it an update. Further, the IKsPropertySet interface is not one of the ones that IFilterGraph aggregates, so the only way to send the property is to enumerate the graph.

Unfortunately, that enumeration can only be done from user mode. Most AVStream capture graphs are kernel-mode only. That represents a problem. The trick here is to use a concept called a "ksproxy plugin". We have our capture driver expose a kernel streaming property set that no one has ever heard of. We use the registry's MediaInterfaces key to essentially say "the property GUID XXX needs help from the user-mode COM object YYY". When ksproxy goes to expose our kernel capture filter to DirectShow, it finds the unknown property set, notices that there is a user-mode plugin helper, and loads the helper.

Once ksproxy loads us, we can participate in the graph all we want. We are given a pointer to ksproxy, which implements IBaseFilter. From that, we can fetch its FILTERINFO, which contains a pointer to the filter graph manager. From there, it's easy; the enumeration and broadcast code is all in the ActiveX Video Control document above.

The implementation is not rocket science, but the key is having someone point you in the right direction. Hopefully, this will save you the multiple months we wasted trying to find a way to advertise our Macrovision detection.


About Me
E-mail Tim
Work info
Personal info
My big dog!
My little dog!
RSS feed

Archives
2010-Jan
2008-Nov
2008-Feb
2007-Oct
2007-Sep
2007-Jul
2007-May
2007-Feb
2006-Oct
2006-Sep
2006-Aug
2006-Jun
2006-May
2006-Apr
2006-Mar
2006-Feb
2006-Jan
2005-Nov
2005-Oct
2005-Sep
2005-Aug
2005-Jul
2005-Jun
2005-May
2005-Apr
2005-Mar
2005-Jan
2004-Dec
2004-Nov
2004-Aug
2004-Jul
2004-Jun
2004-Apr
2004-Feb
2004-Jan
2003-Dec
2003-Nov
2003-Oct
2003-Sep
2003-Aug
2003-Jul
2003-Jun
2003-May
2003-Mar
2003-Feb
2003-Jan
2002-Dec

Categories


Web Sites
P&B company site
Python language site