Author Topic: OxyScheme (Scheme Interpreter in O2)  (Read 21800 times)

0 Members and 1 Guest are viewing this topic.

Aurel

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #60 on: January 15, 2015, 12:41:01 PM »
Mike
you have a right i forget to add brackets  ::)
now work  ;)

Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #61 on: January 15, 2015, 02:20:23 PM »
Great!

( [] --> brackets, {} --> braces, () --> parentheses )

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #62 on: January 15, 2015, 02:20:41 PM »
Using a layered window would be the easiest solution.

Something like this (the code is the same in Basic)
Code: [Select]
void RenderWindow(IN HWND hWnd, IN LONG_PTR hImg) {
    if (hImg) {
        BLENDFUNCTION bf = {0};
        RECT rw; GetWindowRect(hWnd, &rw);
        SIZEL lpSize; lpSize.cx = rw.right - rw.left; lpSize.cy = rw.bottom - rw.top;
        POINT lp; lp.x = rw.left; lp.y = rw.top;
        POINT ptSrc = {0};
        HDC hParentDC = GetDC(GetParent(hWnd));
        HDC hMemDC = CreateCompatibleDC(hParentDC);
        HBITMAP hBmp = CreateCompatibleBitmap(hParentDC, lpSize.cx, lpSize.cy);
        if (hMemDC) {
            SelectObject(hMemDC, hBmp);
            LONG_PTR graphics = 0;
            if (GdipCreateFromHDC(hMemDC, graphics) == 0) {
                GdipDrawImageRectRectI(graphics, hImg, 0, 0, lpSize.cx, lpSize.cy, 0, 0, lpSize.cx, lpSize.cy, 2, 0, NULL, NULL);
                GdipDeleteGraphics(graphics);
            }
            bf.BlendOp             = AC_SRC_OVER;
            bf.BlendFlags          = 0;
            bf.AlphaFormat         = AC_SRC_ALPHA; // Use source alpha
            bf.SourceConstantAlpha = 255;          //alpha
            UpdateLayeredWindow (hWnd, hParentDC, &lp, &lpSize, hMemDC, &ptSrc, 0, &bf, ULW_ALPHA);
            DeleteObject(hBmp);
            DeleteDC(hMemDC);
        }
        ReleaseDC(GetParent(hWnd), hParentDC);
    }
}



Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #63 on: January 15, 2015, 02:34:57 PM »
Yes Patrice,

Thanks for the tip. In fact, my proto already uses a layered window and a similar, albeit a little more elaborate, render proc with a few more render stages from various in-memory resources including Gdip images and pixel buffers where blur takes place selectively. Text lines are rendered the last.

My "mother tongue" is FBSL whose JIT compiler accepts classic ANSI C. That's why I don't publish the quick prototype's alien code here, but rather a precompiled executable only, until it's well polished and is ready to be ported to native OxygenBasic.

Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #64 on: January 15, 2015, 03:08:25 PM »
Charles,

Your ghost-dragging  solution is ingenious. Your work on this problem is intensive! :)
Thanks go to the late Steve Jobbs. :)

Quote
I'm toying with very simple whole-window transparency. The sweet spot seems to be an alpha value of around 240 (/255), so the background is slightly visible, but not intrusive.
My proto uses an alpha value of 250. Then the general outline of another layered window underlying the blurred "console" can yet be recognized while setting full opacity would still make the "console" pass for a completely non-transparent window, especially if it is heavily colored. BTW, Patrice's original SkinBox sources didn't know this alpha tweak and, when blurred, made the underlying layered window completely invisible. Probably this was the very reason why only one SkinBox process could be launched at any one time by Patrice's design. :D

Quote
    SetLayeredWindowAttributes(hwnd, 0, WindowOpacity, LWA_ALPHA)
If you aren't planning to change the attributes often, then perhaps Patrice's
UpdateLayeredWindow (hWnd, hParentDC, &lp, &lpSize, hMemDC, &ptSrc, 0, &bf, ULW_ALPHA);
with the associated BLENDFUNCTION bf structure would be sufficient to produce the desired effect. According to MS specs, SetLayeredWindowAttributes() can be cumbersome. Luckily it may (and should) be omitted unless absolutely necessary. My proto doesn't use such a call at all, only UpdateLayeredWindow ().

