2012-08-15 17 views
12

Czy możliwe jest przechwycenie ekranu (lub okna) za pomocą Haskella w środowisku Windows? (tzn. robienie zrzutu co kilka minut). Jeśli tak, to w jaki sposób można to zrobić (ponownie w Haskell, w środowisku Windows)?Zrzut ekranu w Haskell?

Więcej informacji: Jestem początkujący dla Haskella. Przyjaciel chce obniżyć koszty rozwoju, organizując mi programy dla jego biura rachunkowego, ale nalega, żebym używał Haskella. On chce narzędzia, które pozwoli mu monitorować komputery różnych stacji roboczych Windows XP. Prawdopodobnie byłaby to aplikacja typu klient/serwer. Musi jedynie monitorować aktywność na komputerze, dlatego nie chce żadnego z drogich programów do zarządzania, które są już dostępne na rynku. Przeszedłem przez wiele dokumentacji i udało mi się tylko znaleźć wxHaskell, ale nie mogłem znaleźć zbyt wiele na przechwytywaniu ekranu, szczególnie w środowisku Windows.

+0

Czy rozważałeś właśnie wywołanie zewnętrznego programu z Haskell, który wykonuje zrzut ekranu? – Probie

+0

@Probie Rzeczywiście, rozważałem właśnie zrobienie klienta i serwera w Haskell i wysyłanie zrzutów ekranu w różnych odstępach czasu. ALE, wydaje się to tak sprzeczne z intuicją. Po co zawracać sobie głowy używaniem Haskella, jeśli tylko tak mała część oprogramowania to rzeczywiście Haskell. Niestety, jest to coś, o czym powinna porozmawiać społeczność Haskell. Jeśli chcemy, aby Haskell był bardziej widoczny, z całą pewnością będziemy musieli rozwiązać tego typu problemy. –

Odpowiedz

17

Podane podejście Tichon jest poprawne. Wystarczy dodać trochę kodu do odpowiedzi dał powyżej

import Graphics.Win32.Window 
import Graphics.Win32.GDI.Bitmap 
import Graphics.Win32.GDI.HDC 
import Graphics.Win32.GDI.Graphics2D 

main = do desktop <- getDesktopWindow -- Grab the Hwnd of the desktop, GetDC 0, GetDC NULL etc all work too 
      hdc  <- getWindowDC (Just desktop) -- Get the dc handle of the desktop 
      (x,y,r,b) <- getWindowRect desktop -- Find the size of the desktop so we can know which size the destination bitmap should be 
              -- (left, top, right, bottom) 
      newDC  <- createCompatibleDC (Just hdc) -- Create a new DC to hold the copied image. It should be compatible with the source DC 
      let width = r - x -- Calculate the width 
      let height = b - y -- Calculate the Height 
      newBmp <- createCompatibleBitmap hdc width height -- Create a new Bitmap which is compatible with the newly created DC 
      selBmp <- selectBitmap newDC newBmp -- Select the Bitmap into the DC, drawing on the DC now draws on the bitmap as well 
      bitBlt newDC 0 0 width height hdc 0 0 sRCCOPY -- use SRCCOPY to copy the desktop DC into the newDC 
      createBMPFile "Foo.bmp" newBmp newDC -- Write out the new Bitmap file to Foo.bmp 
      putStrLn "Bitmap image copied" -- Some debug message 
      deleteBitmap selBmp -- Cleanup the selected bitmap 
      deleteBitmap newBmp -- Cleanup the new bitmap 
      deleteDC newDC  -- Cleanup the DC we created. 

To właśnie szybko ułożyła, ale zapisuje zrzut ekranu do pliku o nazwie Foo.bmp. Ps. Wszystkim, którzy napisali bibliotekę Win32, ładnie wykonano :)

+0

Wygląda dobrze! To na pewno pomoże mi w moich przygodach z Haskellem. Dziękuję Ci! –

11

Powinieneś być w stanie to zrobić za pomocą Win32 API. Opierając się na What is the best way to take screenshots of a Window with C++ in Windows?, musisz uzyskać kontekst okna, a następnie skopiować obraz z niego, używając odpowiednio GetWindowDC i BitBlt.

Przeglądając dokumentację API Win32 Haskell, dostępna jest funkcja getWindowDC w Graphics.Win32.Window. Zwraca to IO HDC. Istnieje funkcja bitblt w Graphics.Win32.GDI.Graphics2D. Ta funkcja pobiera HDC wraz z grupą INT s, co prawdopodobnie odpowiada argumentom, które przyjmuje w C++.

Niestety, nie mam pod ręką komputera z systemem Windows, więc nie mogę zapisać rzeczywistego kodu. Musisz sam wymyślić, jak korzystać z funkcji Win32 API, co może być nieco kłopotliwe.

Gdy to zrobisz, byłoby wspaniale, gdybyś umieścił je w bibliotece i umieścił w Hackage - Windows zazwyczaj nie cieszy się dużą popularnością w kraju Haskell (jak sam pokazuję: P), więc jestem pewni inni programiści Windows byliby wdzięczni za łatwy sposób robienia zrzutów ekranu.

12

Możesz to zrobić na wiele platform dzięki GTK.

To nie będzie dużo różnić się od tego z C: Taking a screenshot with C/GTK.

{-# LANGUAGE OverloadedStrings #-} 

import Graphics.UI.Gtk 
import System.Environment 
import Data.Text as T 

main :: IO() 
main = do 
    [fileName] <- getArgs 
    _ <- initGUI 
    Just screen <- screenGetDefault 
    window <- screenGetRootWindow screen 
    size <- drawableGetSize window 
    origin <- drawWindowGetOrigin window 
    Just pxbuf <- 
     pixbufGetFromDrawable 
      window 
      ((uncurry . uncurry Rectangle) origin size) 
    pixbufSave pxbuf fileName "png" ([] :: [(T.Text, T.Text)]) 
+0

To podejście wygląda bardzo czysto i prosto. Na pewno będę o tym pamiętać. Chociaż byłoby to bardzo podobne, chciałbym zobaczyć kilka przykładów w Haskell, ponieważ jestem wciąż początkującym. –

+0

@ M.Ferguson, proszę bardzo.Okazało się, że było nieco inaczej: nie znalazłem odpowiednika 'gdk_get_default_root_window', więc potrzebne było dodatkowe wywołanie' screenGetDefault'. – Rotsor

+0

@Rotsor 'gdk_get_default_root_window' jest zawijany z' Graphics.UI.Gtk.Gdk.DrawWindow.drawWindowGetDefaultRootWindow', nie jest to najłatwiejsza rzecz, ale –