#include <config.h>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Bitmap.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Image_Surface.H>
#include <FL/Fl_Overlay_Window.H>
#include <FL/platform.H>
#include "Fl_WinAPI_Window_Driver.H"
#include "Fl_WinAPI_Screen_Driver.H"
#include "../GDI/Fl_GDI_Graphics_Driver.H"
#include <windows.h>
#include <ole2.h>
#include <math.h>
#if USE_COLORMAP
extern HPALETTE fl_select_palette(void); #endif
Fl_WinAPI_Window_Driver::Fl_WinAPI_Window_Driver(Fl_Window *win)
: Fl_Window_Driver(win)
{
icon_ = new icon_data;
shape_data_ = NULL;
memset(icon_, 0, sizeof(icon_data));
cursor = NULL;
screen_num_ = -1;
}
Fl_WinAPI_Window_Driver::~Fl_WinAPI_Window_Driver()
{
if (shape_data_) {
delete shape_data_->effective_bitmap_;
delete shape_data_;
}
delete icon_;
}
RECT Fl_WinAPI_Window_Driver::border_width_title_bar_height(
int &bx, int &by, int &bt )
{
Fl_Window *win = pWindow;
RECT r = {0,0,0,0};
bx = by = bt = 0;
if (win->shown() && !win->parent() && win->border() && win->visible()) {
static HMODULE dwmapi_dll = LoadLibrary("dwmapi.dll");
typedef HRESULT (WINAPI* DwmGetWindowAttribute_type)(HWND hwnd, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);
static DwmGetWindowAttribute_type DwmGetWindowAttribute = dwmapi_dll ?
(DwmGetWindowAttribute_type)GetProcAddress(dwmapi_dll, "DwmGetWindowAttribute") : NULL;
int need_r = 1;
if (DwmGetWindowAttribute) {
const DWORD DWMWA_EXTENDED_FRAME_BOUNDS = 9;
if ( DwmGetWindowAttribute(fl_xid(win), DWMWA_EXTENDED_FRAME_BOUNDS, &r, sizeof(RECT)) == S_OK ) {
need_r = 0;
}
}
if (need_r) {
GetWindowRect(fl_xid(win), &r);
}
int width, height;
RECT rc;
GetClientRect(fl_xid(win), &rc);
width = rc.right;
height = rc.bottom;
bx = (r.right - r.left - width)/2;
if (bx < 1) bx = 1;
by = bx;
bt = r.bottom - r.top - height - 2 * by;
}
return r;
}
int Fl_WinAPI_Window_Driver::decorated_w()
{
int bt, bx, by;
float s = Fl::screen_driver()->scale(screen_num());
border_width_title_bar_height(bx, by, bt);
int mini_bx = int(bx/s); if (mini_bx < 1) mini_bx = 1;
return w() + 2 * mini_bx;
}
int Fl_WinAPI_Window_Driver::decorated_h()
{
int bt, bx, by;
border_width_title_bar_height(bx, by, bt);
float s = Fl::screen_driver()->scale(screen_num());
int mini_by = int(by / s); if (mini_by < 1) mini_by = 1;
return h() + int((bt + by) / s) + mini_by;
}
void Fl_WinAPI_Window_Driver::shape_bitmap_(Fl_Image* b) {
shape_data_->shape_ = b;
}
void Fl_WinAPI_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8;
unsigned u;
uchar byte, onebit;
const uchar* bits = new uchar[h*bytesperrow]; const uchar* alpha = (const uchar*)*img->data() + offset; for (i = 0; i < h; i++) {
uchar *p = (uchar*)bits + i * bytesperrow;
byte = 0;
onebit = 1;
for (j = 0; j < w; j++) {
if (d == 3) {
u = *alpha;
u += *(alpha+1);
u += *(alpha+2);
}
else u = *alpha;
if (u > 0) { byte |= onebit; }
onebit = onebit << 1; if (onebit == 0 || j == w-1) {
onebit = 1;
*p++ = byte; byte = 0;
}
alpha += d; }
}
Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h);
bitmap->alloc_array = 1;
shape_bitmap_(bitmap);
shape_data_->effective_bitmap_ = bitmap;
shape_data_->shape_ = img;
}
void Fl_WinAPI_Window_Driver::shape(const Fl_Image* img) {
if (shape_data_) {
if (shape_data_->effective_bitmap_) { delete shape_data_->effective_bitmap_; }
}
else {
shape_data_ = new shape_data_type;
}
memset(shape_data_, 0, sizeof(shape_data_type));
pWindow->border(false);
int d = img->d();
if (d && img->count() >= 2) {
shape_pixmap_((Fl_Image*)img);
shape_data_->shape_ = (Fl_Image*)img;
}
else if (d == 0) shape_bitmap_((Fl_Image*)img);
else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1);
else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0);
}
static inline BYTE bit(int x) { return (BYTE)(1 << (x%8)); }
static HRGN bitmap2region(Fl_Image* image) {
HRGN hRgn = 0;
const int ALLOC_UNIT = 100;
DWORD maxRects = ALLOC_UNIT;
RGNDATA* pData = (RGNDATA*)malloc(sizeof(RGNDATAHEADER)+(sizeof(RECT)*maxRects));
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
const int bytesPerLine = (image->w() + 7)/8;
BYTE* p, *data = (BYTE*)*image->data();
for (int y = 0; y < image->h(); y++) {
for (int x = 0; x < image->w(); x++) {
int x0 = x;
while (x < image->w()) {
p = data + x / 8;
if (!((*p) & bit(x))) break; x++;
}
if (x > x0) {
RECT *pr;
if (pData->rdh.nCount >= maxRects) {
maxRects += ALLOC_UNIT;
pData = (RGNDATA*)realloc(pData, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects));
}
pr = (RECT*)&pData->Buffer;
SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
if (x0 < pData->rdh.rcBound.left)
pData->rdh.rcBound.left = x0;
if (y < pData->rdh.rcBound.top)
pData->rdh.rcBound.top = y;
if (x > pData->rdh.rcBound.right)
pData->rdh.rcBound.right = x;
if (y+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = y+1;
pData->rdh.nCount++;
if (pData->rdh.nCount == 2000) {
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects), pData);
if (hRgn) {
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
} else
hRgn = h;
pData->rdh.nCount = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
}
}
}
data += bytesPerLine;
}
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER)
+ (sizeof(RECT)*maxRects), pData);
if (hRgn) {
CombineRgn(hRgn, hRgn, h, RGN_OR);
DeleteObject(h);
} else hRgn = h;
free(pData); return hRgn;
}
void Fl_WinAPI_Window_Driver::draw_begin()
{
if (shape_data_) {
float s = Fl::screen_driver()->scale(screen_num());
if ((shape_data_->lw_ != s*w() || shape_data_->lh_ != s*h()) && shape_data_->shape_) {
shape_data_->lw_ = int(s * w());
shape_data_->lh_ = int(s * h());
Fl_Image* temp = shape_data_->effective_bitmap_ ? shape_data_->effective_bitmap_ : shape_data_->shape_;
temp = temp->copy(shape_data_->lw_, shape_data_->lh_);
HRGN region = bitmap2region(temp);
SetWindowRgn(fl_xid(pWindow), region, TRUE); delete temp;
}
}
}
void Fl_WinAPI_Window_Driver::flush_double()
{
if (!shown()) return;
pWindow->make_current(); Fl_X *i = Fl_X::flx(pWindow);
if (!i) return;
if (!other_xid) {
other_xid = new Fl_Image_Surface(w(), h(), 1);
pWindow->clear_damage(FL_DAMAGE_ALL);
}
if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) {
fl_clip_region(i->region); i->region = 0;
#if 0#else
HDC sgc = fl_gc;
fl_gc = fl_makeDC((HBITMAP)other_xid->offscreen());
int savedc = SaveDC(fl_gc);
fl_graphics_driver->gc(fl_gc);
fl_graphics_driver->restore_clip(); # if defined(FLTK_HAVE_CAIROEXT)
if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow);
# endif
draw();
RestoreDC(fl_gc, savedc);
DeleteDC(fl_gc);
fl_graphics_driver->gc(sgc);
#endif
}
int X = 0, Y = 0, W = 0, H = 0;
fl_clip_box(0, 0, w(), h(), X, Y, W, H);
if (other_xid) fl_copy_offscreen(X, Y, W, H, other_xid->offscreen(), X, Y);
}
void Fl_WinAPI_Window_Driver::flush_overlay()
{
Fl_Overlay_Window *oWindow = pWindow->as_overlay_window();
if (!shown()) return;
pWindow->make_current(); Fl_X *i = Fl_X::flx(pWindow);
if (!i) return;
int eraseoverlay = (pWindow->damage()&FL_DAMAGE_OVERLAY);
pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY));
if (!other_xid) {
other_xid = new Fl_Image_Surface(w(), h(), 1);
pWindow->clear_damage(FL_DAMAGE_ALL);
}
if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) {
fl_clip_region(i->region); i->region = 0;
Fl_Surface_Device::push_current(other_xid);
fl_graphics_driver->clip_region(0);
draw();
Fl_Surface_Device::pop_current();
}
if (eraseoverlay) fl_clip_region(0);
int X, Y, W, H; fl_clip_box(0, 0, w(), h(), X, Y, W, H);
if (other_xid) fl_copy_offscreen(X, Y, W, H, other_xid->offscreen(), X, Y);
if (overlay() == oWindow) oWindow->draw_overlay();
}
void Fl_WinAPI_Window_Driver::icons(const Fl_RGB_Image *icons[], int count) {
free_icons();
if (count > 0) {
icon_->icons = new Fl_RGB_Image*[count];
icon_->count = count;
for (int i = 0;i < count;i++) {
icon_->icons[i] = (Fl_RGB_Image*)((Fl_RGB_Image*)icons[i])->copy();
icon_->icons[i]->normalize();
}
}
if (Fl_X::flx(pWindow))
set_icons();
}
const void *Fl_WinAPI_Window_Driver::icon() const {
return icon_->legacy_icon;
}
void Fl_WinAPI_Window_Driver::icon(const void * ic) {
free_icons();
icon_->legacy_icon = ic;
}
void Fl_WinAPI_Window_Driver::free_icons() {
int i;
icon_->legacy_icon = 0L;
if (icon_->icons) {
for (i = 0;i < icon_->count;i++)
delete icon_->icons[i];
delete [] icon_->icons;
icon_->icons = 0L;
}
icon_->count = 0;
if (icon_->big_icon)
DestroyIcon(icon_->big_icon);
if (icon_->small_icon)
DestroyIcon(icon_->small_icon);
icon_->big_icon = NULL;
icon_->small_icon = NULL;
}
void Fl_WinAPI_Window_Driver::make_current() {
fl_GetDC(fl_xid(pWindow));
#if USE_COLORMAP
fl_select_palette();
#endif
fl_graphics_driver->clip_region(0);
((Fl_GDI_Graphics_Driver*)fl_graphics_driver)->scale(Fl::screen_driver()->scale(screen_num()));
#if defined(FLTK_HAVE_CAIROEXT)
if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow);
#endif
}
void Fl_WinAPI_Window_Driver::label(const char *name,const char *iname) {
if (shown() && !parent()) {
if (!name) name = "";
size_t l = strlen(name);
unsigned wlen = fl_utf8toUtf16(name, (unsigned) l, NULL, 0); wlen++;
unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen);
wlen = fl_utf8toUtf16(name, (unsigned) l, lab, wlen);
lab[wlen] = 0;
SetWindowTextW(fl_xid(pWindow), (WCHAR *)lab);
free(lab);
}
}
extern void fl_clipboard_notify_retarget(HWND wnd);
extern void fl_update_clipboard(void);
extern char fl_i_own_selection[2];
void Fl_WinAPI_Window_Driver::hide() {
Fl_X* ip = Fl_X::flx(pWindow);
int count = 0;
Fl_Window *win, **doit = NULL;
for (win = Fl::first_window(); win && ip; win = Fl::next_window(win)) {
if (win->non_modal() && GetWindow(fl_xid(win), GW_OWNER) == (HWND)ip->xid) {
count++;
}
}
if (count) {
doit = new Fl_Window*[count];
count = 0;
for (win = Fl::first_window(); win && ip; win = Fl::next_window(win)) {
if (win->non_modal() && GetWindow(fl_xid(win), GW_OWNER) == (HWND)ip->xid) {
doit[count++] = win;
}
}
}
if (hide_common()) {
delete[] doit; return;
}
RevokeDragDrop((HWND)ip->xid);
fl_i_own_selection[1] = 0;
if (GetClipboardOwner() == (HWND)ip->xid)
fl_update_clipboard();
fl_clipboard_notify_retarget((HWND)ip->xid);
PostMessage((HWND)ip->xid, WM_APP, 0, 0);
if (private_dc) fl_release_dc((HWND)ip->xid, private_dc);
if ((HWND)ip->xid == fl_window && fl_graphics_driver->gc()) {
fl_release_dc(fl_window, (HDC)fl_graphics_driver->gc());
fl_window = (HWND)-1;
fl_graphics_driver->gc(0);
# ifdef FLTK_HAVE_CAIROEXT
if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0);
# endif
}
if (ip->region) Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region);
HWND p = GetForegroundWindow();
if (p==GetParent((HWND)ip->xid)) {
ShowWindow((HWND)ip->xid, SW_HIDE);
ShowWindow(p, SW_SHOWNA);
}
DestroyWindow((HWND)ip->xid);
if (count) {
int ii;
for (ii = 0; ii < count; ii++) doit[ii]->hide();
for (ii = 0; ii < count; ii++) {
if (ii != 0) doit[0]->show(); doit[ii]->show();
}
}
delete[] doit;
if (pWindow->non_modal() && Fl::first_window() && Fl::first_window()->shown())
Fl::first_window()->show();
delete ip;
screen_num_ = -1;
}
void Fl_WinAPI_Window_Driver::map() {
ShowWindow(fl_xid(pWindow), SW_RESTORE); }
void Fl_WinAPI_Window_Driver::unmap() {
ShowWindow(fl_xid(pWindow), SW_HIDE);
}
#if !defined(FL_DOXYGEN)
void Fl_WinAPI_Window_Driver::make_fullscreen(int X, int Y, int W, int H) {
HWND xid = fl_xid(pWindow);
int top, bottom, left, right;
int sx, sy, sw, sh;
top = fullscreen_screen_top();
bottom = fullscreen_screen_bottom();
left = fullscreen_screen_left();
right = fullscreen_screen_right();
if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
top = screen_num();
bottom = top;
left = top;
right = top;
}
Fl_WinAPI_Screen_Driver *scr_dr = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver();
scr_dr->screen_xywh_unscaled(sx, Y, sw, sh, top);
scr_dr->screen_xywh_unscaled(sx, sy, sw, sh, bottom);
H = sy + sh - Y;
scr_dr->screen_xywh_unscaled(X, sy, sw, sh, left);
scr_dr->screen_xywh_unscaled(sx, sy, sw, sh, right);
W = sx + sw - X;
DWORD flags = GetWindowLong(xid, GWL_STYLE);
flags = flags & ~(WS_THICKFRAME|WS_CAPTION);
SetWindowLong(xid, GWL_STYLE, flags);
SetWindowPos(xid, HWND_TOP, X, Y, W, H, SWP_NOSENDCHANGING | SWP_FRAMECHANGED);
}
#endif
void Fl_WinAPI_Window_Driver::fullscreen_on() {
pWindow->_set_fullscreen();
make_fullscreen(x(), y(), w(), h());
Fl::handle(FL_FULLSCREEN, pWindow);
}
void Fl_WinAPI_Window_Driver::fullscreen_off(int X, int Y, int W, int H) {
pWindow->_clear_fullscreen();
DWORD style = GetWindowLong(fl_xid(pWindow), GWL_STYLE);
if (pWindow->border()) style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION;
HWND xid = fl_xid(pWindow);
Fl_X::flx(pWindow)->xid = 0;
int wx, wy, bt, bx, by;
switch (fake_X_wm(wx, wy, bt, bx, by, style)) {
case 0:
break;
case 1:
style |= WS_CAPTION;
break;
case 2:
break;
}
Fl_X::flx(pWindow)->xid = (fl_uintptr_t)xid;
SetWindowLong(fl_xid(pWindow), GWL_STYLE, style);
if (!pWindow->maximize_active()) {
float s = Fl::screen_driver()->scale(screen_num());
int scaledX = int(ceil(X*s)), scaledY= int(ceil(Y*s)), scaledW = int(ceil(W*s)), scaledH = int(ceil(H*s));
if ((X != x()) || (Y != y())) {
scaledX -= bx;
scaledY -= by+bt;
}
scaledW += bx*2;
scaledH += by*2+bt;
SetWindowPos(fl_xid(pWindow), 0, scaledX, scaledY, scaledW, scaledH,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
} else {
int WX, WY, WW, WH;
((Fl_WinAPI_Screen_Driver*)Fl::screen_driver())->screen_xywh_unscaled(WX, WY, WW, WH, screen_num());
SetWindowPos(fl_xid(pWindow), 0, WX, WY, WW, WH,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
Fl::handle(FL_FULLSCREEN, pWindow);
}
void Fl_WinAPI_Window_Driver::maximize() {
if (!border()) return Fl_Window_Driver::maximize();
ShowWindow(fl_xid(pWindow), SW_SHOWMAXIMIZED);
}
void Fl_WinAPI_Window_Driver::un_maximize() {
if (!border()) return Fl_Window_Driver::un_maximize();
ShowWindow(fl_xid(pWindow), SW_SHOWNORMAL);
}
void Fl_WinAPI_Window_Driver::iconize() {
ShowWindow(fl_xid(pWindow), SW_SHOWMINNOACTIVE);
}
void Fl_WinAPI_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom) {
int minw, minh, maxw, maxh, set;
set = pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL);
if (set && (maxw != minw || maxh != minh)) {
*left = *right = GetSystemMetrics(SM_CXSIZEFRAME);
*top = *bottom = GetSystemMetrics(SM_CYSIZEFRAME);
} else {
*left = *right = GetSystemMetrics(SM_CXFIXEDFRAME);
*top = *bottom = GetSystemMetrics(SM_CYFIXEDFRAME);
}
*top += GetSystemMetrics(SM_CYCAPTION);
}
int Fl_WinAPI_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y,
void (*draw_area)(void*, int,int,int,int), void* data)
{
typedef int (WINAPI* fl_GetRandomRgn_func)(HDC, HRGN, INT);
static fl_GetRandomRgn_func fl_GetRandomRgn = 0L;
static char first_time = 1;
if (first_time) {
HMODULE hMod = GetModuleHandle("GDI32.DLL");
if (hMod) {
fl_GetRandomRgn = (fl_GetRandomRgn_func)GetProcAddress(hMod, "GetRandomRgn");
}
first_time = 0;
}
float s = Fl::screen_driver()->scale(screen_num());
src_x = int(src_x * s); src_y = int(src_y * s);
src_w = int(src_w * s); src_h = int(src_h * s);
dest_x = int(dest_x * s); dest_y = int(dest_y * s);
HDC gc = (HDC)fl_graphics_driver->gc();
if (fl_GetRandomRgn) {
HRGN sys_rgn = CreateRectRgn(0, 0, 0, 0);
fl_GetRandomRgn(gc, sys_rgn, 4);
HRGN src_rgn = CreateRectRgn(src_x, src_y, src_x+src_w, src_y+src_h);
POINT offset = { 0, 0 };
if (GetDCOrgEx(gc, &offset)) {
OffsetRgn(src_rgn, offset.x, offset.y);
}
HRGN dst_rgn = CreateRectRgn(0, 0, 0, 0);
int r = CombineRgn(dst_rgn, src_rgn, sys_rgn, RGN_DIFF);
DeleteObject(dst_rgn);
DeleteObject(src_rgn);
DeleteObject(sys_rgn);
if (r != NULLREGION) {
return 1;
}
}
BitBlt(gc, dest_x, dest_y, src_w, src_h, gc, src_x, src_y,SRCCOPY);
return 0;
}
Fl_WinAPI_Window_Driver::type_for_resize_window_between_screens Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_ = {0, false};
void Fl_WinAPI_Window_Driver::resize_after_screen_change(void *data) {
Fl_Window *win = (Fl_Window*)data;
RECT r;
GetClientRect(fl_xid(win), &r);
float old_f = float(r.right)/win->w();
int ns = data_for_resize_window_between_screens_.screen;
Fl_Window_Driver::driver(win)->resize_after_scale_change(ns, old_f, Fl::screen_driver()->scale(ns));
data_for_resize_window_between_screens_.busy = false;
}
const Fl_Image* Fl_WinAPI_Window_Driver::shape() {
return shape_data_ ? shape_data_->shape_ : NULL;
}
HWND fl_win32_xid(const Fl_Window *win) {
return (HWND)Fl_Window_Driver::xid(win);
}
Fl_Window *fl_win32_find(HWND xid) {
return Fl_Window_Driver::find((fl_uintptr_t)xid);
}