By the way, there's a later version of my proto with yet some more bells and whistles on the FBSL forum. It is now pinnable to the screen's upper corners and visually responsive to app activate events and double clicks to maximize and restore the window.

Quote
But Snapshot (Ctrl P) captures the client area before composite transparency is applied. ie: from the frame buffer with glReadPixels.
I can't comment on this in any way because I don't know the inner workings of Oxygen's Snapshot feature yet. :)

JRS

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #65 on: January 15, 2015, 03:09:47 PM »
Quote
My "mother tongue" is FBSL whose JIT compiler accepts classic ANSI C. That's why I don't publish the quick prototype's alien code here, but rather a precompiled executable only, until it's well polished and is ready to be ported to native OxygenBasic.

Using a good interpreter to prototype projects is a great way to see progress and define direction before starting on a compiled (production) version of it.

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #66 on: January 16, 2015, 12:22:48 AM »
UpdateLayeredWindow, is very fast, making it suitable for animation, it needs to be used {only} when the content of the window has changed, it writes directly onto the DirectDraw surface, meaning all the hard work of composition with the background or the other overlayed popup window is done by DWM.

And since Windows 8, the WS_EX_LAYERED style can also be used with child windows.

Blur effect has been removed from Windows 8, to reduce drastically the extra work for the graphic card.

I would never try to force transparency directly onto an OpenGL render context, but delegate this task to DWM because everything including OpenGL is ultimatly drawn on the DirectDraw surface with a fixed refresch rate of 60Hz.

The main advantage of a {true} layered window is that it could have empty region and variable opacity, while a {standard} layered window apply the same transparent level to all the child controls belonging to a specific popup window, that is very restrictive.

...





Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #67 on: January 16, 2015, 12:37:16 AM »
Hi Patrice,

everything including OpenGL is ultimatly drawn on the DirectDraw surface with a fixed refresch rate of 60Hz.

Are you talking about VSync here? If yes then what about wglSwapIntervalEXT(0) to disable altogether whatever VSync'ing mode the OpenGL driver is currently in (30FPS/60FPS/adaptive)?

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #68 on: January 16, 2015, 02:10:53 AM »
You should never use the OpenGL VSync with DWM (when AERO mode is enabled).
And starting with Windows 8+ the AERO mode is now always on.

The ultimate OpenGL setting is to apply the correct wglChoosePixelFormatARB and use the good PIXELFORMATDESCRIPTOR to build the best RC with wglCreateContext.
This is what the GDImage WGL_CreateWindow API does to create state of the art graphic scene (see the SkinnedChart project).

By the way, i didn't know what FBSL was, before you name it in one of your previous post :)

I use only the core Windows procedural flat API, because it is the only way i found to switch {easily} between all the different programming environment i am using.
« Last Edit: January 16, 2015, 02:49:47 AM by Patrice Terrier »

Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #69 on: January 16, 2015, 05:29:37 AM »
Luckily, I'm not using OpenGL for this project but rather GDI+ only while Charles seems quite content with being restricted to 60FPS only in his OpenGL solutions. We used to discuss it in the past. :)

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #70 on: January 16, 2015, 06:53:20 AM »
Quote
Luckily, I'm not using OpenGL for this project but rather GDI+

GDIPLUS works like a charm with DWM, especially in the AERO "crystal" mode.
The problem arise when using GDI32 and standard Windows controls, that are repainting themselves in 24-bit rather than 32-bit (aka RGB color, rather than ARGB/BGRA).
In this case the solution is to subclass the controls and perform a WM_PRINT with a 32-bit DIB DC, inside of the WM_PAINT message, and use GDI+ to draw the WM_PRINT image with the correct alpha channel.

...

Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #71 on: January 17, 2015, 04:42:45 PM »
Thanks again Patrice,

That hint of yours has just helped me to imprint the console's vertical scrollbar into its "client" area. :)

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #72 on: January 18, 2015, 12:41:35 AM »
Mike--

