Announcement

Collapse
No announcement yet.

Skin support for plugin window controls

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Skin support for plugin window controls

    Hello! I am writing my own plugin. It creates own window. The resource dialog fits nicely into the new window. But here is the problem - all controls do not have a skin support:

    Click image for larger version

Name:	waa.jpg
Views:	1
Size:	12.3 KB
ID:	4481043

    How to fix it?

  • #2
    I'm assuming you've already checked the source code for the example embedded window plugin here: http://forums.winamp.com/showthread.php?t=322114

    If you can, post your source code in a zip or 7z archive so I can see if I can find any obvious errors.
    | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
    | Save your playlist first! | Live voice-over | X-Fade 2.5 |
    | AterKast (Source DSP) | More of my stuff... |

    Comment


    • #3
      Originally Posted by thinktink View Post
      I'm assuming you've already checked the source code for the example embedded window plugin here
      All given links in this thread to the webpage with the example window are no longer available, i.e. article deleted.

      But all I could find was the need to handle messages of embedded dialog proc like this:

      code:
      case WM_CTLCOLORLISTBOX:
      case WM_CTLCOLORDLG:
      case WM_CTLCOLORBTN:
      case WM_CTLCOLORSTATIC:
      case WM_CTLCOLOREDIT:
      case WM_DRAWITEM:
      {
      return WADlg_handleDialogMsgs(hwnd, msg, wParam, lParam);
      }

      Now it works correct. Is everything right here?



      Another one offtopic question: I added the plugin menu item to Winamp main menu using InsertMenuItem() function with text and shortcut:
      code:
      wchar_t menu_caption[] = L"My plugin name\tAlt+B";
      By handling WM_COMMAND I process menu click. And it works. But when I press Alt+B shortcut associated with menu item - nothing happens.

      How to fix this?

      Comment


      • #4
        I could have SWORN I downloaded that example plugin a long time ago but I still can't find it.

        However, you can use the following for an example:
        What is Winamp? Why is Winamp? How is Winamp? All these burning questions and issues discussed within.

        It should have everything you need to learn standard skinning support. It also contains an example for shortcut handling.

        Unfortunately, there is no native skinning support for radio buttons and checkboxes.

        Your edit box doesn't look right. Before going on a tare, check the example I provided for that.
        | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
        | Save your playlist first! | Live voice-over | X-Fade 2.5 |
        | AterKast (Source DSP) | More of my stuff... |

        Comment


        • #5
          thinktink, thanks for example!

          I have added this code:
          code:
          api_application *WASABI_API_APP;
          api_service *WASABI_API_SVC;

          template <class api_T>
          void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
          {
          if(WASABI_API_SVC)
          {
          waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t);

          if(factory)
          {
          api_t = (api_T*)factory->getInterface();
          }
          }
          }

          on init():
          code:
          WADlg_init(plugin.hwndParent);

          WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);

          if(WASABI_API_SVC != NULL)
          {
          ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
          }

          except WASABI_API_CREATEDIALOGW I still use: CreateDialogParam(...

          code:
          ACCEL accel = {FVIRTKEY | FALT, 'A', MY_PLUGINMENU_ID};
          HACCEL hAccel = CreateAcceleratorTable(&accel, 1);
          WASABI_API_APP->app_addAccelerators(embedWnd, &hAccel, 1, TRANSLATE_MODE_NORMAL);

          code:
          DlgProc:
          case WM_COMMAND:
          {
          if(LOWORD(wParam) == MY_PLUGINMENU_ID)
          {
          MessageBox(hwnd, L"This is hotkey!", L"", MB_OK);
          }

          break;
          }

          But when I press Alt+A nothing happens. Hotkey not work. Maybe I something missed?

          Comment


          • #6
            Another one question: thinktink, your project compiled succesfully, but when I try to compile my project compiler throws error:
            error LNK2001: unresolved external symbol "class api_language * languageManager" (?languageManager@@3PAVapi_language@@A)
            I add paths to Winamp SDK in PROJECT->my_project properties->VC++ Directories->Include Directories:
            D:\Winamp_SDK;D:\Winamp_SDK\Wasabi;D:\Winamp_SDK\Agave;$(IncludePath)
            But compiler still error LNK2001: unresolved external symbol "class api_language * languageManager"

            Comment


            • #7
              Originally Posted by Ingvar View Post
              thinktink, thanks for example!

              I have added this code:
              code:
              api_application *WASABI_API_APP;
              api_service *WASABI_API_SVC;

              template <class api_T>
              void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
              {
              if(WASABI_API_SVC)
              {
              waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t);

              if(factory)
              {
              api_t = (api_T*)factory->getInterface();
              }
              }
              }

              on init():
              code:
              WADlg_init(plugin.hwndParent);

              WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);

              if(WASABI_API_SVC != NULL)
              {
              ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
              }

              except WASABI_API_CREATEDIALOGW I still use: CreateDialogParam(...

              code:
              ACCEL accel = {FVIRTKEY | FALT, 'A', MY_PLUGINMENU_ID};
              HACCEL hAccel = CreateAcceleratorTable(&accel, 1);
              WASABI_API_APP->app_addAccelerators(embedWnd, &hAccel, 1, TRANSLATE_MODE_NORMAL);

              code:
              DlgProc:
              case WM_COMMAND:
              {
              if(LOWORD(wParam) == MY_PLUGINMENU_ID)
              {
              MessageBox(hwnd, L"This is hotkey!", L"", MB_OK);
              }

              break;
              }

              But when I press Alt+A nothing happens. Hotkey not work. Maybe I something missed?
              How have you generated/set/defined "MY_PLUGINMENU_ID"?

              I do it with:
              code:
              MY_PLUGINMENU_ID=(UINT)::SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)0,IPC_REGISTER_LOWORD_COMMAND);
              | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
              | Save your playlist first! | Live voice-over | X-Fade 2.5 |
              | AterKast (Source DSP) | More of my stuff... |

              Comment


              • #8
                Originally Posted by Ingvar View Post
                ...

                code:
                DlgProc:
                case WM_COMMAND:
                {
                if(LOWORD(wParam) == MY_PLUGINMENU_ID)
                {
                MessageBox(hwnd, L"This is hotkey!", L"", MB_OK);
                }

                break;
                }

                But when I press Alt+A nothing happens. Hotkey not work. Maybe I something missed?
                Have you subclassed the Winamp main HWND and checking WM_COMMAND/WM_SYSCOMMAND in there or are you checking inside your own embed wnd's DlgProc instead? If you can't subclass Winamp's main HWND then you can also check inside a Windows Hook (see SetWindowsHookEx in the MSDN for usage).
                | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
                | Save your playlist first! | Live voice-over | X-Fade 2.5 |
                | AterKast (Source DSP) | More of my stuff... |

                Comment


                • #9
                  Originally Posted by Ingvar View Post
                  Another one question: thinktink, your project compiled succesfully, but when I try to compile my project compiler throws error:

                  I add paths to Winamp SDK in PROJECT->my_project properties->VC++ Directories->Include Directories:

                  But compiler still error LNK2001: unresolved external symbol "class api_language * languageManager"
                  I'm not sure about your version of the compiler but I'm using the free Microsoft Visual C++ 2010 Express to build gen_classicart. If you're doing the same:

                  Open "gen_yourplugin" Property Pages

                  Select: All Configurations

                  Go to: Configuration Properties -> C/C++ -> General

                  Inspect "Additional Include Directories" and make sure the subfolders in the Winamp SDK folder labeled "Wasabi", "Agave", and "Winamp" are being included.

                  Attached Files
                  | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
                  | Save your playlist first! | Live voice-over | X-Fade 2.5 |
                  | AterKast (Source DSP) | More of my stuff... |

                  Comment


                  • #10
                    Originally Posted by thinktink View Post
                    How have you generated/set/defined "MY_PLUGINMENU_ID"?
                    I didn't generate value for MY_PLUGINMENU_ID. Just assign a value:
                    code:
                    UINT MY_PLUGINMENU_ID = 0xa2bc;
                    Originally Posted by Ingvar View Post
                    but when I try to compile my project compiler throws error: unresolved external symbol "class api_language * languageManager"
                    It was my mistake . When I was writing the code I wrote accidentally.
                    code:
                    api_language *WASAPI_API_LNG;
                    instead of:
                    code:
                    api_language *WASABI_API_LNG;
                    Originally Posted by Ingvar View Post
                    But when I press Alt+A nothing happens
                    I also made a mistake here . And I found it while reading your project. I wrote:
                    code:
                    WASABI_API_APP->app_addAccelerators(dialogWnd, &hAccel, 1, TRANSLATE_MODE_NORMAL);
                    instead of:
                    code:
                    WASABI_API_APP->app_addAccelerators(dialogWnd, &hAccel, 1, TRANSLATE_MODE_GLOBAL);
                    But I found a bug in your code too . Look at quit() function:
                    code:
                    // restores the original playlist window proc now that we are closing and if the window was subclassed
                    HWND pe_wnd = (HWND)SendMessage(plugin.hwndParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
                    if(GetWindowLongPtr(pe_wnd,GWLP_WNDPROC) == (LONG_PTR)SubclassPlaylistProc){
                    SetWindowLongPtr(pe_wnd,GWLP_WNDPROC,(LONG_PTR)oldPlaylistWndProc);
                    }

                    At this moment, the GetWindowLongPtr() function will return 0x00 because the parent window no longer exists. You can verify it using debugger.
                    Therefore, this code should be moved to the parent window's dialog proc into the WM_DESTROY handler.

                    But now it's time to return to the main question of the topic.
                    I opened your "gen_classicart" project and edited the resource dialog: I placed some controls - button, text label and a edit control. Compile and run it in Winamp:



                    It looks like skin support isn't working for controls. I didn't modify source code except GEN_INIT_FAILURE replace with 1 (I didn't find a definition). So how to theme the controls?

                    Comment


                    • #11
                      Originally Posted by Ingvar View Post
                      I didn't generate value for MY_PLUGINMENU_ID. Just assign a value:
                      code:
                      UINT MY_PLUGINMENU_ID = 0xa2bc;...
                      Go ahead and do it the way I showed, it will help prevent future conflicts with other plugins and is the SDK way to do it.

                      Originally Posted by Ingvar View Post
                      ...


                      But I found a bug in your code too . Look at quit() function:
                      code:
                      // restores the original playlist window proc now that we are closing and if the window was subclassed
                      HWND pe_wnd = (HWND)SendMessage(plugin.hwndParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
                      if(GetWindowLongPtr(pe_wnd,GWLP_WNDPROC) == (LONG_PTR)SubclassPlaylistProc){
                      SetWindowLongPtr(pe_wnd,GWLP_WNDPROC,(LONG_PTR)oldPlaylistWndProc);
                      }

                      At this moment, the GetWindowLongPtr() function will return 0x00 because the parent window no longer exists. You can verify it using debugger.
                      Therefore, this code should be moved to the parent window's dialog proc into the WM_DESTROY handler.
                      That is actually a good thing, it means that the window is being destroyed BEFORE your plugin is unloaded from memory.
                      Trying to "unsubclass" a shared window is problematic at best as not all plugins are guaranteed to be loaded and unloaded in the correct order to prevent subclass procedure chain breaks, which is very bad. If your subclass proceedure is unloaded (by dint of your plugin being unloaded) before the window it's connected to is destroyed it will cause Access Violations when the (now non-existent) procedure is called.


                      Originally Posted by Ingvar View Post

                      But now it's time to return to the main question of the topic.
                      I opened your "gen_classicart" project and edited the resource dialog: I placed some controls - button, text label and a edit control. Compile and run it in Winamp:



                      It looks like skin support isn't working for controls. I didn't modify source code except GEN_INIT_FAILURE replace with 1 (I didn't find a definition). So how to theme the controls?
                      Since that window never had standard controls to skin, the original developer never put in the code to skin any. Go ahead and add the handler inside "art_dlgproc" switch/case statements, between the WM_DESTROY and WM_PAINT case statements seems like a good place.
                      PHP Code:
                      case WM_CTLCOLORLISTBOX:
                      case 
                      WM_CTLCOLORDLG:
                      case 
                      WM_CTLCOLORBTN:
                      case 
                      WM_CTLCOLORSTATIC:
                      case 
                      WM_CTLCOLOREDIT:
                      case 
                      WM_DRAWITEM:
                      {
                      return 
                      WADlg_handleDialogMsgs(hwndmsgwParamlParam);



                      PHP Code:
                      #define GEN_INIT_FAILURE 1
                      #define GEN_INIT_SUCCESS 0 
                      Those are defined in GEN.H in the Winamp SDK
                      | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
                      | Save your playlist first! | Live voice-over | X-Fade 2.5 |
                      | AterKast (Source DSP) | More of my stuff... |

                      Comment


                      • #12
                        Found it!

                        I finally found the example package.

                        Attached.
                        Attached Files
                        | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
                        | Save your playlist first! | Live voice-over | X-Fade 2.5 |
                        | AterKast (Source DSP) | More of my stuff... |

                        Comment


                        • #13
                          Originally Posted by thinktink View Post
                          I finally found the example package
                          Thanks!
                          Originally Posted by thinktink View Post
                          That is actually a good thing, it means that the window is being destroyed BEFORE your plugin is unloaded from memory.
                          I think you understood me correctly. Anyway it would be correct to move unsubclass code to the parent window's dialog proc at the WM_DESTROY.
                          Originally Posted by thinktink View Post
                          Since that window never had standard controls to skin, the original developer never put in the code to skin any. Go ahead and add the handler inside "art_dlgproc" switch/case statements
                          Thanks! Now it looks right:



                          Originally Posted by thinktink View Post
                          Those are defined in GEN.H in the Winamp SDK
                          It's weird but I downloaded Winamp SDK 5.5 from here https://download.nullsoft.com/winamp...WA5.55_SDK.exe and Winamp/gen.h file contains only #define GEN_INIT_SUCCESS 0.

                          While looking through your code and using the debugger, I found another one bug. Looks like it's a winamp bug.
                          Look at this code:

                          code:
                          else if(msg == WM_WA_IPC)
                          {
                          if(lParam == IPC_CB_MISC && (wParam == IPC_CB_MISC_TITLE || wParam == IPC_CB_MISC_TITLE_RATING))
                          {

                          //THIS BLOCK RUNS 4 TIMES !!!

                          // art change
                          PostMessage(myWndChild,WM_USER,0,0);
                          }

                          As I marked, this block runs 4 times when track changing or when you press ffwd/rwd. I think you need to handle another notification to monitor the track change.

                          Offtopic. In Visual Studio 2012 I set configuration to "Release" and compiled my plugin dll. But Winamp loads it only on my PC.
                          I sent it to another PC and also tried it on a virtual system using the latest Winamp, but in these cases Winamp doesn't load my plugin.
                          In the "Preferences" window I see "NOT LOADED" message. And log file \AppData\Roaming\Winamp\winamp.log contains error:
                          [5.9.0.9999] [error] Error when loading the plugin 'gen_wa_plugin.dll'! Error code : 126!
                          For example, gen_classicart.dll works fine.
                          What other project settings do I need to set to compile my plugin to distribute it on other PCs?

                          Comment


                          • #14
                            Originally Posted by Ingvar View Post
                            Thanks!
                            You're welcome.



                            Originally Posted by Ingvar View Post
                            I think you understood me correctly. Anyway it would be correct to move unsubclass code to the parent window's dialog proc at the WM_DESTROY.
                            Nope, as I said before, Winamp is not guaranteed to unload other plugins that also subclass the Winamp windows in the correct order. In this case, at least WM_NCDESTROY (if not more messages) will be called after your attempt to unsubclass after the call to WM_DESTROY completes, which can potentially break the subclass chain. If it does then it will cause an access violation if the plugin's subclass proc become unavailable. You might not even see the AV error unless you put Winamp under a debugger. To be honest, the unsubclassing code should just be outright deleted. However, I'm not the original developer of the plugin so I can only guess what the original developer intended.


                            Originally Posted by Ingvar View Post
                            Thanks! Now it looks right:

                            It's definitely an improvement but not entirely correct, it looks like there's a white border around what looks like edit controls, which is not part of the selected skin. The checkboxes also aren't completely skinned, but as I said before, there's currently no native support for skinning checkboxes or radio buttons.


                            Originally Posted by Ingvar View Post
                            It's weird but I downloaded Winamp SDK 5.5 from here https://download.nullsoft.com/winamp...WA5.55_SDK.exe and Winamp/gen.h file contains only #define GEN_INIT_SUCCESS 0.
                            Ok, that's odd. I may have put in the other #define into the SDK source myself and forgot that I did so. If you don't want to modify the SDK headers then just add...
                            PHP Code:
                            #ifndef GEN_INIT_FAILURE
                            #define GEN_INIT_FAILURE 1
                            #endif //GEN_INIT_FAILURE 
                            ...right after the GEN.H include. This way will prevent any warning in the future about duplicate definitions if a new version of the SDK is published with new/updated #define(s).


                            Originally Posted by Ingvar View Post
                            While looking through your code and using the debugger, I found another one bug. Looks like it's a winamp bug.
                            Look at this code:

                            code:
                            else if(msg == WM_WA_IPC)
                            {
                            if(lParam == IPC_CB_MISC && (wParam == IPC_CB_MISC_TITLE || wParam == IPC_CB_MISC_TITLE_RATING))
                            {

                            //THIS BLOCK RUNS 4 TIMES !!!

                            // art change
                            PostMessage(myWndChild,WM_USER,0,0);
                            }

                            As I marked, this block runs 4 times when track changing or when you press ffwd/rwd. I think you need to handle another notification to monitor the track change.
                            I'm not the original developer of the plugin so I can only assume or guess the original intent. It may very well be that those messages are all included to catch all instances of changes for peculiar or specific situations and multiple calls are simply an unfortunate result of the requirements during normal operations.


                            Originally Posted by Ingvar View Post
                            Offtopic. In Visual Studio 2012 I set configuration to "Release" and compiled my plugin dll. But Winamp loads it only on my PC.
                            I sent it to another PC and also tried it on a virtual system using the latest Winamp, but in these cases Winamp doesn't load my plugin.
                            In the "Preferences" window I see "NOT LOADED" message. And log file \AppData\Roaming\Winamp\winamp.log contains error:

                            For example, gen_classicart.dll works fine.
                            What other project settings do I need to set to compile my plugin to distribute it on other PCs?
                            I don't remember the name of the settings but what you're describing sounds like a missing dependency on the computer where the plugin is not loading. Google would be a better resource to figure out what's missing than me. I don't use Visual Studio on a regular basis, only when I absolutely have to, so I'm not uber familiar with specifics or details.
                            | Opus Audio Codec plugins 2.0 | Embedded Album Art | DiskWrite |
                            | Save your playlist first! | Live voice-over | X-Fade 2.5 |
                            | AterKast (Source DSP) | More of my stuff... |

                            Comment


                            • #15
                              Originally Posted by thinktink View Post
                              Winamp is not guaranteed to unload other plugins that also subclass the Winamp windows in the correct order.
                              Do you mean that if WM_DESTROY event occurs and we try to assign a window procedure that holds in memory of already unloaded at this time plugin, then an AV error will occurs?
                              Originally Posted by thinktink View Post
                              The checkboxes also aren't completely skinned
                              On my last screenshot I overridden its WM_PAINT handler and did ownerdraw checkbox painting. Should it look different? How?
                              Originally Posted by thinktink View Post
                              there's currently no native support for skinning checkboxes or radio buttons
                              It looks like there is skinning support for push buttons and scrollbar only.
                              I used code to get skin HBITMAP and save it to file:
                              code:
                              SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_GENSKINBITMAP);
                              And skin HBITMAP contains this:


                              As we can see, there glyphs for push-button and scrollbar are only.

                              By the way, where is this image stored? In what file? I looked through all the graphic and resources files in the Winamp folder but didn't find these glyphs.

                              Originally Posted by thinktink View Post
                              it looks like there's a white border around what looks like edit controls, which is not part of the selected skin
                              Ok, how to fix it to assign edit control skin support? How is it done in Jump to File plugin, for example?



                              Here we can see specific themed borders at the bottom and right side of EDIT control. I tried to change the styles of the control, but I could not get such borders.

                              Is the source code of Jump to File plugin available? I can't find it. I would be able to see the implementation of the edit control.

                              Comment

                              Working...
                              X
                              😀
                              🥰
                              🤢
                              😎
                              😡
                              👍
                              👎