-3
    {- 2012-05-16

    ghc --make -optl-mwindows fileName.hs

option -mwindows is passed to the linker!
attempting to read from stdin with -mwindows may cause a runtime error
any output on stdout/stderr will be lost.
ghc links console app with stdout/stderr as default
-}



--import Graphics.Win32
import Graphics.Win32 hiding (messageBox, c_MessageBox) -- bugfix
import System.Win32.DLL
import Control.Exception (bracket)
import Foreign
import System.Exit




-- bugfix whole msg box
messageBox :: HWND -> String -> String -> MBStyle -> IO MBStatus
messageBox wnd text caption style =
  withTString text $ \ c_text ->
  withTString caption $ \ c_caption ->
  failIfZero "MessageBox" $ c_MessageBox wnd c_text c_caption style
foreign import stdcall safe "windows.h MessageBoxW"
  c_MessageBox :: HWND -> LPCTSTR -> LPCTSTR -> MBStyle -> IO MBStatus


main :: IO ()
main = do
    mainInstance <- getModuleHandle Nothing
    hwnd <- createWindow_ 200 200 wndProc mainInstance
    createButton_ hwnd mainInstance
    messagePump hwnd

wndProc :: HWND -> WindowMessage -> WPARAM -> LPARAM -> IO LRESULT
wndProc hwnd wmsg wParam lParam
    | wmsg == wM_DESTROY = do
        sendMessage hwnd wM_QUIT 1 0
        return 0
    | wmsg == wM_COMMAND && wParam == 3 = do
        messageBox nullPtr "You pressed me." "Haskell msg" 0
        return 0
    | otherwise = defWindowProc (Just hwnd) wmsg wParam lParam


createWindow_ :: Int -> Int -> WindowClosure -> HINSTANCE -> IO HWND
createWindow_ width height wndProc mainInstance = do
    let winClass = mkClassName "Window Empty"
    icon <- loadIcon Nothing iDI_APPLICATION
    cursor <- loadCursor Nothing iDC_ARROW
    bgBrush <- createSolidBrush (rgb 255 0 0)
    registerClass (cS_VREDRAW + cS_HREDRAW, mainInstance, Just icon, Just cursor, Just bgBrush, Nothing, winClass)
    w <- createWindow winClass "Window Empty" wS_OVERLAPPEDWINDOW Nothing Nothing (Just width) (Just height) Nothing Nothing mainInstance wndProc
    showWindow w sW_SHOWNORMAL
    updateWindow w
    return w


createButton_ :: HWND -> HINSTANCE -> IO ()
createButton_ hwnd mainInstance = do
    hBtn <- createButton "Button test" wS_EX_CLIENTEDGE (bS_PUSHBUTTON + wS_VISIBLE + wS_CHILD) (Just 50) (Just 80) (Just 80) (Just 20) (Just hwnd) (Just (castUINTToPtr 3)) mainInstance
    return ()


messagePump :: HWND -> IO ()
messagePump hwnd = allocaMessage $ \ msg ->
    let pump = do
        getMessage msg (Just hwnd) `catch` \ _ -> exitWith ExitSuccess
        translateMessage msg
        dispatchMessage msg
        pump
    in pump

Original link is here

Usage: copy/paste code, save it in a file, compile with ghc --make -optl-mwindows fileName.hs and it will create nice little window. It's basic C/C++ like here.

