! HelloWin2.f90
! Public domain 2007-2010 James Van Buskirk
! Compiled with:
! gfortran -Wall -mwindows HelloWin2.f90 -oHelloWin2 -lgdi32

module win32_types
   use ISO_C_BINDING
   implicit none
   private

   public WNDCLASSEX_T
   type, bind(C) :: WNDCLASSEX_T
      integer(C_INT) cbSize
      integer(C_INT) style
      type(C_FUNPTR) lpfnWndProc
      integer(C_INT) cbClsExtra
      integer(C_INT) cbWndExtra
      integer(C_INTPTR_T) hInstance
      integer(C_INTPTR_T) hIcon
      integer(C_INTPTR_T) hCursor
      integer(C_INTPTR_T) hbrBackground
      type(C_PTR) lpszMenuName
      type(C_PTR) lpszClassName
      integer(C_INTPTR_T) hIconSm
   end type WNDCLASSEX_T

   public POINT_T
   type, bind(C) :: POINT_T
      integer(C_LONG) x
      integer(C_LONG) y
   end type POINT_T

   public MSG_T
   type, bind(C) :: MSG_T
      integer(C_INTPTR_T) hwnd
      integer(C_INT) message
      integer(C_INTPTR_T) wParam
      integer(C_INTPTR_T) lParam
      integer(C_LONG) time
      type(POINT_T) pt
   end type MSG_T

   public RECT_T
   type, bind(C) :: RECT_T
      integer(C_LONG) left
      integer(C_LONG) top
      integer(C_LONG) right
      integer(C_LONG) bottom
   end type RECT_T

   public PAINTSTRUCT_T
   type, bind(C) :: PAINTSTRUCT_T
      integer(C_INTPTR_T) hdc
      integer(C_INT) fErase
      type(RECT_T) rcPaint
      integer(C_INT) fRestore
      integer(C_INT) fIncUpdate
      integer(C_INT8_T) rgbReserved(32)
   end type PAINTSTRUCT_T
end module win32_types

