Skip navigation.

Fat R笔记……与减肥无关

Fat awful terrible Rubbish-bin

关于On Screen Display (OSD)的实现

由于俺对GDI了解不深(基本上是一个小白),弄了半天才弄出一个,总结一下……

  *OSD实际上也是一个窗口。虽然它看起来只是显示在屏幕上的一段文字,但事实上它仍然是一个窗口,只不过这个窗口并不具备一般窗口的外形,一直处于所有窗口的最前端并且不能移动,还有就是可能具备“鼠标穿透”的功能。之前我还幻想简单地将文本TextOut到屏幕上,然而这是不行的,不但因为这么做会造成丑陋的闪烁,而且还会使得拖动它下面的窗口时带上文字的“残像”。

  *既然是一个窗口,就可以通过RegisterClassEx然后CreateWindowEx来创建,通过ShowWindowAsync之类的函数来show或hide。窗口的绘制也是在窗口的消息响应回调函数里面实现。所以实现起来也很简单,一个初始化函数加一个回调函数即可。

  *调用CreateWindowEx创建窗口时,style一般设置成WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_LAYERED。TOPMOST表示置于前端,TOOLWINDOW表示窗口不在任务栏和任务列表里出现,LAYERED表示窗口为Layered Window,TRANSPARENT表示允许鼠标穿透。本来是不想使用LAYERED的,毕竟这个9x/Me不支持。然而如果不用它,加了TRANSPARENT并不能实现鼠标穿透,而且还会导致OSD下面的窗口拖动时带上OSD的“残像”,并且文本也不能正常显示。如果两个都不用,OSD下面的窗口拖动时正常,但OSD窗口的内容保持上次刷新时OSD及其下方的内容,不会随OSD下方窗口内容的变化而重绘。顺手试了一下VirtualDimension的OSD功能,似乎也不太正常……不知道到底怎么回事了,总之现在似乎只有设置成LAYERED才能正常工作。

  *注册class时,hbrBackground似乎不起作用……本来想用SetLayeredWindowAttributes(hWnd, 0, level, LWA_ALPHA)设置成半透明的,然而这样出来的窗口就是一块黑色半透明的矩形……如果自己实现WM_PAINT就会变成白色,如果实现WM_ERASEBKGND就会变得乱七八糟 而且Alpha模式下,HOLLOW_BRUSH/NULL_BRUSH是不起作用的。所以最后用了KeyColor实现透明,即SetLayeredWindowAttributes( m_hWnd,RGB(255,255,255),0,1 )。看了一下VirtualDimension的代码,也是鱼与熊掌不可兼得啊……
   //Make sure the window is not translucent if it is displayed without background
   m_transp->SetTransparencyLevel(m_hasBackground ? m_transpLevel : (unsigned char)255);

总之现在就是要设置成LAYERED,keycolor实现透明,并且不自行处理WM_ERASEBKGND才能正常显示……

  *至于WM_PAINT,要注意的是HDC通过BeginPaint而不是GetDC来获得:
    case WM_PAINT:
      hdc = BeginPaint(aHWnd, &ps);
      OSDpaint(hdc);
      EndPaint(aHWnd, &ps);
      break;


  *调用ShowWindowAsync时似乎有时会获得焦点,show的时候用ShowWindowAsync(m_hWnd, SW_SHOWNA)似乎可以解决,Hide的时候虽然似乎不会,但是如果这时焦点已经在OSD的window上,就会让我自动隐藏的taskbar显示出来。我的解决方法:
        if (GetFocus() == m_hWnd)
            SetForegroundWindow(hWnd);
        ShowWindowAsync(m_hWnd, SW_HIDE);      

这里m_hWnd是OSD的HWND,hWnd是主程序的hWnd。

  *让OSD显示一段时间后自动消失,可以用Timer实现,并让回调函数处理WM_TIMER消息。而消息处理要做的事情就是隐藏OSD窗口。用上面那段程序即可实现。显示和隐藏OSD的语句如下:
        KillTimer(m_hWnd, 1);
        ShowWindowAsync(m_hWnd, SW_HIDE);        

        ShowWindowAsync(m_hWnd, SW_SHOWNA);        
        SetTimer(m_hWnd, 1, OSDDelay, NULL);

另外,应该也可以用线程来实现,过程是:创建窗口并Show->Sleep()延时->销毁窗口并注销类。

吖。。。又到星期一昨天开始,我的手机将长期处于漫游状态……

Comments

Anonymous 25. August 2006, 01:23

KAKA writes:

我想说的是~~~~ 我是学文科的~~ 谢谢~ 再见 当我没来过

Anonymous 9. July 2007, 07:27

pashpash writes:

OSD 不是调用操作系统的API来显示的,是硬件自带的功能

How to use Quote function:

  1. Select some text
  2. Click on the Quote link

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

If you can't read the words, press the small reload icon.


Smilies