Cut and past of the code i am using inside of WinLIFT to skin ListBox and the Combo ListBox part,
the principle is the same with all the GDI32 child controls.

Code: [Select]
         switch (nCtrlType) {
         case CTRL_LISTBOX:
         case CTRL_COMBOLISTBOX:
              if (ListCount(hWnd) > 0) { // To deal with the DWM_AERO mode
                  if ((nCtrlType = CTRL_LISTBOX) && (skGetSystemMetrics(SK_DWM_AERO))) {
                      skChildOffset(hWnd, ofX, ofY);
                      if (wParam == 0) {
                          BeginPaint(hWnd, &ps); hDC = ps.hdc;
                      } else {
                          hDC = (HDC) wParam;
                      }
                      hDCmem = skOFFscreen(hDC, Xin, Yin, 1);
                      CallWindowProc(ChildMessage, WM_PRINTCLIENT, (WPARAM) hDCmem, lParam);
                      if (GdipCreateFromHDC(hDCmem, graphics) == 0) {
                          GdipCreateBitmapFromHBITMAP((HBITMAP) GetCurrentObject(hDCmem, OBJ_BITMAP), NULL, img);
                          GdipDrawImageRectI(graphics, img, 0, 0, Xin, Yin);
                          if (img) { GdipDisposeImage(img); }
                          GdipDeleteGraphics(graphics);
                      }
                      skOFFscreen(0, 0, 0, 0);
                      if (wParam == 0) { EndPaint(hWnd, &ps); }
                      return 0;
                  } else {
                      nRet = CallWindowProc(ChildMessage, uMsg, wParam, lParam);
                  }
              } else { // in case of empty ListBox
                  skChildOffset(hWnd, ofX, ofY);
                  if (wParam == 0) {
                      BeginPaint(hWnd, &ps); hDC = ps.hdc; }
                  else {
                      hDC = (HDC) wParam;
                  }
                  hDCmem = skOFFscreen(hDC, Xin, Yin, 1);
                  if (nCtrlType == CTRL_LISTBOX) {
                      skAlphaBlend(hDCmem, 0, 0, Xin, Yin, skGetHdcMemBmp(skPopupOwner(hWnd)), ofX, ofY, Xin, Yin);
                      skShadowBlt(hDCmem, 0, 0, Xin, Yin, skGetSysColor(SKCOLOR_SHADOW), skGetSystemMetrics(SK_TRANSLUCENCY));
                  } else { // 4.61 CTRL_COMBOLISTBOX
                      skFillRect(hDCmem, 0, 0, Xin, Yin, skGetSysColor(SKCOLOR_EDITCOLORBACK));
                  }
                  skOFFscreen(0, 0, 0, 0);

                  if (wParam == 0) { EndPaint(hWnd, &ps); }
                  return 0;
              }
              break;
« Last Edit: January 18, 2015, 12:55:20 AM by Patrice Terrier »

Mike Lobanovsky

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #73 on: January 18, 2015, 10:31:27 PM »
Yes Patrice,

That piece with WM_PRINTCLIENT is basically what I'm doing in my render proc too except that my only control I'm currently working with is a vertical scrollbar.

It seems to draw statically OK and it is responsive to mouse hover events (i.e. it redraws as expected when skinned). I'm drawing it on an alpha=250 GDI+ canvas so it looks almost opaque but not quite, which is sufficient for me to know it draws exactly as I expect it to.

However I'm not seeing it behave as a default control should if both the dialog and control were non-layered. It's thumb should be draggable with the left button down, and the left button down on its bottom arrow icon shouldn't redraw the icon.

Contrary to that, I can't drag the thumb down and I also get the bottom arrow icon unpainted when I press the mouse's left button on it. At the same time, the messages in both the control window and main dialog seem to be flowing as usual. I'm curious what the cause of such misbehavior might be.

.

Patrice Terrier

  • Guest
Re: OxyScheme (Scheme Interpreter in O2)
« Reply #74 on: January 19, 2015, 12:53:09 AM »
This is because the horizontal and vertical window's scrollbar are not real controls.
Indeed thay are drawn directly by the OS itself when it paints (WM_NCPAINT) the frame of a window popup or child window.

Tooks me years to figure how to handle them correctly into WinLIFT...
http://forum.pcsoft.fr/es-ES/pcsoft.fr.horssujet/2500-skins-gabarits/read.awp

And my solution was to draw everything myself using a region, to defeat Windows redrawing hover my nice skin and using a specific control of my own as the container.

Nothing easy as you would see below:
Code: [Select]
LRESULT CALLBACK ScrollBarProc(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam) {
    LRESULT nRet = 0;
    RECT rc = {0}, rw = {0};
    PAINTSTRUCT ps = {0}; POINT p = {0}; SCROLLBARINFO sbi = {0}; SCROLLINFO si = {0};

    HDC hDC = 0, hDCpaint = 0; HRGN hRgn = 0, hRgnClip = 0;
    HWND hMain = 0, hParent = 0;
    long x = 0, y = 0, w = 0, h = 0, xX = 0, yY = 0, UseSB = 0, nState = 0, ofX = 0, ofY = 0, hH = 0;
    long UseState = 0, dxy = 0, dxy1 = 0, dxy2 = 0, nDelta = 0, K = 0, nCtrlType = 0;

    static long TimerDelay, ThumbMoving, ThumbX, ThumbY;

    long nItem = GetScrollParent(hWnd, hParent);

    if (nItem > -1) {
        switch (uMsg) {
        case WM_TIMER:
             if (IsLButtonDown()) {
                GetWindowRect(hWnd, &rw);
                GetCursorPos(&p);
                x = p.x - rw.left; y = p.y - rw.top;
                if (SBmouseCheck(hWnd, hParent, UseSB, x, y, nItem, ThumbMoving)) { return nRet; }
                }
             else {
                if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
                ReleaseCapture();
             }
             break;

        case WM_MOUSEMOVE:
             if (ThumbMoving) {
                 nCtrlType = g_Child[skChild(hParent)].CtrlType;
                 GetCursorPos(&p);
                 sbi.cbSize = sizeof(sbi);
                 if (ThumbMoving == THUMB_HORZ) {
                     if (nCtrlType == CTRL_ZIMAGECTRL) {
                         si.cbSize =  sizeof(si);
                         si.fMask  = SIF_ALL;
                         GetScrollInfo(hParent, SB_HORZ, &si);
                         GetScrollBarInfo(hParent, OBJID_HSCROLL, &sbi);
                         dxy1 = sbi.rcScrollBar.left + sbi.dxyLineButton;
                         dxy2 = sbi.rcScrollBar.right - sbi.dxyLineButton;
                         p.x -= (sbi.xyThumbBottom - sbi.xyThumbTop) / 2;
                         nDelta = dxy2 - dxy1; if (nDelta == 0) { nDelta = 1; } // 10-31-2013 Avoid divide by zero
                         //dxy = (min(max(p.x, dxy1), dxy2) - dxy1);
                         //dxy = (long) (dxy * ((si.nMax - si.nMin) / (float) nDelta));
                         dxy = (long) ((min(max(p.x, dxy1), dxy2) - dxy1) * ((si.nMax - si.nMin) / (float) nDelta));

                         if (dxy != si.nPos) {
                            if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
                            si.nPos = dxy;
                            SetScrollInfo(hParent, SB_HORZ, &si, TRUE);
                            skRedrawWindow(hParent);
                         } }
                     else {
                         nDelta = (p.x - ThumbX); ThumbX = p.x;
                         if (nDelta > 0) {    // To the right
                             UseSB = SB_LINERIGHT; }
                         else {               // To the left
                             UseSB = SB_LINELEFT;
                         }
                         for (K = 0; K < abs(nDelta); ++K) {
                             if (IsLButtonDown() == 0) {
                                ThumbMoving = 0;
                                if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
                                ReleaseCapture();
                                break;
                             }
                             SendMessage(hParent, WM_HSCROLL, MAKLNG(UseSB, 0), 0);
                         }
                     } }
                 else {
                     si.cbSize =  sizeof(si);
                     si.fMask  = SIF_ALL;
                     GetScrollInfo(hParent, SB_VERT, &si);
                     GetScrollBarInfo(hParent, OBJID_VSCROLL, &sbi);
                     dxy1 = sbi.rcScrollBar.top + sbi.dxyLineButton;
                     dxy2 = sbi.rcScrollBar.bottom - sbi.dxyLineButton;
                     
                     if (nCtrlType == CTRL_ZIMAGECTRL) {
                        p.y -= (sbi.xyThumbBottom - sbi.xyThumbTop) / 2;
                     }
                   
                     nDelta = dxy2 - dxy1; if (nDelta == 0) { nDelta = 1; } // 10-31-2013 Avoid divide by zero
                   
                     //dxy = (min(max(p.y, dxy1), dxy2) - dxy1);
                     //dxy = (long) (dxy * ((si.nMax - si.nMin) / (float) nDelta));
                     dxy = (long) ((min(max(p.y, dxy1), dxy2) - dxy1) * ((si.nMax - si.nMin) / (float) nDelta));

                     if (dxy != si.nPos) {
                         InvalidateRect(hParent, NULL, 0);
                         switch (nCtrlType) {
                         case CTRL_LISTVIEW:
                              SendMessage(hParent, LVM_ENSUREVISIBLE, dxy, 0);
                              break;
                         case CTRL_LISTBOX:
                              SendMessage(hParent, LB_SETTOPINDEX, dxy, 0);
                              break;
                         case CTRL_SYSTREEVIEW:
                              SendMessage(hParent, WM_VSCROLL, MAKLNG(SB_THUMBTRACK, dxy), 0);
                              break;
                         case CTRL_EDIT:
                              SendMessage(hParent, WM_VSCROLL, MAKLNG(SB_THUMBTRACK, dxy), 0);
                              break;
                         case CTRL_ZIMAGECTRL:
                              if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
                              si.nPos = dxy;
                              SetScrollInfo(hParent, SB_VERT, &si, TRUE);
                              skRedrawWindow(hParent);
                              break;
                         }
                     }
                 }
             }
             break;

        case WM_LBUTTONDOWN:
             x = LOINT(lParam); y = HIINT(lParam);
             if (SBmouseCheck(hWnd, hParent, UseSB, x, y, nItem, ThumbMoving)) { return nRet; }
             ReleaseCapture();
             SetCapture(hWnd);
             if (GetFocus() != hParent) { SetFocus(hParent); }
             if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
             if (ThumbMoving == 0) {
                TimerDelay = 50; SetTimer(hWnd, 1, TimerDelay, NULL); }
             else {
                GetCursorPos(&p);
                ThumbX = p.x; ThumbY = p.y;
             }
             return nRet;

        case WM_LBUTTONUP:
             ThumbMoving = 0;
             if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
             ReleaseCapture();
             skUpdateWindow(hWnd, 0);
             break;

        case WM_SIZE:
             GetWindowRect(hParent, &rw);
             p.x = 0; p.y = 0; ClientToScreen(GetParent(hParent), &p);
             x = rw.left - p.x; y = rw.top - p.y;
             w = rw.right - rw.left; h = rw.bottom - rw.top;
             MoveWindow(hWnd, x, y, w, h, 0);
             GetClientRect(hParent, &rc);
       
             if (g_Child[skChild(hParent)].CtrlType == CTRL_COMBOLISTBOX) { nDelta = 1; } else { nDelta = 0; }
       
             hRgnClip = CreateRectRgn(0, 0, w, h + nDelta);
             hRgn = CreateRectRgn(rc.left + nDelta, rc.top + nDelta, rc.right + nDelta, rc.bottom + nDelta);
             CombineRgn(hRgnClip, hRgnClip, hRgn, RGN_DIFF);
             SetWindowRgn(hWnd, hRgnClip, 0);
             SetWindowRgn(hParent, hRgn, 0);
       
             if (nItem > -1) {
                UseSB = skCheckScrollBar(hParent);
                sbi.cbSize = sizeof(sbi);
                p.x = 0; p.y = 0; ClientToScreen(hWnd, &p);
                if ((UseSB == SCROLLBAR_VERT) || (UseSB == SCROLLBAR_BOTH)) {
                   GetScrollBarInfo(hParent, OBJID_VSCROLL, &sbi);
                   x = sbi.rcScrollBar.left - p.x;
                   y = sbi.rcScrollBar.top - p.y;
                   w = sbi.rcScrollBar.right - p.x - x;
                   h = sbi.rcScrollBar.bottom - p.y - y;
                   SetRect(&g_SI[nItem].rvu, x, y, x + sbi.dxyLineButton, y + sbi.dxyLineButton); // Vertical Up button
                   SetRect(&g_SI[nItem].rvt, x, y + sbi.xyThumbTop, x + sbi.dxyLineButton, y + sbi.xyThumbBottom); // Vertical Thumb button 
                   SetRect(&g_SI[nItem].rvd, x, y + h - sbi.dxyLineButton, x + sbi.dxyLineButton, y + h); // Vertical Down button
                }
                if ((UseSB == SCROLLBAR_HORZ) || (UseSB == SCROLLBAR_BOTH)) {
                   GetScrollBarInfo(hParent, OBJID_HSCROLL, &sbi);
                   x = sbi.rcScrollBar.left - p.x;
                   y = sbi.rcScrollBar.top - p.y;
                   w = sbi.rcScrollBar.right - p.x - x;
                   h = sbi.rcScrollBar.bottom - p.y - y;
                   SetRect(&g_SI[nItem].rhl, x, y, x + sbi.dxyLineButton, y + sbi.dxyLineButton); // Vertical Up button
                   SetRect(&g_SI[nItem].rht, x + sbi.xyThumbTop, y, x + sbi.xyThumbBottom, y + sbi.dxyLineButton); // Vertical Thumb button 
                   SetRect(&g_SI[nItem].rhr, x + w - sbi.dxyLineButton, y, x + w, y + sbi.dxyLineButton); // Vertical Down button
                }
             }
             return nRet;

        case WM_PRINT:
        case WM_PAINT:
        case WM_ERASEBKGND:
             if (uMsg == WM_PAINT) {
                hDCpaint = BeginPaint(hWnd, &ps); }
             else { // WM_PRINT
                hDCpaint = (HDC) wParam;
             }
             hDC = hDCpaint;

             UseSB = skCheckScrollBar(hParent);
             // GOSUB DetectState
             if (IsLButtonDown()) {
                 GetWindowRect(hWnd, &rw);
                 GetCursorPos(&p);
                 p.x -= rw.left; p.y -= rw.top;
                 
                 GetClientRect(hWnd, &rc);
                 if ((UseSB == SCROLLBAR_BOTH) && (p.x > rc.right - GetSystemMetrics(SM_CXVSCROLL) - 1) && (y > rc.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1)) {
                     p.x = 0; p.y = 0; } // Do nothing
                 else {
                     if (p.x < rc.right - GetSystemMetrics(SM_CXVSCROLL)) {
                         // HORIZONTAL SCROLLBAR
                         if (PtInRect(&g_SI[nItem].rhl, p)) {      // Button Left
                            nState = 4; }
                         else if (PtInRect(&g_SI[nItem].rht, p)) { // Button Thumb
                            nState = 5; }
                         else if (PtInRect(&g_SI[nItem].rhr, p)) { // Button Right
                            nState = 6; }
                         else {                                    // Page
                            nState = 8; }
                         }
                     else {
                         // VERTICAL SCROLLBAR
                         if (PtInRect(&g_SI[nItem].rvu, p)) {      // Button Up
                            nState = 1; }
                         else if (PtInRect(&g_SI[nItem].rvt, p)) { // Button Thumb
                            nState = 2; }
                         else if (PtInRect(&g_SI[nItem].rvd, p)) { // Button Down
                            nState = 3; }
                         else {                                    // Page
                            nState = 7;
                         }
                     }
                 }
             }
             hMain = skPopupOwner(hWnd);

             sbi.cbSize =  sizeof(sbi);
             p.x = 0; p.y = 0; ClientToScreen(hWnd, &p);
       
             if ((UseSB == SCROLLBAR_VERT) || (UseSB == SCROLLBAR_BOTH)) {
       
                GetScrollBarInfo(hParent, OBJID_VSCROLL, &sbi);
                x = sbi.rcScrollBar.left - p.x;
                y = sbi.rcScrollBar.top - p.y;
                w = sbi.rcScrollBar.right - p.x - x;
                h = sbi.rcScrollBar.bottom - p.y - y;
       
                if (UseSB == SCROLLBAR_BOTH) { GetClientRect(hWnd, &rc); hH = rc.bottom - h; }
                if (uMsg == WM_PAINT) {
                   hDC = skOFFscreen(hDCpaint, w, h + hH, 1);
                   xX = x; yY = y; }
                else {
                   xX = 0; yY = 0;
                }
       
                GetWindowRect(hMain, &rw);
                ofX = sbi.rcScrollBar.left - rw.left; ofY = sbi.rcScrollBar.top - rw.top;
       
                if (g_Child[skChild(hParent)].CtrlType == CTRL_COMBOLISTBOX) {
                   nDelta = 1;
                   w = w - nDelta;
                   // Draw shadow border.
                   skDrawRect3D(hDC, 0, 0, x + w + 2, h + 2, skGetSysColor(SKCOLOR_EDITCOLORRECTUP), skGetSysColor(SKCOLOR_EDITCOLORRECTDOWN)); }
                else {
                   nDelta = 0;
                }
               
                skAlphaBlend(hDC, x - xX, y - yY, w, h, skGetHdcMemBmp(hMain), ofX, ofY, w, h);
       
                skPaintButton(hDC, 3, g_VertBar, x - xX, y - yY, w, h);
       
                SetRect(&g_SI[nItem].rvt, x, y + sbi.xyThumbTop, x + sbi.dxyLineButton, y + sbi.xyThumbBottom); // Vertical Thumb button 
                if (nState == 1) {
                    UseState = 2; }
                else {
                    UseState = 1;
                }
                skPaintButton(hDC, UseState, g_VertBar, g_SI[nItem].rvu.left - xX, g_SI[nItem].rvu.top - yY, g_SI[nItem].rvu.right - g_SI[nItem].rvu.left, g_SI[nItem].rvu.bottom - g_SI[nItem].rvu.top);
                    DrawPicto(hDC, 1, g_SI[nItem].rvu.left - xX, g_SI[nItem].rvu.top - yY, g_SI[nItem].rvu.right - g_SI[nItem].rvu.left, g_SI[nItem].rvu.bottom - g_SI[nItem].rvu.top);
                if (nState = 2) {
                    UseState = 5; }
                else {
                    UseState = 1;
                }
                skPaintButton(hDC, UseState, g_VertBar, g_SI[nItem].rvt.left - xX, g_SI[nItem].rvt.top - yY, g_SI[nItem].rvt.right - g_SI[nItem].rvt.left, g_SI[nItem].rvt.bottom - g_SI[nItem].rvt.top);
                    DrawPicto(hDC, 2, g_SI[nItem].rvt.left - xX, g_SI[nItem].rvt.top - yY, g_SI[nItem].rvt.right - g_SI[nItem].rvt.left, g_SI[nItem].rvt.bottom - g_SI[nItem].rvt.top);
                if (nState = 3) {
                    UseState = 2; }
                else {
                    UseState = 1;
                }
                skPaintButton(hDC, UseState, g_VertBar, g_SI[nItem].rvd.left - xX, g_SI[nItem].rvd.top - yY, g_SI[nItem].rvd.right - g_SI[nItem].rvd.left, g_SI[nItem].rvd.bottom - g_SI[nItem].rvd.top);
                    DrawPicto(hDC, 3, g_SI[nItem].rvd.left - xX, g_SI[nItem].rvd.top - yY, g_SI[nItem].rvd.right - g_SI[nItem].rvd.left, g_SI[nItem].rvd.bottom - g_SI[nItem].rvd.top);
                if (UseSB == SCROLLBAR_BOTH) { // Paint the right corner
                    skAlphaBlend(hDC, x - xX, y - yY + h, w, hH, skGetHdcMemBmp(hMain), ofX, ofY + h, w, hH);
                    skPaintButton(hDC, 3, g_VertBar, x - xX, y - yY + h, w, hH);
                }

                if (uMsg == WM_PAINT) { skOFFscreen(0, x, y, 0); }

             }
             if ((UseSB == SCROLLBAR_HORZ) || (UseSB == SCROLLBAR_BOTH)) {
       
                GetScrollBarInfo(hParent, OBJID_HSCROLL, &sbi);
                x = sbi.rcScrollBar.left - p.x;
                y = sbi.rcScrollBar.top - p.y;
                w = sbi.rcScrollBar.right - p.x - x;
                h = sbi.rcScrollBar.bottom - p.y - y;
       
                if (uMsg == WM_PAINT) {
                   hDC = skOFFscreen(hDCpaint, w, h, 1);
                   xX = x; yY = y; }
                else {
                   xX = 0; yY = 0;
                }
       
                GetWindowRect(hMain, &rw);
                ofX = sbi.rcScrollBar.left - rw.left; ofY = sbi.rcScrollBar.top - rw.top;
       
                skAlphaBlend(hDC, x - xX, y - yY, w, h, skGetHdcMemBmp(hMain), ofX, ofY, w, h);
       
                skPaintButton(hDC, 3, g_HorzBar, x - xX, y - yY, w, h);
       
                SetRect(&g_SI[nItem].rht, x + sbi.xyThumbTop, y, x + sbi.xyThumbBottom, y + sbi.dxyLineButton); // Vertical Thumb button 
       
                if (nState == 4) { UseState = 2; } else { UseState = 1; }
                skPaintButton(hDC, UseState, g_HorzBar, g_SI[nItem].rhl.left - xX, g_SI[nItem].rhl.top - yY, g_SI[nItem].rhl.right - g_SI[nItem].rhl.left, g_SI[nItem].rhl.bottom - g_SI[nItem].rhl.top);
                    DrawPicto(hDC, 4, g_SI[nItem].rhl.left - xX, g_SI[nItem].rhl.top - yY, g_SI[nItem].rhl.right - g_SI[nItem].rhl.left, g_SI[nItem].rhl.bottom - g_SI[nItem].rhl.top);
                if (nState == 5) { UseState = 5; } else { UseState = 1; }
                skPaintButton(hDC, UseState, g_HorzBar, g_SI[nItem].rht.left - xX, g_SI[nItem].rht.top - yY, g_SI[nItem].rht.right - g_SI[nItem].rht.left, g_SI[nItem].rht.bottom - g_SI[nItem].rht.top);
                    DrawPicto(hDC, 5, g_SI[nItem].rht.left - xX, g_SI[nItem].rht.top - yY, g_SI[nItem].rht.right - g_SI[nItem].rht.left, g_SI[nItem].rht.bottom - g_SI[nItem].rht.top);
                if (nState == 6) { UseState = 2; } else { UseState = 1; }
                skPaintButton(hDC, UseState, g_HorzBar, g_SI[nItem].rhr.left - xX, g_SI[nItem].rhr.top - yY, g_SI[nItem].rhr.right - g_SI[nItem].rhr.left, g_SI[nItem].rhr.bottom - g_SI[nItem].rhr.top);
                    DrawPicto(hDC, 6, g_SI[nItem].rhr.left - xX, g_SI[nItem].rhr.top - yY, g_SI[nItem].rhr.right - g_SI[nItem].rhr.left, g_SI[nItem].rhr.bottom - g_SI[nItem].rhr.top);

                if (uMsg == WM_PAINT) { skOFFscreen(0, x, y, 0); }

             }

             if (uMsg == WM_PAINT) {
                EndPaint(hWnd, &ps);
             }
             if (uMsg == WM_ERASEBKGND) {
                nRet = 1; }
             else {
                nRet = 0;
             }
             return nRet;

        case WM_DESTROY:
             if (TimerDelay) { KillTimer(hWnd, 1); TimerDelay = 0; }
             break;
        }
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}