This and two more examples below are ONLY raw createWindow code I could find written in Haskell :(

My rethoric questions:

  • I understand that C++ process quite well. You create come functions, winProc will call it if some win_msg is true...
    But, it's not the only way. Soon enough MS put that in mfc classes. And we have EventListeners that do basically the same thing. Instead of directly testing win_msg you create/addEventListener, pass desired function and it works.
    But grouping of code is nicer and easier for maintenance and it's more OO like.

  • What are Haskell's ways for Haskellising winProc? There probably are ways to mimic addEventListener(evt, my_func).
    How would that code look like? How many different solutions are there? Is it usable?

  • And more important, is there some Haskell like (better) way that I an not aware of?

  • In what ways you can use that code, improve it a bit and create something like wxWidgets or gtk, but extremely simplified, easy to understand, etc.
Community
  • 1
  • 1
CoR
  • 3,826
  • 5
  • 35
  • 42
  • 1
    Just to explain what some of the downvotes are probably about: "write my code for me" is the classic red-flag question of somebody who is going to be an ungrateful time sink. Your question went even a step further, to "write my code for me and document it, too!". Please: do your homework, and take a shot at it yourself. I'm much more likely to try helping you correct a wrong attempt at scratching your itch than to make an attempt at scratching your itch myself. – Daniel Wagner May 29 '12 at 17:11
  • Yes Daniel, you are quite right :) It seems that I am really lazy. Too lazy to know it for myself. Internet is obviously "filled" with pure Haskell GUI examples and tutorials. Or is it? ;) My question seems so simple that anyone could answer it :D If that's true, why there isn't any pure GUI tutorial for Haskell? – CoR May 29 '12 at 20:29
  • 1
    GUI library and "Quick Start" tutorial: http://www.haskell.org/haskellwiki/WxHaskell/Quick_start – Tony Jun 20 '12 at 13:40

1 Answers1

1

This is second version of createWindow. Link

Slightly different, but unfortunately without any comments, or explanation why are some things like they are. Not to mention it's 13y old!

Here is third. Mind it's on Japanese and it needs translation. These three are ONLY Haskell win32 createWindow files I could find on the web!