module win32
   use ISO_C_BINDING
   implicit none
   private

   public GetModuleHandle
   interface
      function GetModuleHandle(lpModuleName) &
         bind(C,name='GetModuleHandleA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: GetModuleHandle
         integer(C_INTPTR_T) GetModuleHandle
         character(kind=C_CHAR) lpModuleName(*)
      end function GetModuleHandle
   end interface

   public GetCommandLine
   interface
      function GetCommandLine() &
         bind(C,name='GetCommandLineA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: GetCommandLine
         type(C_PTR) GetCommandLine
      end function GetCommandLine
   end interface

   public DefWindowProc
   interface
      function DefWindowProc(hwnd, Msg, wParam, lParam) &
         bind(C,name='DefWindowProcA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: DefWindowProc
         integer(C_LONG) DefWindowProc
         integer(C_INTPTR_T), value :: hwnd
         integer(C_INT), value :: Msg
         integer(C_INTPTR_T), value :: wParam
         integer(C_INTPTR_T), value :: lParam
      end function DefWindowProc
   end interface

   public LoadIcon
   interface
      function LoadIcon(hInstance, lpIconName) &
         bind(C,name='LoadIconA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: LoadIcon
         integer(C_INTPTR_T) LoadIcon
         integer(C_INTPTR_T), value :: hInstance
         character(kind=C_CHAR) lpIconName(*)
      end function LoadIcon
   end interface

   public LoadCursor
   interface
      function LoadCursor(hInstance, lpCursorName) &
         bind(C,name='LoadCursorA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: LoadCursor
         integer(C_INTPTR_T) LoadCursor
         integer(C_INTPTR_T), value :: hInstance
         character(kind=C_CHAR) lpCursorName(*)
      end function LoadCursor
   end interface

   public GetStockObject
   interface
      function GetStockObject(fnObject) &
         bind(C,name='GetStockObject')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: GetStockObject
         integer(C_INTPTR_T) GetStockObject
         integer(C_INT), value :: fnObject
      end function GetStockObject
   end interface

   integer(C_INT), parameter, public :: WHITE_BRUSH = 0 ! Stock object

   public RegisterClassEx
   interface
      function RegisterClassEx(WndClass) &
         bind(C,name='RegisterClassExA')

         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: RegisterClassEx
         integer(C_SHORT) RegisterClassEx
         type(WNDCLASSEX_T) WndClass
      end function RegisterClassEx
   end interface

! Work around bug in libuser32.a
!   public CreateWindow
!   interface
!      function CreateWindow(lpClassName, lpWindowName, dwStyle, &
!      x, y, nWidth, nHeight, hwndParent, hMenu, hInstance, &
!      lpParam) bind(C,name='CreateWindow')
!
!         use ISO_C_BINDING
!         implicit none
!!GCC$ ATTRIBUTES STDCALL :: CreateWindow
!         integer(C_INTPTR_T) CreateWindow
!         character(kind=C_CHAR) lpClassName(*)
!         character(kind=C_CHAR) lpWindowName(*)
!         integer(C_LONG), value :: dwStyle
!         integer(C_INT), value :: x
!         integer(C_INT), value :: y
!         integer(C_INT), value :: nWidth
!         integer(C_INT), value :: nHeight
!         integer(C_INTPTR_T), value :: hWndParent
!         integer(C_INTPTR_T), value :: hMenu
!         integer(C_INTPTR_T), value :: hInstance
!         type(C_PTR), value :: lpParam
!      end function CreateWindow
!   end interface

   public CreateWindowEx
   interface
      function CreateWindowEx(dwExStyle, lpClassName, &
      lpWindowName, dwStyle, x, y, nWidth, nHeight, &
      hwndParent, hMenu, hInstance, lpParam) &
      bind(C,name='CreateWindowExA')

         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: CreateWindowEx
         integer(C_INTPTR_T) CreateWindowEx
         integer(C_LONG), value :: dwExStyle
         character(kind=C_CHAR) lpClassName(*)
         character(kind=C_CHAR) lpWindowName(*)
         integer(C_LONG), value :: dwStyle
         integer(C_INT), value :: x
         integer(C_INT), value :: y
         integer(C_INT), value :: nWidth
         integer(C_INT), value :: nHeight
         integer(C_INTPTR_T), value :: hWndParent
         integer(C_INTPTR_T), value :: hMenu
         integer(C_INTPTR_T), value :: hInstance
         type(C_PTR), value :: lpParam
      end function CreateWindowEx
   end interface

   public ShowWindow
   interface
      function ShowWindow(hWnd,nCmdShow) bind(C,name='ShowWindow')
         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: ShowWindow
         integer(C_INT) ShowWindow
         integer(C_INTPTR_T), value :: hWnd
         integer(C_INT), value :: nCmdShow
      end function ShowWindow
   end interface

   public UpdateWindow
   interface
      function UpdateWindow(hWnd) bind(C,name='UpdateWindow')
         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: UpdateWindow
         integer(C_INT) UpdateWindow
         integer(C_INTPTR_T), value :: hWnd
      end function UpdateWindow
   end interface

   public GetMessage
   interface
      function GetMessage(lpMsg,hWnd,wMsgFilterMin,wMsgFilterMax) &
         bind(C,name='GetMessageA')

         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: GetMessage
         integer(C_INT) GetMessage
         type(MSG_T) lpMsg
         integer(C_INTPTR_T), value :: hWnd
         integer(C_INT), value :: wMsgFilterMin
         integer(C_INT), value :: wMsgFilterMax
      end function GetMessage
   end interface

   public TranslateMessage
   interface
      function TranslateMessage(lpMsg) bind(C,name='TranslateMessage')
         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: TranslateMessage
         integer(C_INT) TranslateMessage
         type(MSG_T) lpMsg
      end function TranslateMessage
   end interface

   public DispatchMessage
   interface
      function DispatchMessage(lpMsg) bind(C,name='DispatchMessageA')
         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: DispatchMessage
         integer(C_LONG) DispatchMessage
         type(MSG_T) lpMsg
      end function DispatchMessage
   end interface

   public ExitProcess
   interface
      subroutine ExitProcess(uExitCode) bind(C,name='ExitProcess')
         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: ExitProcess

         integer(C_INT), value :: uExitCode
      end subroutine ExitProcess
   end interface

   public BeginPaint
   interface
      function BeginPaint(hwnd,lpPaint) bind(C,name='BeginPaint')
         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: BeginPaint
         integer(C_INTPTR_T) BeginPaint
         integer(C_INTPTR_T), value :: hwnd
         type(PAINTSTRUCT_T) lpPaint
      end function BeginPaint
   end interface

   public GetClientRect
   interface
      function GetClientRect(hwnd,lpRect) bind(C,name='GetClientRect')
         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: GetClientRect
         integer(C_INT) GetClientRect
         integer(C_INTPTR_T), value :: hwnd
         type(RECT_T) lpRect
      end function GetClientRect
   end interface

   public DrawText
   interface
      function DrawText(hdc, lpString, nCount, lpRect, &
         uFormat) bind(C,name='DrawTextA')

         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: DrawText
         integer(C_INT) DrawText
         integer(C_INTPTR_T), value :: hdc
         character(kind=C_CHAR) lpString(*)
         integer(C_INT), value :: nCount
         type(RECT_T) lpRect
         integer(C_INT), value :: uFormat
      end function DrawText
   end interface

   public EndPaint
   interface
      function EndPaint(hwnd,lpPaint) bind(C,name='EndPaint')
         use ISO_C_BINDING
         use win32_types
         implicit none
!GCC$ ATTRIBUTES STDCALL :: EndPaint
         integer(C_INT) EndPaint
         integer(C_INTPTR_T), value :: hwnd
         type(PAINTSTRUCT_T) lpPaint
      end function EndPaint
   end interface

   public PostQuitMessage
   interface
      subroutine PostQuitMessage(nExitCode) bind(C,name='PostQuitMessage')
         use ISO_C_BINDING
         implicit none
!GCC$ ATTRIBUTES STDCALL :: PostQuitMessage

         integer(C_INT), value :: nExitCode
      end subroutine PostQuitMessage
   end interface
end module win32

module procs
   use win32
   use win32_types
   use ISO_C_BINDING
   implicit none
   private
   public WndProc
   contains
      function WndProc(hwnd, iMsg, wParam, lParam) bind(C)
!GCC$ ATTRIBUTES STDCALL :: WndProc
         integer(C_LONG) WndProc
         integer(C_INTPTR_T), value :: hwnd
         integer(C_INT), value :: iMsg
         integer(C_INTPTR_T), value :: wParam
         integer(C_INTPTR_T), value :: lParam
         integer(C_INTPTR_T) hdc
         type(PAINTSTRUCT_T) ps
         type(RECT_T) rect
         integer(C_INT) result4
         character(kind=C_CHAR) message*(80)

         select case(iMsg)
            case(1) ! WM_CREATE
               WndProc = 0
               return
            case(15) ! WM_PAINT
               hdc = BeginPaint(hwnd, ps)
               result4 = GetClientRect(hwnd, rect)
               message = 'Hello, gfortran!'//achar(0)
               result4 = DrawText(hdc, message, -1, rect, 37)
               result4 = EndPaint(hwnd, ps)
               WndProc = 0
               return
            case(2) ! WM_DESTROY
               call PostQuitMessage(0)
               WndProc = 0
               return
         end select

         WndProc = DefWindowProc(hwnd, iMsg, wParam, lParam)
      end function WndProc
end module procs

function WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow) bind(C, name='WinMain')
!program WinMain
   use win32
   use win32_types
   use procs
   use ISO_C_BINDING
   implicit none
!GCC$ ATTRIBUTES STDCALL :: WinMain
   integer(C_INT) WinMain
   integer(C_INTPTR_T), value :: hInstance
   integer(C_INTPTR_T), value :: hPrevInstance
   type(C_PTR), value :: lpCmdLine
   integer(C_INT), value :: nCmdShow
   character(kind=C_CHAR), pointer :: pcNull(:)
!   integer(C_INTPTR_T) hInstance
!   type(C_PTR) szCommandLine
   type(WNDCLASSEX_T) WndClass
   character(kind=C_CHAR), pointer :: cDefault(:)
   character(kind=C_CHAR), target :: szAppName*(80)
   integer(C_SHORT) result2
   integer(C_INTPTR_T) hwnd
   character(kind=C_CHAR), target :: szWindowCaption*(80)
   integer(C_INT) result4
   type(MSG_T) msg
   integer(C_INT) argh4

   nullify(pcNull)
!   hInstance = GetModuleHandle(pcNull)
!   szCommandLine = GetCommandLine()
   call C_F_POINTER(lpCmdLine,cDefault,[0])
!   call C_F_POINTER(szCommandLine,cDefault,[0])
   szAppName = 'HelloWin'//achar(0)
   WndClass%cbSize = size(transfer(Wndclass,[0_C_INT8_T]))
   WndClass%style = 3 ! ior(CS_HREDRAW, CS_VREDRAW)
   WndClass%lpfnWndProc = C_FUNLOC(WndProc)
   WndClass%cbClsExtra = 0
   WndClass%cbWndExtra = 0
   WndClass%hInstance = hInstance
   WndClass%hIcon = LoadIcon(0_C_INTPTR_T, cDefault) ! IDI_APPLICATION
   WndClass%hCursor = LoadCursor(0_C_INTPTR_T, cDefault) ! IDC_ARROW
   WndClass%hbrBackground = GetStockObject(WHITE_BRUSH)
   WndClass%lpszMenuName = C_NULL_PTR
   WndClass%lpszClassName = C_LOC(szAppName(1:1))
   WndClass%hIconSm = LoadIcon(0_C_INTPTR_T, cDefault) ! IDI_APPLICATION

   result2 = RegisterClassEx(WndClass)

   szWindowCaption = 'The Hello Program'//achar(0)
! Workaround for bug
!   hwnd = CreateWindow(szAppName, szWindowCaption, &
!      13565952, -2147483648, -2147483648, -2147483648, &
!      -2147483648, 0_C_INTPTR_T, 0_C_INTPTR_T, hInstance, &
!      C_NULL_PTR)
   argh4 = -2147483647-1
! Workaround for libuser32.a bug
!   hwnd = CreateWindow(szAppName, szWindowCaption, &
!      13565952, argh4, argh4, argh4, argh4, 0_C_INTPTR_T, &
!      0_C_INTPTR_T, hInstance, C_NULL_PTR)
   hwnd = CreateWindowEx(0, szAppName, szWindowCaption, &
      13565952, argh4, argh4, argh4, argh4, 0_C_INTPTR_T, &
      0_C_INTPTR_T, hInstance, C_NULL_PTR)

   result4 = ShowWindow(hwnd, 10) ! SW_SHOWDEFAULT
   result4 = UpdateWindow(hwnd)

   do while(GetMessage(msg, 0_C_INTPTR_T, 0, 0) /= 0)
      result4 = TranslateMessage(msg)
      result4 = DispatchMessage(msg)
   end do

   call ExitProcess(int(msg%wParam, C_INT))
   WinMain = 0
end function WinMain
!end program WinMain
