1{%MainUnit fpguiint.pp}
2{
3 *****************************************************************************
4  This file is part of the Lazarus Component Library (LCL)
5
6  See the file COPYING.modifiedLGPL.txt, included in this distribution,
7  for details about the license.
8 *****************************************************************************
9}
10//---------------------------------------------------------------
11
12type
13
14    { TFPGUITimer }
15
16    TFPGUITimer = class
17  private
18    //FLCLTimer: TTimer;
19    FTimer: TfpgTimer;
20    FCallback: TWSTimerProc;
21  protected
22    procedure FPGTimer(Sender: TObject);
23  public
24    constructor Create(AInterval: Integer; ACallbackFunc: TWSTimerProc);
25    destructor  Destroy; override;
26
27    property Timer : TfpgTimer read FTimer;
28  end;
29
30{ TFPGUITimer }
31
32procedure TFPGUITimer.FPGTimer(Sender: TObject);
33begin
34  if Assigned(FCallback) then
35    FCallback;
36end;
37
38constructor TFPGUITimer.Create(AInterval: Integer; ACallbackFunc: TWSTimerProc);
39begin
40  FTimer := TfpgTimer.Create(AInterval);
41  FTimer.OnTimer:=@FPGTimer;
42  FCallback := ACallbackFunc;
43  FTimer.Enabled:= True;
44end;
45
46destructor TFPGUITimer.Destroy;
47begin
48  FTimer.Free;
49  inherited Destroy;
50end;
51
52
53{------------------------------------------------------------------------------
54  Method: TFpGuiWidgetSet.Create
55  Params:  None
56  Returns: Nothing
57
58  Constructor for the class.
59 ------------------------------------------------------------------------------}
60constructor TFpGuiWidgetSet.Create;
61begin
62  inherited Create;
63
64  FpGuiWidgetSet := Self;
65end;
66
67{------------------------------------------------------------------------------
68  Method: TFpGuiWidgetSet.Destroy
69  Params:  None
70  Returns: Nothing
71
72  Destructor for the class.
73 ------------------------------------------------------------------------------}
74destructor TFpGuiWidgetSet.Destroy;
75begin
76  FpGuiWidgetSet := nil;
77
78  inherited Destroy;
79end;
80
81function TFpGuiWidgetSet.DrawFrameControl(DC: HDC; const Rect: TRect; uType,
82  uState: Cardinal): Boolean;
83var
84  ADC: TFPGUIDeviceContext;
85  ControlType: Cardinal;
86  ControlStyle: Cardinal;
87  fpgRect: TfpgRect;
88  Style: TfpgButtonFlags;
89  (*
90  DFC_CAPTION = $01;
91  DFC_MENU = $02;
92  DFC_SCROLL = $03;
93  DFC_BUTTON =  $04;
94  DFCS_BUTTONCHECK = 0;
95  DFCS_BUTTONRADIOIMAGE = 1;
96  DFCS_BUTTONRADIOMASK = 2;
97  DFCS_BUTTONRADIO = 4;
98  DFCS_BUTTON3STATE = 8;
99  DFCS_BUTTONPUSH = 16;
100  *)
101const
102  DFCS_ALLSTATES=DFCS_BUTTONCHECK or DFCS_BUTTONRADIOIMAGE or DFCS_BUTTONRADIOMASK
103                or DFCS_BUTTONRADIO or DFCS_BUTTON3STATE or DFCS_BUTTONPUSH;
104begin
105  Result:=false;
106  ADC:=TFPGUIDeviceContext(DC);
107  if Assigned(ADC.fpgCanvas) then begin
108    ControlType:=uType;
109    ControlStyle:=uState and DFCS_ALLSTATES;
110    fpgRect:=ADC.PrepareRectOffsets(Rect);
111    Case ControlType of
112      DFC_BUTTON:
113        begin
114          if (ControlStyle and DFCS_BUTTONPUSH)=DFCS_BUTTONPUSH then begin
115            Style:=[];
116            if (uState and DFCS_INACTIVE) <> 0 then
117              Style:=Style+[btfIsEmbedded] //Disabled ?
118            else
119            if (uState and DFCS_PUSHED) <> 0 then
120              Style:=Style+[btfIsPressed]
121            else
122            if (uState and DFCS_HOT) <> 0 then
123              Style:=Style+[btfHover];
124            ADC.fpgCanvas.DrawButtonFace(fpgRect,Style);
125            Result:=true;
126          end;
127        end;
128      else
129        Result:=false;
130    end;
131  end;
132end;
133
134{------------------------------------------------------------------------------
135  Method: TFpGuiWidgetSet.CreateTimer
136  Params:  None
137  Returns: Nothing
138
139  Creates a new timer and sets the callback event.
140 ------------------------------------------------------------------------------}
141function TFpGuiWidgetSet.CreateTimer(Interval: integer; TimerFunc: TWSTimerProc): THandle;
142var
143  Timer: TFPGUITimer;
144begin
145  Timer := TFPGUITimer.Create(Interval, TimerFunc);
146
147  Result := PtrInt(Timer);
148end;
149
150{------------------------------------------------------------------------------
151  Method: TFpGuiWidgetSet.DestroyTimer
152  Params:  None
153  Returns: Nothing
154
155  Destroys a timer.
156 ------------------------------------------------------------------------------}
157function TFpGuiWidgetSet.DestroyTimer(TimerHandle: THandle): boolean;
158var
159  Timer: TFPGUITimer absolute TimerHandle;
160begin
161  if Timer <> nil then
162    Timer.Free;
163
164  Result := True;
165end;
166
167function TFpGuiWidgetSet.CreateThemeServices: TThemeServices;
168begin
169  Result:=TFPGUIThemeServices.Create;
170end;
171
172{------------------------------------------------------------------------------
173  Method: TFpGuiWidgetSet.AppInit
174  Params:  None
175  Returns: Nothing
176
177  Initializes the application
178 ------------------------------------------------------------------------------}
179procedure TFpGuiWidgetSet.AppInit(var ScreenInfo: TScreenInfo);
180//var
181//  Display: String;
182begin
183  // This doesn't hurt. on other playforms than X it just will do nothing
184//  Display := GetEnvironmentVariableUTF8('DISPLAY');
185  fpgApplication.Initialize;
186  //GFApplication.QuitWhenLastWindowCloses := False;
187
188  //if fpgStyleManager.SetStyle('win8') then begin
189  //  fpgStyle:=fpgStyleManager.Style;
190  //end;
191
192end;
193
194{------------------------------------------------------------------------------
195  Method: TFpGuiWidgetSet.AppRun
196  Params:  None
197  Returns: Nothing
198
199  Enter the main message loop
200 ------------------------------------------------------------------------------}
201procedure TFpGuiWidgetSet.AppRun(const ALoop: TApplicationMainLoop);
202var
203  vMainForm: TfpgForm;
204begin
205  { Shows the main form }
206  if Assigned(Application.MainForm) then
207  begin
208    vMainForm := TFPGUIPrivateWindow(Application.MainForm.Handle).Form;
209    if Application.MainForm.Visible then
210      vMainForm.Show;
211  end;
212  // GFApplication.EventFilter can maybe be used on X11 for aloop but it is X only
213//  fpgApplication.Run;
214  if Assigned(ALoop) then begin
215    ALoop
216  end else begin
217    repeat
218      try
219        fpgApplication.ProcessMessages;
220        if not fpgApplication.Terminated then Application.Idle(true);
221      except
222        Application.HandleException(Self);
223      end;
224    until fpgApplication.Terminated;
225  end;
226
227end;
228
229{------------------------------------------------------------------------------
230  Method: TFpGuiWidgetSet.AppWaitMessage
231  Params:  None
232  Returns: Nothing
233
234  Wait till an OS application message is received
235 ------------------------------------------------------------------------------}
236procedure TFpGuiWidgetSet.AppWaitMessage;
237begin
238  fpgWaitWindowMessage;
239end;
240
241{------------------------------------------------------------------------------
242  Method: TFpGuiWidgetSet.AppProcessMessage
243  Params:  None
244  Returns: Nothing
245
246  Handle the messages in the queue
247 ------------------------------------------------------------------------------}
248procedure TFpGuiWidgetSet.AppProcessMessages;
249begin
250  fpgApplication.ProcessMessages;
251end;
252
253{------------------------------------------------------------------------------
254  Method: TFpGuiWidgetSet.AppTerminate
255  Params:  None
256  Returns: Nothing
257
258  Implements Application.Terminate and MainForm.Close.
259 ------------------------------------------------------------------------------}
260procedure TFpGuiWidgetSet.AppTerminate;
261begin
262  fpgApplication.Terminated := True;
263end;
264
265{------------------------------------------------------------------------------
266  Method: TFpGuiWidgetSet.AppMinimize
267  Params:  None
268  Returns: Nothing
269
270  Minimizes the application window.
271 ------------------------------------------------------------------------------}
272procedure TFpGuiWidgetSet.AppMinimize;
273begin
274end;
275
276procedure TFpGuiWidgetSet.AppRestore;
277begin
278
279end;
280
281{------------------------------------------------------------------------------
282  Method: TFpGuiWidgetSet.AppBringToFront
283  Params:  None
284  Returns: Nothing
285
286  Brings the application window to the front
287 ------------------------------------------------------------------------------}
288procedure TFpGuiWidgetSet.AppBringToFront;
289begin
290
291end;
292
293function TFpGuiWidgetSet.LCLPlatform: TLCLPlatform;
294begin
295  Result:= lpfpGUI;
296end;
297
298function TFpGuiWidgetSet.DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor;
299begin
300  Result:=clNone;
301end;
302
303procedure TFpGuiWidgetSet.DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor);
304var
305  DC: TFPGUIDeviceContext;
306  p: TPoint;
307begin
308  DC:=TFPGUIDeviceContext(CanvasHandle);
309  p:=Point(X,Y);
310  p:=DC.PreparePointOffsets(p);
311  DC.FPrivateWidget.Widget.Canvas.Pixels[p.x,p.y]:=TColorToTfpgColor(AColor);
312end;
313
314procedure TFpGuiWidgetSet.DCRedraw(CanvasHandle: HDC);
315begin
316
317end;
318
319procedure TFpGuiWidgetSet.SetDesigning(AComponent: TComponent);
320begin
321//  Include(AComponent.ComponentState, csDesigning);
322end;
323
324function TFpGuiWidgetSet.RawImage_CreateBitmaps(const ARawImage: TRawImage; out
325  ABitmap, AMask: HBitmap; ASkipMask: Boolean): Boolean;
326var
327  OutBitmap: TFPGUIWinAPIBitmap;
328  fpgBitmap: TfpgImage;
329  ImgData: Pointer absolute ARawImage.Data;
330  ImgMask: Pointer absolute ARawImage.Mask;
331  ImgWidth: Cardinal absolute ARawImage.Description.Width;
332  ImgHeight: Cardinal absolute ARawImage.Description.Height;
333  ImgDepth: Byte absolute ARawImage.Description.Depth;
334  ImgDataSize: PtrUInt absolute ARawImage.DataSize;
335  function min(const a,b: SizeInt): SizeInt;
336  begin
337    if a>b then Result:=b else Result:=a;
338  end;
339begin
340  ABitmap:=0;
341  AMask:=0;
342  Result:=false;
343  OutBitmap:=TFPGUIWinAPIBitmap.Create(ARawImage.Description.BitsPerPixel,ARawImage.Description.Width,ARawImage.Description.Height);
344  fpgBitmap:=OutBitmap.Image;
345  ABitmap:=HBITMAP(OutBitmap);
346  move(ARawImage.Data^,pbyte(fpgBitmap.ImageData)^,min(ARawImage.DataSize,fpgBitmap.ImageDataSize));
347  fpgBitmap.UpdateImage;
348  Result:=true;
349end;
350
351procedure FillRawImageDescriptionColors(var ADesc: TRawImageDescription);
352begin
353  case ADesc.BitsPerPixel of
354    1,4,8:
355      begin
356        // palette mode, no offsets
357        ADesc.Format := ricfGray;
358        ADesc.RedPrec := ADesc.BitsPerPixel;
359        ADesc.GreenPrec := 0;
360        ADesc.BluePrec := 0;
361        ADesc.RedShift := 0;
362        ADesc.GreenShift := 0;
363        ADesc.BlueShift := 0;
364      end;
365    16:
366      begin
367        // 5-5-5 mode
368        ADesc.RedPrec := 5;
369        ADesc.GreenPrec := 5;
370        ADesc.BluePrec := 5;
371        ADesc.RedShift := 10;
372        ADesc.GreenShift := 5;
373        ADesc.BlueShift := 0;
374        ADesc.Depth := 15;
375      end;
376    24:
377      begin
378        // 8-8-8 mode
379        ADesc.RedPrec := 8;
380        ADesc.GreenPrec := 8;
381        ADesc.BluePrec := 8;
382        ADesc.RedShift := 16;
383        ADesc.GreenShift := 8;
384        ADesc.BlueShift := 0;
385      end;
386  else    //  32:
387    // 8-8-8-8 mode, high byte can be native alpha or custom 1bit maskalpha
388    ADesc.AlphaPrec := 8;
389    ADesc.RedPrec := 8;
390    ADesc.GreenPrec := 8;
391    ADesc.BluePrec := 8;
392    ADesc.AlphaShift := 24;
393    ADesc.RedShift := 16;
394    ADesc.GreenShift := 8;
395    ADesc.BlueShift := 0;
396    ADesc.Depth := 32;
397  end;
398end;
399
400procedure FillRawImageDescription(const ABitmapInfo: TfpgImage; out ADesc: TRawImageDescription);
401begin
402  ADesc.Init;
403  ADesc.Format := ricfRGBA;
404  ADesc.Depth := 32;             // used bits per pixel
405  ADesc.Width := ABitmapInfo.Width;
406  ADesc.Height := ABitmapInfo.Height;
407  ADesc.BitOrder := riboReversedBits;
408  ADesc.ByteOrder := riboLSBFirst;
409  ADesc.LineOrder := riloTopToBottom;
410  ADesc.BitsPerPixel := 32;      // bits per pixel. can be greater than Depth.
411  ADesc.LineEnd := rileDWordBoundary;
412
413  if ADesc.BitsPerPixel <= 8
414  then begin
415    // each pixel is an index in the palette
416    // TODO, ColorCount
417    ADesc.PaletteColorCount := 0;
418  end
419  else ADesc.PaletteColorCount := 0;
420
421  FillRawImageDescriptionColors(ADesc);
422
423  ADesc.MaskBitsPerPixel := 8;
424  ADesc.MaskShift := 0;
425  ADesc.MaskLineEnd := rileWordBoundary; // CreateBitmap requires word boundary
426  ADesc.MaskBitOrder := riboReversedBits;
427end;
428
429function TFpGuiWidgetSet.RawImage_DescriptionFromBitmap(ABitmap: HBITMAP; out ADesc: TRawImageDescription): Boolean;
430var
431  o: TFPGUIWinAPIBitmap;
432begin
433  o:=TFPGUIWinAPIBitmap(ABitmap);
434  FillRawImageDescription(o.Image,ADesc);
435  Result:=true;
436end;
437
438function TFpGuiWidgetSet.RawImage_DescriptionFromDevice(ADC: HDC; out ADesc: TRawImageDescription): Boolean;
439var
440  DC: TFPGUIDeviceContext;
441  r: TfpgRect;
442begin
443  DC:=TFPGUIDeviceContext(ADC);
444  ADesc.Init;
445  with ADesc do begin
446    Format:=      ricfRGBA;
447    if Assigned(DC) and Assigned(DC.fpgCanvas) then begin
448      dc.fpgCanvas.GetWinRect(r);
449      Width:=     r.Width;
450      Height:=    r.Height;
451    end else begin
452      Width:=     0;
453      Height:=    0;
454    end;
455    Depth:=       32; // used bits per pixel
456    BitOrder:=    riboBitsInOrder;
457    ByteOrder:=   riboMSBFirst;
458    LineOrder:=   riloTopToBottom;
459    LineEnd:=     rileByteBoundary;
460    BitsPerPixel:=32; // bits per pixel. can be greater than Depth.
461    RedPrec:=     8;      // red or gray precision. bits for red
462    RedShift:=    8;     // bitshift. Direction: from least to most significant
463    GreenPrec:=   8;
464    GreenShift:=  16;
465    BluePrec:=    8;
466    BlueShift:=   24;
467    AlphaPrec:=   8;
468    AlphaShift:=  0;
469    // Test
470
471    // The next values are only valid, if there is a mask (MaskBitsPerPixel > 0)
472    // Masks are always separate with a depth of 1 bpp. One pixel can occupy
473    // one byte at most
474    // a value of 1 means that pixel is masked
475    // a value of 0 means the pixel value is shown
476    MaskBitsPerPixel:= 8;
477    MaskShift:=        0;
478    MaskLineEnd:=      rileWordBoundary;
479    MaskBitOrder:=     riboReversedBits;
480  end;
481  Result:=true;
482end;
483
484procedure TFpGuiWidgetSet.InitializeCriticalSection(
485  var CritSection: TCriticalSection);
486var
487  ACritSec: System.PRTLCriticalSection;
488begin
489  New(ACritSec);
490  System.InitCriticalSection(ACritSec^);
491  CritSection:=TCriticalSection(ACritSec);
492end;
493
494procedure TFpGuiWidgetSet.DeleteCriticalSection(
495  var CritSection: TCriticalSection);
496var
497  ACritSec: System.PRTLCriticalSection;
498begin
499  ACritSec:=System.PRTLCriticalSection(CritSection);
500  System.DoneCriticalsection(ACritSec^);
501  Dispose(ACritSec);
502  CritSection:=0;
503end;
504
505procedure TFpGuiWidgetSet.EnterCriticalSection(var CritSection: TCriticalSection);
506var
507  ACritSec: System.PRTLCriticalSection;
508begin
509  ACritSec:=System.PRTLCriticalSection(CritSection);
510  System.EnterCriticalsection(ACritSec^);
511end;
512
513procedure TFpGuiWidgetSet.LeaveCriticalSection(var CritSection: TCriticalSection);
514var
515  ACritSec: System.PRTLCriticalSection;
516begin
517  ACritSec:=System.PRTLCriticalSection(CritSection);
518  System.LeaveCriticalsection(ACritSec^);
519end;
520
521{------------------------------------------------------------------------------
522  Function: TFpGuiWidgetSet.IsValidDC
523  Params:   DC     -  handle to a device context (TFpGuiDeviceContext)
524  Returns:  True   -  if the DC is valid
525 ------------------------------------------------------------------------------}
526function TFpGuiWidgetSet.IsValidDC(const DC: HDC): Boolean;
527begin
528  Result := (DC <> 0);
529end;
530
531{------------------------------------------------------------------------------
532  Function: TFpGuiWidgetSet.IsValidGDIObject
533  Params:   GDIObject  -  handle to a GDI Object (TFpGuiFont, TFpGuiBrush, etc)
534  Returns:  True       -  if the DC is valid
535
536  Remark: All handles for GDI objects must be pascal objects so we can
537 distinguish between them
538 ------------------------------------------------------------------------------}
539function TFpGuiWidgetSet.IsValidGDIObject(const GDIObject: HGDIOBJ): Boolean;
540var
541  aObject: TObject;
542begin
543  Result := False;
544
545  if GDIObject = 0 then Exit;
546
547  aObject := TObject(GDIObject);
548
549  try
550    if aObject is TObject then
551    begin
552      Result:= (aObject is TFPGUIWinAPIObject);
553    end;
554  except
555    //Eat exceptions. If Exception happends it is not a TObject after all and
556    //of course it is not a fpgui GDI object.
557  end;
558end;
559
560{ Unable to make it work properly
561function TFpGuiWidgetSet.CreateRubberBand(const ARect: TRect;
562  const ABrush: HBrush): HWND;
563var
564  FakeParams: TCreateParams;
565  fpgForm: TfpgForm;
566begin
567  FillByte(FakeParams,sizeof(FakeParams),0);
568  FakeParams.Style:=FakeParams.Style or WS_VISIBLE;
569  FakeParams.X:=ARect.Left;
570  FakeParams.Y:=ARect.Top;
571  FakeParams.Width:=aRect.Width;
572  FakeParams.Height:=aRect.Height;
573  Result:=HWND(TFPGUIPrivateWindow.Create(nil,FakeParams));
574  fpgForm:=TFPGUIPrivateWindow(Result).Form;
575  TFPGUIPrivateWindow(Result).SetFormBorderStyle(TFormBorderStyle.bsNone);
576  fpgForm.Show;
577end;
578
579procedure TFpGuiWidgetSet.DestroyRubberBand(ARubberBand: HWND);
580begin
581  TFPGUIPrivateWindow(ARubberBand).Free;
582end;
583}
584
585function TFpGuiWidgetSet.RawImage_FromBitmap(out ARawImage: TRawImage; ABitmap,
586  AMask: HBITMAP; ARect: PRect): Boolean;
587var
588  img: TFPGUIWinAPIBitmap;
589begin
590  ARawImage.Init;
591  img:=TFPGUIWinAPIBitmap(ABitmap);
592  FillRawImageDescription(img.Image, ARawImage.Description);
593  ARawImage.DataSize:=ARawImage.Description.Width*ARawImage.Description.Height*4;
594  ARawImage.Data:=GetMem(ARawImage.DataSize);
595  move(img.Image.ImageData^,ARawImage.Data^,img.Image.ImageDataSize);
596end;
597
598//------------------------------------------------------------------------
599