No comments, no explanation, nothing :(

%
% (c) sof, 1999
%

Haskell version of "Hello, World" using the Win32 library.
Demonstrates how the Win32 library can be put to use.

Works with Hugs and GHC. To compile it up using the latter,
do: "ghc -o main hello.lhs -syslib win32 -fglasgow-exts"

For GHC 5.03:

  ghc -package win32 hello.lhs -o hello.exe -optl "-Wl,--subsystem,windows"

\begin{code}
module Main(main) where

import qualified Graphics.Win32
import qualified System.Win32.DLL
import qualified System.Win32.Types
import Control.Exception (bracket)
import Foreign
import System.Exit
{-import Addr-}
\end{code}

Toplevel main just creates a window and pumps messages.
The window procedure (wndProc) we pass in is partially
applied with the user action that takes care of responding
to repaint messages (WM_PAINT).

\begin{code}
main :: IO ()
main =
  Graphics.Win32.allocaPAINTSTRUCT $ \ lpps -> do
  hwnd <- createWindow 200 200 (wndProc lpps onPaint)
  messagePump hwnd

{-
 OnPaint handler for a window - draw a string centred
 inside it.
-}
onPaint :: Graphics.Win32.RECT -> Graphics.Win32.HDC -> IO ()
onPaint (_,_,w,h) hdc = do
   Graphics.Win32.setBkMode hdc Graphics.Win32.tRANSPARENT
   Graphics.Win32.setTextColor hdc (Graphics.Win32.rgb 255 255 0)
   let y | h==10     = 0
         | otherwise = ((h-10) `div` 2)
       x | w==50     = 0
         | otherwise = (w-50) `div` 2
   Graphics.Win32.textOut hdc x y "Hello, world"
   return ()
\end{code}

Simple window procedure - one way to improve and generalise
it would be to pass it a message map (represented as a
finite map from WindowMessages to actions, perhaps).

\begin{code}

wndProc :: Graphics.Win32.LPPAINTSTRUCT
    -> (Graphics.Win32.RECT -> Graphics.Win32.HDC -> IO ()) -- on paint action
        -> Graphics.Win32.HWND
        -> Graphics.Win32.WindowMessage
    -> Graphics.Win32.WPARAM
    -> Graphics.Win32.LPARAM
    -> IO Graphics.Win32.LRESULT
wndProc lpps onPaint hwnd wmsg wParam lParam
 | wmsg == Graphics.Win32.wM_DESTROY = do
     Graphics.Win32.sendMessage hwnd Graphics.Win32.wM_QUIT 1 0
     return 0
 | wmsg == Graphics.Win32.wM_PAINT && hwnd /= nullPtr = do
     r <- Graphics.Win32.getClientRect hwnd
     paintWith lpps hwnd (onPaint r)
     return 0
 | otherwise =
     Graphics.Win32.defWindowProc (Just hwnd) wmsg wParam lParam

createWindow :: Int -> Int -> Graphics.Win32.WindowClosure -> IO Graphics.Win32.HWND
createWindow width height wndProc = do
  let winClass = Graphics.Win32.mkClassName "Hello"
  icon         <- Graphics.Win32.loadIcon   Nothing Graphics.Win32.iDI_APPLICATION
  cursor       <- Graphics.Win32.loadCursor Nothing Graphics.Win32.iDC_ARROW
  bgBrush      <- Graphics.Win32.createSolidBrush (Graphics.Win32.rgb 0 0 255)
  mainInstance <- System.Win32.DLL.getModuleHandle Nothing
  Graphics.Win32.registerClass
      ( Graphics.Win32.cS_VREDRAW + Graphics.Win32.cS_HREDRAW
      , mainInstance
      , Just icon
      , Just cursor
      , Just bgBrush
      , Nothing
      , winClass
      )
  w <- Graphics.Win32.createWindow
         winClass
         "Hello, World example"
         Graphics.Win32.wS_OVERLAPPEDWINDOW
         Nothing Nothing -- leave it to the shell to decide the position
                 -- at where to put the window initially
                 (Just width)
         (Just height)
         Nothing      -- no parent, i.e, root window is the parent.
         Nothing      -- no menu handle
         mainInstance
         wndProc
  Graphics.Win32.showWindow w Graphics.Win32.sW_SHOWNORMAL
  Graphics.Win32.updateWindow w
  return w

messagePump :: Graphics.Win32.HWND -> IO ()
messagePump hwnd = Graphics.Win32.allocaMessage $ \ msg ->
  let pump = do
        Graphics.Win32.getMessage msg (Just hwnd)
        `catch` \ _ -> exitWith ExitSuccess
    Graphics.Win32.translateMessage msg
    Graphics.Win32.dispatchMessage msg
    pump
  in pump

paintWith :: Graphics.Win32.LPPAINTSTRUCT -> Graphics.Win32.HWND -> (Graphics.Win32.HDC -> IO a) -> IO a
paintWith lpps hwnd p =
  bracket
    (Graphics.Win32.beginPaint hwnd lpps)
    (const $ Graphics.Win32.endPaint hwnd lpps)
    p

\end{code}
CoR
  • 3,826
  • 5
  • 35
  • 42
  • 3
    If Windows GUI's are your primary motivation maybe Clean would be a better choice (or even F-sharp if you don't want laziness). Clean is a very similar language to Haskell that was commercially developed for a number of years so had an excellent GUI binding. The GUI is not so actively developed these days as the Clean developers are working on GUIs via JavaScript and Applets, but it should still be more powerful and mature than the Haskell Win32 binding. There are substantial tutorials on line for both Clean the language and "Object IO" the Clean GUI. – stephen tetley May 29 '12 at 21:24
  • 1
    @stephen tetley: Thanks for answer. Thank you for allowing me not to know something :) Now, my goal is to learn Haskell. After a while I was bored with console apps and got an idea to port my "Quotes JS" to Haskell win32. I thought it would be quite easy. BIG MISTAKE! There's almost nothing on how to use Haskell like pure win32 GUI. Those 3 links are only code I could find on the web. That's plane wrong. If Haskell is really as good as people are saying there has to be a way to create a window and deal with win_msg. First solution is testing wmsg for events. Like people did on Windows 95 – CoR May 30 '12 at 02:43
  • I can do that if I had to. But I was hoping there's a better solution. Something like eventListeners or better. Idea was not to use a framework, but to slowly build one SIMPLE enough to understand it and learn in the process. I know my question is FAR from simple... But I had to start somewhere. Will take a look at Clean. If it is similar to Haskell maybe those solutions could be used in Haskell too :) If I had enough knowledge I would just read wxWidgets code and 'learn' from there. But I am not. I need human explanation and examples. Goal is to understand "how thing works" not just use it. – CoR May 30 '12 at 02:58