#include <FL/platform.H>
#include "Fl_Wayland_Window_Driver.H"
#include "Fl_Wayland_Screen_Driver.H"
#include "Fl_Wayland_Graphics_Driver.H"
#include <FL/filename.H>
#include <wayland-cursor.h>
#include "../../../libdecor/build/fl_libdecor.h"
#include "xdg-shell-client-protocol.h"
#include "gtk-shell-client-protocol.h"
#include <pango/pangocairo.h>
#include <FL/Fl_Overlay_Window.H>
#include <FL/Fl_Tooltip.H>
#include <FL/fl_draw.H>
#include <FL/fl_ask.H>
#include <FL/Fl.H>
#include <FL/Fl_Image_Surface.H>
#include <FL/Fl_Menu_Button.H>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <unistd.h>
struct cursor_image { struct wl_cursor_image image;
struct wl_cursor_theme *theme;
struct wl_buffer *buffer;
int offset;
};
extern "C" {
# include "../../../libdecor/src/libdecor-plugin.h"
uchar *fl_libdecor_titlebar_buffer(struct libdecor_frame *frame, int *w, int *h, int *stride);
}
#define fl_max(a,b) ((a) > (b) ? (a) : (b))
#define fl_min(a,b) ((a) < (b) ? (a) : (b))
#if !defined(FLTK_USE_X11)
Window fl_window = 0;
#endif
struct wld_window *Fl_Wayland_Window_Driver::wld_window = NULL;
bool Fl_Wayland_Window_Driver::new_popup = false; Fl_Window *Fl_Wayland_Window_Driver::previous_floatingtitle = NULL;
Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_Driver(win)
{
shape_data_ = NULL;
standard_cursor_ = FL_CURSOR_DEFAULT;
in_handle_configure = false;
screen_num_ = -1;
gl_start_support_ = NULL;
subRect_ = NULL;
is_popup_window_ = false;
can_expand_outside_parent_ = false;
}
void Fl_Wayland_Window_Driver::delete_cursor(
struct Fl_Wayland_Window_Driver::custom_cursor *custom, bool delete_rgb) {
struct wl_cursor *wl_cursor = custom->wl_cursor;
struct cursor_image *new_image = (struct cursor_image*)wl_cursor->images[0];
struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen =
(struct Fl_Wayland_Graphics_Driver::wld_buffer *)
wl_buffer_get_user_data(new_image->buffer);
struct wld_window fake_xid;
memset(&fake_xid, 0, sizeof(fake_xid));
fake_xid.buffer = offscreen;
Fl_Wayland_Graphics_Driver::buffer_release(&fake_xid);
free(new_image);
free(wl_cursor->images);
free(wl_cursor->name);
free(wl_cursor);
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
if (scr_driver->default_cursor() == wl_cursor) {
scr_driver->default_cursor(scr_driver->xc_cursor[Fl_Wayland_Screen_Driver::arrow]);
}
if (delete_rgb) delete custom->rgb;
delete custom;
}
Fl_Wayland_Window_Driver::~Fl_Wayland_Window_Driver()
{
if (shape_data_) {
cairo_surface_t *surface;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface);
uchar *data = cairo_image_surface_get_data(surface);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] data;
delete shape_data_;
}
if (subRect_) delete subRect_;
if (gl_start_support_) { gl_plugin()->destroy(gl_start_support_);
}
}
void Fl_Wayland_Window_Driver::decorated_win_size(int &w, int &h)
{
Fl_Window *win = pWindow;
w = win->w();
h = win->h();
if (!win->shown() || win->parent() || !win->border() || !win->visible()) return;
int X, titlebar_height;
libdecor_frame_translate_coordinate(fl_wl_xid(win)->frame, 0, 0, &X, &titlebar_height);
h = win->h() + ceil(titlebar_height / Fl::screen_scale(win->screen_num()));
}
int Fl_Wayland_Window_Driver::decorated_h()
{
int w, h;
decorated_win_size(w, h);
return h;
}
int Fl_Wayland_Window_Driver::decorated_w()
{
int w, h;
decorated_win_size(w, h);
return w;
}
struct xdg_toplevel *Fl_Wayland_Window_Driver::xdg_toplevel() {
struct wld_window * w = fl_wl_xid(pWindow);
struct xdg_toplevel *top = NULL;
if (w->kind == DECORATED) top = libdecor_frame_get_xdg_toplevel(w->frame);
else if (w->kind == UNFRAMED) top = w->xdg_toplevel;
return top;
}
void Fl_Wayland_Window_Driver::take_focus()
{
struct wld_window *w = fl_wl_xid(pWindow);
if (w) {
Fl_Window *old_first = Fl::first_window();
struct wld_window *first_xid = (old_first ? fl_wl_xid(old_first->top_window()) : NULL);
if (first_xid && first_xid != w && xdg_toplevel()) {
Fl_Wayland_Window_Driver *top_dr =
Fl_Wayland_Window_Driver::driver(old_first->top_window());
xdg_toplevel_set_parent(xdg_toplevel(), top_dr->xdg_toplevel());
xdg_toplevel_set_parent(xdg_toplevel(), NULL);
wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display);
}
fl_wl_find(w);
}
}
void Fl_Wayland_Window_Driver::flush_overlay()
{
if (!shown()) return;
Fl_Overlay_Window *oWindow = pWindow->as_overlay_window();
int erase_overlay = (pWindow->damage()&FL_DAMAGE_OVERLAY) | (overlay() == oWindow);
pWindow->clear_damage((uchar)(pWindow->damage()&~FL_DAMAGE_OVERLAY));
pWindow->make_current();
if (!other_xid) {
other_xid = new Fl_Image_Surface(oWindow->w(), oWindow->h(), 1);
oWindow->clear_damage(FL_DAMAGE_ALL);
}
if (oWindow->damage() & ~FL_DAMAGE_EXPOSE) {
Fl_X *myi = Fl_X::flx(pWindow);
fl_clip_region(myi->region); myi->region = 0;
Fl_Surface_Device::push_current(other_xid);
draw();
Fl_Surface_Device::pop_current();
}
if (erase_overlay) fl_clip_region(0);
if (other_xid) {
struct Fl_Wayland_Graphics_Driver::draw_buffer *buffer =
Fl_Wayland_Graphics_Driver::offscreen_buffer(other_xid->offscreen());
struct wld_window *xid = fl_wl_xid(pWindow);
struct Fl_Wayland_Graphics_Driver::wld_buffer *wbuffer = xid->buffer;
if (wbuffer->draw_buffer.data_size != buffer->data_size) {
fl_copy_offscreen(0, 0, oWindow->w(), oWindow->h(), other_xid->offscreen(), 0, 0);
} else {
memcpy(wbuffer->draw_buffer.buffer, buffer->buffer, wbuffer->draw_buffer.data_size);
}
}
if (overlay() == oWindow) oWindow->draw_overlay();
}
const Fl_Image* Fl_Wayland_Window_Driver::shape() {
return shape_data_ ? shape_data_->shape_ : NULL;
}
void Fl_Wayland_Window_Driver::shape_bitmap_(Fl_Image* b) {
shape_data_->mask_pattern_ = Fl_Cairo_Graphics_Driver::bitmap_to_pattern(
(Fl_Bitmap*)b, true, NULL);
shape_data_->shape_ = b;
shape_data_->lw_ = b->data_w();
shape_data_->lh_ = b->data_h();
}
void Fl_Wayland_Window_Driver::shape_alpha_(Fl_Image* img, int offset) {
int i, j, d = img->d(), w = img->data_w(), h = img->data_h();
int bytesperrow = cairo_format_stride_for_width(CAIRO_FORMAT_A1, w);
unsigned u;
uchar byte, onebit;
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; }
}
cairo_surface_t *mask_surf = cairo_image_surface_create_for_data(bits, CAIRO_FORMAT_A1,
w, h, bytesperrow);
shape_data_->mask_pattern_ = cairo_pattern_create_for_surface(mask_surf);
cairo_surface_destroy(mask_surf);
shape_data_->shape_ = img;
shape_data_->lw_ = w;
shape_data_->lh_ = h;
}
void Fl_Wayland_Window_Driver::shape(const Fl_Image* img) {
if (shape_data_) {
if (shape_data_->mask_pattern_) {
cairo_surface_t *surface;
cairo_pattern_get_surface(shape_data_->mask_pattern_, &surface);
uchar *data = cairo_image_surface_get_data(surface);
cairo_pattern_destroy(shape_data_->mask_pattern_);
delete[] data;
}
}
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);
}
void Fl_Wayland_Window_Driver::draw_end()
{
if (shape_data_ && shape_data_->mask_pattern_) {
Fl_Wayland_Graphics_Driver *gr_dr = (Fl_Wayland_Graphics_Driver*)fl_graphics_driver;
cairo_t *cr = gr_dr->cr();
cairo_matrix_t matrix;
cairo_matrix_init_scale(&matrix, double(shape_data_->lw_) / (pWindow->w() + 1),
double(shape_data_->lh_) / (pWindow->h() + 1) );
cairo_matrix_translate(&matrix, 1, 1);
cairo_pattern_set_matrix(shape_data_->mask_pattern_, &matrix);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_mask(cr, shape_data_->mask_pattern_);
cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
}
}
void Fl_Wayland_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image*& top,
Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right)
{
top = left = bottom = right = NULL;
if (pWindow->decorated_h() == h()) return;
int htop = pWindow->decorated_h() - pWindow->h();
struct wld_window *wwin = fl_wl_xid(pWindow);
int width, height, stride;
uchar *cairo_data = fl_libdecor_titlebar_buffer(wwin->frame, &width, &height, &stride);
if (!cairo_data) return;
uchar *data = new uchar[width * height * 4];
uchar *p = data;
for (int j = 0; j < height; j++) {
uchar *q = cairo_data + j * stride;
for (int i = 0; i < width; i++) {
*p++ = *(q+2); *p++ = *(q+1); *p++ = *q; *p++ = *(q+3); q += 4;
}
}
top = new Fl_RGB_Image(data, width, height, 4);
top->alloc_array = 1;
top->scale(pWindow->w(), htop);
}
void Fl_Wayland_Window_Driver::make_current() {
if (!shown()) {
static const char err_message[] = "Fl_Window::make_current(), but window is not shown().";
fl_alert(err_message);
Fl::fatal(err_message);
}
struct wld_window *window = fl_wl_xid(pWindow);
if (window->buffer) {
((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag(
&window->buffer->draw_buffer_needs_commit);
}
if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb) &&
(!wait_for_expose_value) ) {
Fl_Wayland_Graphics_Driver::buffer_commit(window);
}
Fl_Wayland_Window_Driver::wld_window = window;
fl_window = (Window)window;
float f = Fl::screen_scale(pWindow->screen_num());
int wld_s = wld_scale();
if (!window->buffer) {
window->buffer = Fl_Wayland_Graphics_Driver::create_wld_buffer(
int(pWindow->w() * f) * wld_s, int(pWindow->h() * f) * wld_s, false);
((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag(
&window->buffer->draw_buffer_needs_commit);
}
((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->set_cairo(
window->buffer->draw_buffer.cairo_, f * wld_s);
((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->wld_scale = wld_s;
int *poffset = Fl_Window_Driver::menu_offset_y(pWindow);
if (poffset) { cairo_translate(window->buffer->draw_buffer.cairo_, 0, *poffset);
}
cairo_rectangle_int_t *extents = subRect();
if (extents) { Fl_Region clip_region = fl_graphics_driver->XRectangleRegion(extents->x, extents->y,
extents->width, extents->height);
Fl_X::flx(pWindow)->region = clip_region;
}
else fl_graphics_driver->clip_region(0);
#ifdef FLTK_HAVE_CAIROEXT
if (Fl::cairo_autolink_context()) Fl::cairo_make_current(pWindow);
#endif
}
void Fl_Wayland_Window_Driver::flush() {
if (!pWindow->damage()) return;
if (pWindow->as_gl_window()) {
int W = pWindow->w();
int H = pWindow->h();
float scale = fl_graphics_driver->scale();
Fl_Wayland_Window_Driver::in_flush_ = true;
Fl_Window_Driver::flush();
Fl_Wayland_Window_Driver::in_flush_ = false;
gl_plugin()->do_swap(pWindow); if (scale != fl_graphics_driver->scale() || W != pWindow->w() || H != pWindow->h()) {
gl_plugin()->invalidate(pWindow);
}
return;
}
struct wld_window *window = fl_wl_xid(pWindow);
if (!window || !window->configured_width) return;
Fl_X *ip = Fl_X::flx(pWindow);
cairo_region_t* r = (cairo_region_t*)ip->region;
if (!window->buffer || pWindow->as_overlay_window()) r = NULL;
Fl_Wayland_Window_Driver::in_flush_ = true;
Fl_Window_Driver::flush();
Fl_Wayland_Window_Driver::in_flush_ = false;
if (!window->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(window, r);
}
void Fl_Wayland_Window_Driver::show() {
if (!shown()) {
fl_open_display();
makeWindow();
} else {
Fl::handle(FL_SHOW, pWindow);
}
}
static void popup_done(void *data, struct xdg_popup *xdg_popup);
static void destroy_surface_caution_pointer_focus(struct wl_surface *surface,
struct Fl_Wayland_Screen_Driver::seat *seat) {
if (seat->pointer_focus == surface) seat->pointer_focus = NULL;
if (seat->keyboard_surface == surface) seat->keyboard_surface = NULL;
wl_surface_destroy(surface);
}
void Fl_Wayland_Window_Driver::hide() {
if (pWindow == Fl_Screen_Driver::transient_scale_parent) {
Fl::remove_timeout(Fl_Screen_Driver::del_transient_window);
Fl_Screen_Driver::del_transient_window(NULL);
}
Fl_X* ip = Fl_X::flx(pWindow);
if (hide_common()) return;
if (ip->region) {
Fl_Graphics_Driver::default_driver().XDestroyRegion(ip->region);
ip->region = 0;
}
screen_num_ = -1;
struct wld_window *wld_win = (struct wld_window*)ip->xid;
if (wld_win) { Fl_Wayland_Graphics_Driver::buffer_release(wld_win);
if (wld_win->kind == SUBWINDOW && wld_win->subsurface) {
wl_subsurface_destroy(wld_win->subsurface);
wld_win->subsurface = NULL;
}
if (wld_win->kind == DECORATED) {
libdecor_frame_unref(wld_win->frame);
wld_win->frame = NULL;
wld_win->xdg_surface = NULL;
} else {
if (wld_win->kind == POPUP && wld_win->xdg_popup) {
popup_done(xdg_popup_get_user_data(wld_win->xdg_popup), wld_win->xdg_popup);
wld_win->xdg_popup = NULL;
}
if (wld_win->kind == UNFRAMED && wld_win->xdg_toplevel) {
xdg_toplevel_destroy(wld_win->xdg_toplevel);
wld_win->xdg_toplevel = NULL;
}
if (wld_win->xdg_surface) {
xdg_surface_destroy(wld_win->xdg_surface);
wld_win->xdg_surface = NULL;
}
}
if (wld_win->custom_cursor) delete_cursor(wld_win->custom_cursor);
if (wld_win->wl_surface) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
destroy_surface_caution_pointer_focus(wld_win->wl_surface, scr_driver->seat);
wld_win->wl_surface = NULL;
}
while (!wl_list_empty(&wld_win->outputs)) { struct surface_output *s_output;
s_output = wl_container_of(wld_win->outputs.next, s_output, link);
wl_list_remove(&s_output->link);
free(s_output);
}
if (Fl_Wayland_Window_Driver::wld_window == wld_win) {
Fl_Wayland_Window_Driver::wld_window = NULL;
}
if (wld_win->frame_cb) wl_callback_destroy(wld_win->frame_cb); free(wld_win);
}
delete ip;
}
void Fl_Wayland_Window_Driver::map() {
Fl_X* ip = Fl_X::flx(pWindow);
struct wld_window *wl_win = (struct wld_window*)ip->xid;
if (wl_win->kind == SUBWINDOW && !wl_win->subsurface) {
struct wld_window *parent = fl_wl_xid(pWindow->window());
if (parent) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
wl_win->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor,
wl_win->wl_surface, parent->wl_surface);
float f = Fl::screen_scale(pWindow->top_window()->screen_num());
wl_subsurface_set_position(wl_win->subsurface, pWindow->x() * f, pWindow->y() * f);
wl_subsurface_set_desync(wl_win->subsurface); wl_subsurface_place_above(wl_win->subsurface, parent->wl_surface);
wl_win->configured_width = pWindow->w();
wl_win->configured_height = pWindow->h();
wait_for_expose_value = 0;
pWindow->redraw();
}
}
}
void Fl_Wayland_Window_Driver::unmap() {
Fl_X* ip = Fl_X::flx(pWindow);
struct wld_window *wl_win = (struct wld_window*)ip->xid;
if (wl_win->kind == SUBWINDOW && wl_win->wl_surface) {
wl_surface_attach(wl_win->wl_surface, NULL, 0, 0);
Fl_Wayland_Graphics_Driver::buffer_release(wl_win);
wl_subsurface_destroy(wl_win->subsurface);
wl_win->subsurface = NULL;
}
}
void Fl_Wayland_Window_Driver::size_range() {
if (shown()) {
Fl_X* ip = Fl_X::flx(pWindow);
struct wld_window *wl_win = (struct wld_window*)ip->xid;
float f = Fl::screen_scale(pWindow->screen_num());
int minw, minh, maxw, maxh;
pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL);
if (wl_win->kind == DECORATED && wl_win->frame) {
int X,Y,W,H;
Fl::screen_work_area(X,Y,W,H, Fl::screen_num(x(),y(),w(),h()));
if (maxw && maxw < W && maxh && maxh < H) {
libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN);
} else {
libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_FULLSCREEN);
}
if (maxw && maxh && (minw >= maxw || minh >= maxh)) {
libdecor_frame_unset_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE);
} else {
libdecor_frame_set_capabilities(wl_win->frame, LIBDECOR_ACTION_RESIZE);
}
libdecor_frame_set_min_content_size(wl_win->frame, minw*f, minh*f);
libdecor_frame_set_max_content_size(wl_win->frame, maxw*f, maxh*f);
if (xdg_toplevel()) {
struct libdecor_state *state = libdecor_state_new(int(w() * f), int(h() * f));
libdecor_frame_commit(wl_win->frame, state, NULL);
libdecor_state_free(state);
}
} else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) {
xdg_toplevel_set_min_size(wl_win->xdg_toplevel, minw*f, minh*f);
if (maxw && maxh)
xdg_toplevel_set_max_size(wl_win->xdg_toplevel, maxw*f, maxh*f);
}
}
}
void Fl_Wayland_Window_Driver::iconize() {
Fl_X* ip = Fl_X::flx(pWindow);
struct wld_window *wl_win = (struct wld_window*)ip->xid;
if (wl_win->kind == DECORATED) {
libdecor_frame_set_minimized(wl_win->frame);
Fl::handle(FL_HIDE, pWindow);
}
else if (wl_win->kind == UNFRAMED && wl_win->xdg_toplevel) xdg_toplevel_set_minimized(wl_win->xdg_toplevel);
}
void Fl_Wayland_Window_Driver::decoration_sizes(int *top, int *left, int *right, int *bottom)
{
struct wld_window *xid = (struct wld_window*)fl_xid(pWindow);
if (xid && xid->kind == DECORATED) {
libdecor_frame_translate_coordinate(xid->frame, 0, 0, left, top);
*right = *left;
*bottom = 0;
} else {
Fl_Window_Driver::decoration_sizes(top, left, right, bottom);
}
}
int Fl_Wayland_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)
{
struct wld_window * xid = fl_wl_xid(pWindow);
struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer = xid->buffer;
float s = wld_scale() * fl_graphics_driver->scale();
if (s != 1) {
src_x = src_x * s;
src_y = src_y * s;
src_w = src_w * s;
src_h = src_h * s;
dest_x = dest_x * s;
dest_y = dest_y * s;
}
if (src_x == dest_x) { int i, to, step;
if (src_y > dest_y) {
i = 0; to = src_h; step = 1;
} else {
i = src_h - 1; to = -1; step = -1;
}
while (i != to) {
memcpy(
buffer->draw_buffer.buffer + (dest_y + i) * buffer->draw_buffer.stride + 4 * dest_x,
buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x,
4 * src_w);
i += step;
}
} else { int i, to, step;
if (src_x > dest_x) {
i = 0; to = src_h; step = 1;
} else {
i = src_h - 1; to = -1; step = -1;
}
while (i != to) {
memmove(
buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * dest_x,
buffer->draw_buffer.buffer + (src_y + i) * buffer->draw_buffer.stride + 4 * src_x,
4 * src_w);
i += step;
}
}
return 0;
}
static void handle_error(struct libdecor *libdecor_context, enum libdecor_error error, const char *message)
{
Fl::fatal("Caught error (%d): %s\n", error, message);
}
static struct libdecor_interface libdecor_iface = {
.error = handle_error,
};
void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *window,
float pre_scale) {
Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
if (!window->fl_win->parent()) {
Fl_Wayland_Screen_Driver::output *running_output;
Fl_Wayland_Screen_Driver *scr_dr = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
int i = 0;
wl_list_for_each(running_output, &scr_dr->outputs, link) { if (running_output == output) { win_driver->screen_num(i);
break;
}
i++;
}
}
float post_scale = Fl::screen_scale(win_driver->screen_num()) * output->wld_scale;
if (post_scale != pre_scale) {
if (window->kind == Fl_Wayland_Window_Driver::POPUP) {
Fl_Wayland_Graphics_Driver::buffer_release(window);
window->fl_win->redraw();
} else {
win_driver->is_a_rescale(true);
window->fl_win->size(window->fl_win->w(), window->fl_win->h());
win_driver->is_a_rescale(false);
}
}
if (window->fl_win->as_gl_window())
wl_surface_set_buffer_scale(window->wl_surface, output->wld_scale);
}
static void surface_enter(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) {
struct wld_window *window = (struct wld_window*)data;
if (!Fl_Wayland_Screen_Driver::own_output(wl_output))
return;
Fl_Wayland_Screen_Driver::output *output =
(Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output);
if (output == NULL)
return;
Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
float pre_scale = Fl::screen_scale(win_driver->screen_num()) * win_driver->wld_scale();
bool list_was_empty = wl_list_empty(&window->outputs);
struct Fl_Wayland_Window_Driver::surface_output *surface_output =
(struct Fl_Wayland_Window_Driver::surface_output*)malloc(
sizeof(struct Fl_Wayland_Window_Driver::surface_output));
surface_output->output = output;
struct wl_list *e = &window->outputs;
while (e->next != &window->outputs) e = e->next; wl_list_insert(e, &surface_output->link);
if (list_was_empty) {
change_scale(output, window, pre_scale);
}
}
static void surface_leave(void *data, struct wl_surface *wl_surface,
struct wl_output *wl_output) {
if (!Fl_Wayland_Screen_Driver::own_output(wl_output))
return;
struct wld_window *window = (struct wld_window*)data;
Fl_Wayland_Screen_Driver::output *output =
(Fl_Wayland_Screen_Driver::output*)wl_output_get_user_data(wl_output);
Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
float pre_scale = Fl::screen_scale(win_driver->screen_num()) * win_driver->wld_scale();
struct Fl_Wayland_Window_Driver::surface_output *s_output;
int count = 0;
wl_list_for_each(s_output, &window->outputs, link) {
count++;
if (s_output->output == output) {
wl_list_remove(&s_output->link);
free(s_output);
break;
}
}
if (count == 1 && !wl_list_empty(&window->outputs)) {
s_output = wl_container_of(window->outputs.next, s_output, link);
change_scale(s_output->output, window, pre_scale);
}
}
static struct wl_surface_listener surface_listener = {
surface_enter,
surface_leave,
};
Fl_Window *Fl_Wayland_Window_Driver::surface_to_window(struct wl_surface *surface) {
if (surface) {
if (wl_proxy_get_listener((struct wl_proxy *)surface) == &surface_listener) {
return ((struct wld_window *)wl_surface_get_user_data(surface))->fl_win;
}
}
return NULL;
}
static struct Fl_Wayland_Screen_Driver::output *screen_num_to_output(int num_screen) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
int i = 0;
Fl_Wayland_Screen_Driver::output *output;
wl_list_for_each(output, &(scr_driver->outputs), link) { if (i++ == num_screen) return output;
}
return NULL;
}
#define LIBDECOR_MR131 1
#ifdef LIBDECOR_MR131
static bool in_decorated_window_resizing = false;
static void (*decor_xdg_toplevel_configure)(void*, struct xdg_toplevel *, int32_t,
int32_t, struct wl_array *);
static void fltk_xdg_toplevel_configure(void *user_data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states) {
uint32_t *p;
in_decorated_window_resizing = false;
for (p = (uint32_t *)(states)->data;
(const char *) p < ((const char *) (states)->data + (states)->size);
(p)++) {
if (*p == XDG_TOPLEVEL_STATE_RESIZING) {
in_decorated_window_resizing = true;
break;
}
}
decor_xdg_toplevel_configure(user_data, xdg_toplevel, width, height, states);
}
struct wl_object { const struct wl_interface *interface;
const void *implementation;
uint32_t id;
};
static void use_FLTK_toplevel_configure_cb(struct libdecor_frame *frame) {
struct wl_object *object = (struct wl_object *)libdecor_frame_get_xdg_toplevel(frame);
static struct xdg_toplevel_listener *fltk_listener = NULL;
if (!fltk_listener) {
struct xdg_toplevel_listener *decor_listener = (struct xdg_toplevel_listener*)
object->implementation;
fltk_listener = (struct xdg_toplevel_listener*)
malloc(sizeof(struct xdg_toplevel_listener));
*fltk_listener = *decor_listener;
decor_xdg_toplevel_configure = decor_listener->configure;
fltk_listener->configure = fltk_xdg_toplevel_configure;
}
object->implementation = fltk_listener;
}
#endif
static void does_window_cover_parent(Fl_Window *win) {
Fl_Window *parent = win->window();
fl_wl_xid(parent)->covered = (win->x() <= 0 && win->y() <= 0 &&
win->w() >= parent->w() && win->h() >= parent->h());
}
static void scan_subwindows(Fl_Group *g, void (*f)(Fl_Window *)) {
for (int i = 0; i < g->children(); i++) {
Fl_Widget *o = g->child(i);
if (o->as_window()) {
if (!o->as_window()->shown()) continue;
f(o->as_window());
}
if (o->as_group()) scan_subwindows(o->as_group(), f);
}
}
static void handle_configure(struct libdecor_frame *frame,
struct libdecor_configuration *configuration, void *user_data)
{
struct wld_window *window = (struct wld_window*)user_data;
if (!window->wl_surface) return;
int width, height;
enum libdecor_window_state window_state;
struct libdecor_state *state;
Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
bool is_1st_run = (window->xdg_surface == 0);
bool is_2nd_run = (window->xdg_surface != 0 && driver->wait_for_expose_value);
float f = Fl::screen_scale(window->fl_win->screen_num());
if (!window->xdg_surface) window->xdg_surface = libdecor_frame_get_xdg_surface(frame);
#ifdef LIBDECOR_MR131
if (is_1st_run) use_FLTK_toplevel_configure_cb(frame);
#endif
struct wl_output *wl_output = NULL;
if (window->fl_win->fullscreen_active()) {
if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) {
if (Fl_Window_Driver::driver(window->fl_win)->force_position()) {
struct Fl_Wayland_Screen_Driver::output *output =
screen_num_to_output(window->fl_win->screen_num());
if (output) wl_output = output->wl_output;
}
libdecor_frame_set_fullscreen(window->frame, wl_output);
}
} else if (driver->show_iconic()) {
libdecor_frame_set_minimized(window->frame);
driver->show_iconic(0);
}
if (!libdecor_configuration_get_window_state(configuration, &window_state))
window_state = LIBDECOR_WINDOW_STATE_NONE;
if ((window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) &&
!(window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) && !window->fl_win->border()) {
window->fl_win->redraw();
}
window->state = window_state;
if (!libdecor_configuration_get_content_size(configuration, frame, &width, &height)) {
if (is_2nd_run) {
width = window->floating_width;
height = window->floating_height;
if (!driver->is_resizable()) {
libdecor_frame_set_min_content_size(frame, width, height);
libdecor_frame_set_max_content_size(frame, width, height);
}
} else { width = height = 0; }
}
if (is_2nd_run && Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER) {
scan_subwindows(window->fl_win, does_window_cover_parent); }
if (window->fl_win->fullscreen_active() &&
Fl_Window_Driver::driver(window->fl_win)->force_position()) {
int X, Y, W, H;
Fl::screen_xywh(X, Y, W, H, window->fl_win->screen_num());
width = W * f; height = H * f;
}
if (width == 0) {
width = window->floating_width;
height = window->floating_height;
}
#ifndef LIBDECOR_MR131
bool in_decorated_window_resizing = (window->state & LIBDECOR_WINDOW_STATE_RESIZING);
#endif
bool condition = in_decorated_window_resizing;
if (condition) { condition = (window->covered ? (window->buffer && window->buffer->in_use) : (window->frame_cb != NULL));
}
if (condition) {
return;
}
driver->in_handle_configure = true;
window->fl_win->resize(0, 0, ceil(width / f), ceil(height / f));
driver->in_handle_configure = false;
if (wl_output) window->fl_win->redraw();
window->configured_width = ceil(width / f);
window->configured_height = ceil(height / f);
if (is_2nd_run) driver->wait_for_expose_value = 0;
if (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) {
if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::WESTON) {
Fl::handle(FL_FOCUS, window->fl_win);
}
if (!window->fl_win->border()) libdecor_frame_set_visibility(window->frame, false);
else if (!libdecor_frame_is_visible(window->frame)) {
libdecor_frame_set_visibility(window->frame, true);
}
}
if (window->fl_win->border())
driver->is_maximized(window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED);
if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) state = libdecor_state_new(width,
height);
else state = libdecor_state_new(int(ceil(width/f)*f), int(ceil(height/f)*f));
libdecor_frame_commit(frame, state, configuration);
if (libdecor_frame_is_floating(frame)) { window->floating_width = int(ceil(width/f)*f);
window->floating_height = int(ceil(height/f)*f);
}
libdecor_state_free(state);
driver->flush();
if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::WESTON || !is_1st_run) {
window->fl_win->clear_damage();
}
}
void Fl_Wayland_Window_Driver::wait_for_expose()
{
Fl_Window_Driver::wait_for_expose();
struct wld_window * xid = fl_wl_xid(pWindow);
if (!xid) return;
if (pWindow->fullscreen_active()) {
if (xid->kind == DECORATED) {
while (!(xid->state & LIBDECOR_WINDOW_STATE_FULLSCREEN) ||
!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) {
wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
}
} else if (xid->kind == UNFRAMED) {
wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display);
}
} else if (xid->kind == DECORATED) {
if (!(xid->state & LIBDECOR_WINDOW_STATE_ACTIVE)) {
wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
}
}
}
static void delayed_close(Fl_Window *win) {
Fl::remove_check((Fl_Timeout_Handler)delayed_close, win);
Fl::handle(FL_CLOSE, win);
}
static void handle_close(struct libdecor_frame *frame, void *user_data)
{ Fl_Window* win = ((struct wld_window*)user_data)->fl_win;
int X, Y;
libdecor_frame_translate_coordinate(frame, 0, 0, &X, &Y);
if (Y == 0) Fl::handle(FL_CLOSE, win);
else {
Fl::add_check((Fl_Timeout_Handler)delayed_close, win);
}
}
static void handle_commit(struct libdecor_frame *frame, void *user_data)
{
struct wld_window* wl_win = (struct wld_window*)user_data;
if (wl_win->wl_surface) wl_surface_commit(wl_win->wl_surface);
}
static void handle_dismiss_popup(struct libdecor_frame *frame, const char *seat_name, void *user_data)
{
}
static struct libdecor_frame_interface libdecor_frame_iface = {
handle_configure,
handle_close,
handle_commit,
handle_dismiss_popup,
};
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
struct wld_window *window = (struct wld_window*)data;
xdg_surface_ack_configure(xdg_surface, serial);
if (window->fl_win->w() != window->configured_width ||
window->fl_win->h() != window->configured_height) {
if (window->buffer) {
Fl_Wayland_Graphics_Driver::buffer_release(window);
}
}
window->configured_width = window->fl_win->w();
window->configured_height = window->fl_win->h();
Fl_Window_Driver::driver(window->fl_win)->flush();
window->fl_win->clear_damage();
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure,
};
static bool parse_states_fullscreen(struct wl_array *states)
{
uint32_t *p;
for (p = (uint32_t *)(states)->data;
(const char *) p < ((const char *) (states)->data + (states)->size);
(p)++) {
if (*p == XDG_TOPLEVEL_STATE_FULLSCREEN) return true;
}
return false;
}
static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states)
{
struct wld_window *window = (struct wld_window*)data;
if (window->fl_win->fullscreen_active() && !parse_states_fullscreen(states)) {
struct wl_output *wl_output = NULL;
if (Fl_Window_Driver::driver(window->fl_win)->force_position()) {
struct Fl_Wayland_Screen_Driver::output *output =
screen_num_to_output(window->fl_win->screen_num());
if (output) wl_output = output->wl_output;
}
xdg_toplevel_set_fullscreen(xdg_toplevel, wl_output);
if (wl_output) {
int X, Y;
Fl::screen_xywh(X, Y, width, height, window->fl_win->screen_num());
}
}
if (window->configured_width) {
Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0;
}
float f = Fl::screen_scale(window->fl_win->screen_num());
if (width == 0 || height == 0) {
width = window->fl_win->w() * f;
height = window->fl_win->h() * f;
}
window->fl_win->size(ceil(width / f), ceil(height / f));
if (window->buffer && (ceil(width / f) != window->configured_width ||
ceil(height / f) != window->configured_height)) {
Fl_Wayland_Graphics_Driver::buffer_release(window);
}
window->configured_width = ceil(width / f);
window->configured_height = ceil(height / f);
}
static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel)
{
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure,
.close = xdg_toplevel_close,
};
struct win_positioner {
struct wld_window *window;
int x, y;
Fl_Window *child_popup;
};
static void popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y,
int32_t width, int32_t height) {
struct win_positioner *win_pos = (struct win_positioner *)data;
struct wld_window *window = win_pos->window;
Fl_Window_Driver::driver(window->fl_win)->wait_for_expose_value = 0;
int HH;
Fl_Window_Driver::menu_parent(&HH);
if (window->fl_win->h() > HH && y != win_pos->y) { window->state = (y - win_pos->y);
Fl_Window_Driver::scroll_to_selected_item(window->fl_win);
}
if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(window->fl_win)) {
int X, Y;
Fl_Window_Driver::current_menu_button->top_window_offset(X, Y);
if (y < Y) {
Fl_Window *win = window->fl_win;
win->Fl_Widget::resize(win->x(), Y - win->h(), win->w(), win->h());
}
}
}
static struct xdg_popup *mem_grabbing_popup = NULL;
static void popup_done(void *data, struct xdg_popup *xdg_popup) {
struct win_positioner *win_pos = (struct win_positioner *)data;
struct wld_window *window = win_pos->window;
if (win_pos->child_popup) win_pos->child_popup->hide();
xdg_popup_destroy(xdg_popup);
delete win_pos;
window->xdg_popup = NULL;
window->fl_win->hide();
if (mem_grabbing_popup == xdg_popup) {
mem_grabbing_popup = NULL;
}
}
static const struct xdg_popup_listener popup_listener = {
.configure = popup_configure,
.popup_done = popup_done,
};
bool Fl_Wayland_Window_Driver::in_flush_ = false;
static const char *get_prog_name() {
pid_t pid = getpid();
char fname[100];
snprintf(fname, 100, "/proc/%u/cmdline", pid);
FILE *in = fopen(fname, "r");
if (in) {
static char line[200];
const char *p = fgets(line, sizeof(line), in);
fclose(in);
p = strrchr(line, '/'); if (!p) p = line; else p++;
return p;
}
return "unknown";
}
bool Fl_Wayland_Window_Driver::process_menu_or_tooltip(struct wld_window *new_window) {
new_window->kind = Fl_Wayland_Window_Driver::POPUP;
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
if (Fl_Window_Driver::is_floating_title(pWindow)) {
previous_floatingtitle = pWindow;
return true;
}
new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base,
new_window->wl_surface);
xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window);
Fl_Wayland_Window_Driver::new_popup = true;
Fl_Window *menu_origin = NULL;
if (pWindow->menu_window()) {
menu_origin = Fl_Window_Driver::menu_leftorigin(pWindow);
if (!menu_origin && !previous_floatingtitle) menu_origin =
Fl_Window_Driver::menu_title(pWindow);
}
Fl_Widget *target = (pWindow->tooltip_window() ? Fl_Tooltip::current() : NULL);
if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display &&
Fl_Screen_Driver::transient_scale_parent) {
target = Fl_Screen_Driver::transient_scale_parent;
}
if (!target) target = Fl_Window_Driver::menu_parent();
if (!target) target = Fl::belowmouse();
if (!target) target = Fl::first_window();
Fl_Window *parent_win = target->top_window();
while (parent_win && parent_win->menu_window() && driver(parent_win)->popup_window()) {
parent_win = Fl::next_window(parent_win);
}
Fl_Window *origin_win = (menu_origin ? menu_origin : parent_win);
struct wld_window * parent_xid = fl_wl_xid(origin_win);
struct xdg_surface *parent_xdg = parent_xid->xdg_surface;
float f = Fl::screen_scale(parent_win->screen_num());
struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
int popup_x, popup_y;
if (Fl_Window_Driver::current_menu_button && !Fl_Window_Driver::menu_leftorigin(pWindow)) {
int X, Y;
Fl_Window_Driver::current_menu_button->top_window_offset(X, Y);
xdg_positioner_set_anchor_rect(positioner, X * f, Y * f,
Fl_Window_Driver::current_menu_button->w() * f,
Fl_Window_Driver::current_menu_button->h() * f);
popup_x = X * f;
popup_y = 0;
if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active())
libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, &popup_x, &popup_y);
} else if (Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow)) {
xdg_positioner_set_anchor_rect(positioner, 0, 0,
Fl_Window_Driver::menu_title(pWindow)->w() * f,
Fl_Window_Driver::menu_title(pWindow)->h() * f);
popup_x = 0;
popup_y = Fl_Window_Driver::menu_title(pWindow)->h() * f;
} else {
popup_x = pWindow->x() * f; popup_y = pWindow->y() * f;
if (popup_x + pWindow->w() * f < 0) popup_x = - pWindow->w() * f;
if (menu_origin) {
popup_x -= menu_origin->x() * f;
popup_y -= menu_origin->y() * f;
}
if (popup_x >= origin_win->w() * f) popup_x = origin_win->w() * f - 1;
if (!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_bartitle(pWindow) &&
!Fl_Window_Driver::menu_leftorigin(pWindow)) {
popup_y = fl_max(popup_y, - pWindow->h() * f);
}
if (parent_xid->kind == Fl_Wayland_Window_Driver::DECORATED && !origin_win->fullscreen_active())
libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y, &popup_x, &popup_y);
xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1);
popup_y++;
}
xdg_positioner_set_size(positioner, pWindow->w() * f , pWindow->h() * f );
xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);
xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
int constraint = 0;
int top_menubar = pWindow->y() -
(Fl_Window_Driver::menu_bartitle(pWindow) && Fl_Window_Driver::menu_title(pWindow) ?
Fl_Window_Driver::menu_title(pWindow)->h() : 0);
if ( !(parent_win->fullscreen_active() &&
Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::MUTTER &&
((!Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_leftorigin(pWindow)) ||
Fl_Window_Driver::menu_bartitle(pWindow)) && top_menubar < 10 &&
!Fl_Window_Driver::current_menu_button)
) {
constraint |= (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
if ((Fl_Window_Driver::menu_bartitle(pWindow) || Fl_Window_Driver::current_menu_button) && !Fl_Window_Driver::menu_leftorigin(pWindow)) {
constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
}
xdg_positioner_set_constraint_adjustment(positioner, constraint);
}
if (!(Fl_Window_Driver::menu_title(pWindow) && Fl_Window_Driver::menu_bartitle(pWindow))) {
xdg_positioner_set_offset(positioner, 0, popup_y);
}
new_window->xdg_popup = xdg_surface_get_popup(new_window->xdg_surface,
parent_xdg, positioner);
struct win_positioner *win_pos = new struct win_positioner;
win_pos->window = new_window;
win_pos->x = popup_x;
win_pos->y = popup_y;
win_pos->child_popup = NULL;
xdg_positioner_destroy(positioner);
xdg_popup_add_listener(new_window->xdg_popup, &popup_listener, win_pos);
if (!mem_grabbing_popup) {
mem_grabbing_popup = new_window->xdg_popup;
}
wl_surface_commit(new_window->wl_surface);
this->screen_num(parent_win->screen_num());
return false;
}
void Fl_Wayland_Window_Driver::makeWindow()
{
Fl_Group::current(0); struct wld_window *new_window;
bool is_floatingtitle = false;
wait_for_expose_value = 1;
if (pWindow->parent() && !pWindow->window()) return;
if (pWindow->parent() && !pWindow->window()->shown()) return;
if (!pWindow->parent() && !popup_window()) {
x(0); y(0); }
new_window = (struct wld_window *)calloc(1, sizeof *new_window);
new_window->fl_win = pWindow;
wl_list_init(&new_window->outputs);
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
new_window->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
wl_surface_add_listener(new_window->wl_surface, &surface_listener, new_window);
if (!shape()) { struct wl_region *opaque = wl_compositor_create_region(scr_driver->wl_compositor);
wl_region_add(opaque, 0, 0, 1000000, 1000000);
wl_surface_set_opaque_region(new_window->wl_surface, opaque);
wl_region_destroy(opaque);
}
if (pWindow->user_data() == &Fl_Screen_Driver::transient_scale_display &&
Fl::first_window()) {
Fl_Screen_Driver::transient_scale_parent = Fl::first_window();
pWindow->set_tooltip_window();
set_popup_window();
pWindow->position(
(Fl_Screen_Driver::transient_scale_parent->w() - pWindow->w())/2 ,
(Fl_Screen_Driver::transient_scale_parent->h() - pWindow->h())/2);
}
if (popup_window()) { is_floatingtitle = process_menu_or_tooltip(new_window);
} else if (pWindow->border() && !pWindow->parent() ) { new_window->kind = DECORATED;
if (!scr_driver->libdecor_context)
scr_driver->libdecor_context = libdecor_new(Fl_Wayland_Screen_Driver::wl_display,
&libdecor_iface);
new_window->frame = libdecor_decorate(scr_driver->libdecor_context, new_window->wl_surface,
&libdecor_frame_iface, new_window);
libdecor_frame_set_app_id(new_window->frame, get_prog_name());
libdecor_frame_set_title(new_window->frame, pWindow->label()?pWindow->label():"");
if (!is_resizable()) {
libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_RESIZE);
libdecor_frame_unset_capabilities(new_window->frame, LIBDECOR_ACTION_FULLSCREEN);
}
libdecor_frame_map(new_window->frame);
float f = Fl::screen_scale(pWindow->screen_num());
new_window->floating_width = pWindow->w() * f;
new_window->floating_height = pWindow->h() * f;
} else if (pWindow->parent()) { new_window->kind = SUBWINDOW;
struct wld_window *parent = fl_wl_xid(pWindow->window());
new_window->subsurface = wl_subcompositor_get_subsurface(scr_driver->wl_subcompositor,
new_window->wl_surface,
parent->wl_surface);
float f = Fl::screen_scale(pWindow->top_window()->screen_num());
wl_subsurface_set_position(new_window->subsurface, pWindow->x() * f, pWindow->y() * f);
wl_subsurface_set_desync(new_window->subsurface); new_window->configured_width = pWindow->w();
new_window->configured_height = pWindow->h();
if (!pWindow->as_gl_window()) {
parent->fl_win->wait_for_expose();
wl_surface_commit(parent->wl_surface);
}
wait_for_expose_value = 0;
pWindow->border(0);
checkSubwindowFrame();
} else { new_window->kind = UNFRAMED;
new_window->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base,
new_window->wl_surface);
xdg_surface_add_listener(new_window->xdg_surface, &xdg_surface_listener, new_window);
new_window->xdg_toplevel = xdg_surface_get_toplevel(new_window->xdg_surface);
xdg_toplevel_add_listener(new_window->xdg_toplevel, &xdg_toplevel_listener, new_window);
if (pWindow->label()) xdg_toplevel_set_title(new_window->xdg_toplevel, pWindow->label());
wl_surface_commit(new_window->wl_surface);
pWindow->border(0);
}
Fl_Window *old_first = Fl::first_window();
struct wld_window * first_xid = (old_first ? fl_wl_xid(old_first) : NULL);
Fl_X *xp = new Fl_X;
xp->xid = (fl_uintptr_t)new_window;
other_xid = 0;
xp->w = pWindow;
flx(xp);
xp->region = 0;
if (!pWindow->parent()) {
xp->next = Fl_X::first;
Fl_X::first = xp;
} else if (Fl_X::first) {
xp->next = Fl_X::first->next;
Fl_X::first->next = xp;
} else {
xp->next = NULL;
Fl_X::first = xp;
}
if (pWindow->modal() || pWindow->non_modal()) {
if (pWindow->modal()) Fl::modal_ = pWindow;
if (new_window->kind == DECORATED && first_xid && first_xid->kind == DECORATED) {
if (first_xid->frame) libdecor_frame_set_parent(new_window->frame, first_xid->frame);
} else if (new_window->kind == UNFRAMED && new_window->xdg_toplevel && first_xid) {
Fl_Wayland_Window_Driver *top_dr = Fl_Wayland_Window_Driver::driver(first_xid->fl_win);
if (top_dr->xdg_toplevel()) xdg_toplevel_set_parent(new_window->xdg_toplevel,
top_dr->xdg_toplevel());
}
if (scr_driver->seat->gtk_shell && pWindow->modal() &&
(new_window->kind == DECORATED || new_window->kind == UNFRAMED)) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(scr_driver->seat->gtk_shell,
new_window->wl_surface);
gtk_surface1_set_modal(gtk_surface);
if (gtk_surface1_get_version(gtk_surface) >= GTK_SURFACE1_RELEASE_SINCE_VERSION)
gtk_surface1_release(gtk_surface); else
gtk_surface1_destroy(gtk_surface);
}
}
size_range();
pWindow->set_visible();
int old_event = Fl::e_number;
pWindow->redraw();
pWindow->handle(Fl::e_number = FL_SHOW); Fl::e_number = old_event;
if (pWindow->menu_window() && popup_window() && !is_floatingtitle) {
pWindow->wait_for_expose();
if (previous_floatingtitle) { int HH;
Fl_Window_Driver::menu_parent(&HH);
if (pWindow->h() > HH) {
struct wld_window *xid = fl_wl_xid(previous_floatingtitle);
destroy_surface_caution_pointer_focus(xid->wl_surface, scr_driver->seat);
free(xid);
Fl_Window_Driver::driver(previous_floatingtitle)->hide_common();
previous_floatingtitle = NULL;
return;
}
struct wld_window *xid = fl_wl_xid(previous_floatingtitle);
xid->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, xid->wl_surface);
xdg_surface_add_listener(xid->xdg_surface, &xdg_surface_listener, xid);
struct xdg_positioner *positioner =
xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
xdg_positioner_set_anchor_rect(positioner, 0, 0, 1, 1);
int snum = Fl_Window_Driver::menu_parent()->screen_num();
float f = Fl::screen_scale(snum);
Fl_Window_Driver::driver(previous_floatingtitle)->screen_num(snum);
xdg_positioner_set_size(positioner, previous_floatingtitle->w() * f ,
previous_floatingtitle->h() * f );
xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_TOP_RIGHT);
xid->xdg_popup = xdg_surface_get_popup(xid->xdg_surface, new_window->xdg_surface,
positioner);
xdg_positioner_destroy(positioner);
struct win_positioner *win_pos = new struct win_positioner;
win_pos->window = xid;
win_pos->x = 0;
win_pos->y = 0;
win_pos->child_popup = NULL;
xdg_popup_add_listener(xid->xdg_popup, &popup_listener, win_pos);
wl_surface_commit(xid->wl_surface);
struct win_positioner *parent_win_pos =
(struct win_positioner*)xdg_popup_get_user_data(new_window->xdg_popup);
parent_win_pos->child_popup = previous_floatingtitle;
previous_floatingtitle = NULL;
}
}
}
Fl_Wayland_Window_Driver::type_for_resize_window_between_screens Fl_Wayland_Window_Driver::data_for_resize_window_between_screens_ = {0, false};
void Fl_Wayland_Window_Driver::resize_after_screen_change(void *data) {
Fl_Window *win = (Fl_Window*)data;
float f = Fl::screen_driver()->scale(data_for_resize_window_between_screens_.screen);
Fl_Window_Driver::driver(win)->resize_after_scale_change(
data_for_resize_window_between_screens_.screen, f, f);
data_for_resize_window_between_screens_.busy = false;
}
int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow);
if (!scr_driver->seat->cursor_theme) return 1;
static struct cursor_file_struct {
Fl_Cursor c;
const char *fname;
Fl_Wayland_Screen_Driver::cursor_shapes wld_c;
} cursor_file_array[] = {
{FL_CURSOR_ARROW, "left_ptr", Fl_Wayland_Screen_Driver::arrow },
{FL_CURSOR_CROSS, "cross", Fl_Wayland_Screen_Driver::cross },
{FL_CURSOR_WAIT, "watch", Fl_Wayland_Screen_Driver::wait },
{FL_CURSOR_INSERT, "xterm", Fl_Wayland_Screen_Driver::insert },
{FL_CURSOR_HAND, "hand1", Fl_Wayland_Screen_Driver::hand },
{FL_CURSOR_HELP, "help", Fl_Wayland_Screen_Driver::help },
{FL_CURSOR_MOVE, "move", Fl_Wayland_Screen_Driver::move },
{FL_CURSOR_N, "top_side", Fl_Wayland_Screen_Driver::north },
{FL_CURSOR_E, "right_side", Fl_Wayland_Screen_Driver::east },
{FL_CURSOR_W, "left_side", Fl_Wayland_Screen_Driver::west },
{FL_CURSOR_S, "bottom_side", Fl_Wayland_Screen_Driver::south },
{FL_CURSOR_NS, "sb_v_double_arrow", Fl_Wayland_Screen_Driver::north_south },
{FL_CURSOR_WE, "sb_h_double_arrow", Fl_Wayland_Screen_Driver::west_east },
{FL_CURSOR_SW, "bottom_left_corner", Fl_Wayland_Screen_Driver::south_west },
{FL_CURSOR_SE, "bottom_right_corner", Fl_Wayland_Screen_Driver::south_east },
{FL_CURSOR_NE, "top_right_corner", Fl_Wayland_Screen_Driver::north_east },
{FL_CURSOR_NW, "top_left_corner", Fl_Wayland_Screen_Driver::north_west },
{FL_CURSOR_NESW, "fd_double_arrow", Fl_Wayland_Screen_Driver::nesw },
{FL_CURSOR_NWSE, "bd_double_arrow", Fl_Wayland_Screen_Driver::nwse }
};
int found = -1;
for (unsigned i = 0; i < sizeof(cursor_file_array) / sizeof(struct cursor_file_struct); i++) {
if (cursor_file_array[i].c == c) {
found = cursor_file_array[i].wld_c;
if (!scr_driver->xc_cursor[found]) scr_driver->xc_cursor[found] =
scr_driver->cache_cursor(cursor_file_array[i].fname);
if (scr_driver->xc_cursor[found]) {
scr_driver->default_cursor(scr_driver->xc_cursor[found]);
}
break;
}
}
if (found < 0 || !scr_driver->xc_cursor[found]) return 0;
if (xid->custom_cursor) {
delete_cursor(xid->custom_cursor);
xid->custom_cursor = NULL;
}
standard_cursor_ = c;
scr_driver->set_cursor();
return 1;
}
void Fl_Wayland_Window_Driver::use_border() {
if (!shown() || pWindow->parent()) return;
pWindow->wait_for_expose(); struct libdecor_frame *frame = fl_wl_xid(pWindow)->frame;
if (frame && Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::KWIN) {
if (fl_wl_xid(pWindow)->kind == DECORATED) {
libdecor_frame_set_visibility(frame, pWindow->border());
} else {
pWindow->hide();
pWindow->show();
}
pWindow->redraw();
} else {
Fl_Window_Driver::use_border();
}
}
void Fl_Wayland_Window_Driver::fullscreen_on() {
int top, bottom, left, right;
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;
}
pWindow->wait_for_expose(); if (xdg_toplevel()) {
xdg_toplevel_set_fullscreen(xdg_toplevel(), NULL);
pWindow->_set_fullscreen();
Fl::handle(FL_FULLSCREEN, pWindow);
}
}
void Fl_Wayland_Window_Driver::fullscreen_off(int X, int Y, int W, int H) {
if (!border()) pWindow->resize(X, Y, W, H);
xdg_toplevel_unset_fullscreen(xdg_toplevel());
pWindow->_clear_fullscreen();
Fl::handle(FL_FULLSCREEN, pWindow);
}
void Fl_Wayland_Window_Driver::label(const char *name, const char *iname) {
if (shown() && !parent() && fl_wl_xid(pWindow)->kind == DECORATED) {
if (!name) name = "";
if (!iname) iname = fl_filename_name(name);
libdecor_frame_set_title(fl_wl_xid(pWindow)->frame, name);
}
}
int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) {
int retval = set_cursor_4args(rgb, hotx, hoty, true);
if (retval) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow);
Fl_Wayland_Screen_Driver::do_set_cursor(scr_driver->seat, xid->custom_cursor->wl_cursor);
}
return retval;
}
int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx, int hoty,
bool keep_copy) {
if (keep_copy) {
int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d();
uchar *data = new uchar[ld * rgb->data_h()];
memcpy(data, rgb->array, ld * rgb->data_h());
Fl_RGB_Image *rgb2 = new Fl_RGB_Image(data, rgb->data_w(), rgb->data_h(), rgb->d(), rgb->ld());
rgb2->alloc_array = 1;
rgb2->scale(rgb->w(), rgb->h(), 0, 1);
rgb = rgb2;
}
struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow);
struct wl_cursor *new_cursor = (struct wl_cursor*)malloc(sizeof(struct wl_cursor));
struct cursor_image *new_image = (struct cursor_image*)calloc(1,
sizeof(struct cursor_image));
int scale = wld_scale();
new_image->image.width = rgb->w() * scale;
new_image->image.height = rgb->h() * scale;
new_image->image.hotspot_x = hotx * scale;
new_image->image.hotspot_y = hoty * scale;
new_image->image.delay = 0;
new_image->offset = 0;
struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen;
Fl_Image_Surface *img_surf = Fl_Wayland_Graphics_Driver::custom_offscreen(
new_image->image.width, new_image->image.height, &offscreen);
new_image->buffer = offscreen->wl_buffer;
wl_buffer_set_user_data(new_image->buffer, offscreen);
new_cursor->image_count = 1;
new_cursor->images = (struct wl_cursor_image**)malloc(sizeof(struct wl_cursor_image*));
new_cursor->images[0] = (struct wl_cursor_image*)new_image;
new_cursor->name = strdup("custom cursor");
Fl_Surface_Device::push_current(img_surf);
Fl_Wayland_Graphics_Driver *driver = (Fl_Wayland_Graphics_Driver*)img_surf->driver();
cairo_scale(driver->cr(), scale, scale);
((Fl_RGB_Image*)rgb)->draw(0, 0);
Fl_Surface_Device::pop_current();
delete img_surf;
memcpy(offscreen->data, offscreen->draw_buffer.buffer, offscreen->draw_buffer.data_size);
if (xid->custom_cursor) delete_cursor(xid->custom_cursor, keep_copy);
xid->custom_cursor = new custom_cursor;
xid->custom_cursor->wl_cursor = new_cursor;
xid->custom_cursor->rgb = rgb;
xid->custom_cursor->hotx = hotx;
xid->custom_cursor->hoty = hoty;
return 1;
}
void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) {
static int depth = 0;
struct wld_window *fl_win = fl_wl_xid(pWindow);
if (fl_win && fl_win->kind == DECORATED && !xdg_toplevel()) {
pWindow->wait_for_expose();
}
int is_a_move = (X != x() || Y != y());
bool true_rescale = Fl_Window::is_a_rescale();
float f = fl_win ? Fl::screen_scale(pWindow->screen_num()) : 1;
if (fl_win && fl_win->buffer) {
int scale = wld_scale();
int stride = cairo_format_stride_for_width(
Fl_Cairo_Graphics_Driver::cairo_format, int(W * f) * scale );
size_t bsize = stride * int(H * f) * scale;
true_rescale = (bsize != fl_win->buffer->draw_buffer.data_size);
}
int is_a_resize = (W != w() || H != h() || true_rescale);
if (is_a_move) force_position(1);
else if (!is_a_resize && !is_a_move) return;
depth++;
if (shown() && !(parent() || popup_window())) {
X = Y = 0;
}
Fl_Window *parent = this->parent() ? pWindow->window() : NULL;
struct wld_window *parent_xid = parent ? fl_wl_xid(parent) : NULL;
if (depth == 1 && fl_win && parent_xid && parent_xid->frame_cb && can_expand_outside_parent_) {
depth--;
return;
}
if (is_a_resize) {
if (pWindow->parent()) {
if (W < 1) W = 1;
if (H < 1) H = 1;
}
pWindow->Fl_Group::resize(X,Y,W,H);
if (shown()) {pWindow->redraw();}
} else {
x(X); y(Y);
}
if (!fl_win) {
depth--;
return;
}
if (is_a_resize) {
if (pWindow->as_overlay_window() && other_xid) {
destroy_double_buffer();
}
if (fl_win->kind == DECORATED) { if (fl_win->buffer) {
Fl_Wayland_Graphics_Driver::buffer_release(fl_win);
}
fl_win->configured_width = W;
fl_win->configured_height = H;
if (!in_handle_configure && xdg_toplevel()) {
if (Fl_Window::is_a_rescale()) size_range();
struct libdecor_state *state = libdecor_state_new(int(W * f), int(H * f));
libdecor_frame_commit(fl_win->frame, state, NULL);
libdecor_state_free(state);
if (libdecor_frame_is_floating(fl_win->frame)) {
fl_win->floating_width = int(W*f);
fl_win->floating_height = int(H*f);
}
}
} else if (fl_win->kind == SUBWINDOW && fl_win->subsurface) { wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f);
if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win);
fl_win->configured_width = W;
fl_win->configured_height = H;
} else if (fl_win->xdg_surface) { if (!pWindow->as_gl_window()) Fl_Wayland_Graphics_Driver::buffer_release(fl_win);
fl_win->configured_width = W;
fl_win->configured_height = H;
W *= f; H *= f;
if (!pWindow->fullscreen_active()) {
xdg_toplevel_set_min_size(fl_win->xdg_toplevel, W, H);
xdg_toplevel_set_max_size(fl_win->xdg_toplevel, W, H);
}
xdg_surface_set_window_geometry(fl_win->xdg_surface, 0, 0, W, H);
}
} else if (!in_handle_configure && xdg_toplevel() && Fl::e_state == FL_BUTTON1) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
xdg_toplevel_move(xdg_toplevel(), scr_driver->seat->wl_seat, scr_driver->seat->serial);
Fl::pushed(NULL);
Fl::e_state = 0;
}
if (parent_xid) {
if (depth > 1) {
if (fl_win->subsurface) {
wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f);
wl_surface_commit(parent_xid->wl_surface);
}
} else if (parent_xid->buffer && is_a_move) {
if (fl_win->subsurface) wl_subsurface_set_position(fl_win->subsurface, X * f, Y * f);
if (!parent_xid->buffer->wl_buffer || parent_xid->buffer->draw_buffer_needs_commit) {
if (!parent_xid->frame_cb) Fl_Wayland_Graphics_Driver::buffer_commit(parent_xid);
else wl_surface_commit(parent_xid->wl_surface);
} else {
if (!parent_xid->frame_cb) {
parent_xid->frame_cb = wl_surface_frame(parent_xid->wl_surface);
wl_callback_add_listener(parent_xid->frame_cb,
Fl_Wayland_Graphics_Driver::p_surface_frame_listener, parent_xid);
}
wl_surface_commit(parent_xid->wl_surface);
}
}
checkSubwindowFrame(); }
depth--;
}
static void crect_intersect(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) {
int x = fl_max(to->x, with->x);
to->width = fl_min(to->x + to->width, with->x + with->width) - x;
if (to->width < 0) to->width = 0;
int y = fl_max(to->y, with->y);
to->height = fl_min(to->y + to->height, with->y + with->height) - y;
if (to->height < 0) to->height = 0;
to->x = x;
to->y = y;
}
static bool crect_equal(cairo_rectangle_int_t *to, cairo_rectangle_int_t *with) {
return (to->x == with->x && to->y == with->y && to->width == with->width &&
to->height == with->height);
}
void Fl_Wayland_Window_Driver::checkSubwindowFrame() {
if (!pWindow->parent() || can_expand_outside_parent_) return;
Fl_Window *from = pWindow, *parent;
cairo_rectangle_int_t full = {0, 0, pWindow->w(), pWindow->h()}; cairo_rectangle_int_t srect = full; int fromx = 0, fromy = 0;
while ((parent = from->window()) != NULL) { fromx -= from->x(); fromy -= from->y();
cairo_rectangle_int_t prect = {fromx, fromy, parent->w(), parent->h()};
crect_intersect(&srect, &prect); from = parent;
}
cairo_rectangle_int_t *r = subRect();
cairo_rectangle_int_t current_clip = (r ? *r : full);
if (!crect_equal(&srect, ¤t_clip)) { if (crect_equal(&srect, &full)) r = NULL;
else {
r = &srect;
if (r->width == 0 || r->height == 0) {
r = NULL;
}
}
subRect(r);
}
}
void Fl_Wayland_Window_Driver::subRect(cairo_rectangle_int_t *r) {
if (subRect_) delete subRect_;
cairo_rectangle_int_t *r2 = NULL;
if (r) {
r2 = new cairo_rectangle_int_t;
*r2 = *r;
}
subRect_ = r2;
}
void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) {
if (y == pWindow->y()) return;
if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KWIN) {
*Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y());
struct wld_window *xid = fl_wl_xid(pWindow);
wl_surface_set_opaque_region(xid->wl_surface, NULL);
if (xid->buffer) memset(xid->buffer->draw_buffer.buffer, 0,
xid->buffer->draw_buffer.data_size);
this->y(y);
pWindow->redraw();
return;
}
struct wld_window * xid_menu = fl_wl_xid(pWindow);
struct xdg_popup *old_popup = xid_menu->xdg_popup;
struct xdg_surface *old_xdg = xid_menu->xdg_surface;
struct wl_surface *old_surface = xid_menu->wl_surface;
Fl_Window *menu_origin = Fl_Window_Driver::menu_title(pWindow);
if (!menu_origin) menu_origin = Fl_Window_Driver::menu_leftorigin(pWindow);
if (!menu_origin) menu_origin = Fl_Window_Driver::menu_parent();
if (Fl_Window_Driver::menu_title(pWindow) && !Fl_Window_Driver::menu_bartitle(pWindow) &&
!Fl_Window_Driver::menu_leftorigin(pWindow)) {
menu_origin = Fl_Window_Driver::menu_parent();
}
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
xid_menu->wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
wl_surface_add_listener(xid_menu->wl_surface, &surface_listener, xid_menu);
xid_menu->xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base,
xid_menu->wl_surface);
xdg_surface_add_listener(xid_menu->xdg_surface, &xdg_surface_listener, xid_menu);
struct xdg_positioner *positioner = xdg_wm_base_create_positioner(scr_driver->xdg_wm_base);
struct wld_window * parent_xid = fl_wl_xid(menu_origin);
float f = Fl::screen_scale(Fl_Window_Driver::menu_parent()->screen_num());
int popup_x = x * f, popup_y = y * f + xid_menu->state;
if (menu_origin->menu_window() && driver(menu_origin)->popup_window()) {
popup_x -= menu_origin->x() * f;
popup_y -= menu_origin->y() * f;
}
if (popup_x >= menu_origin->w() * f) popup_x = menu_origin->w() * f - 1;
if (parent_xid->kind == DECORATED) {
libdecor_frame_translate_coordinate(parent_xid->frame, popup_x, popup_y,
&popup_x, &popup_y);
}
xdg_positioner_set_anchor_rect(positioner, popup_x, 0, 1, 1);
xdg_positioner_set_size(positioner, pWindow->w() * f , pWindow->h() * f );
xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
xdg_positioner_set_constraint_adjustment(positioner,
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X);
xdg_positioner_set_offset(positioner, 0, popup_y);
xid_menu->xdg_popup = xdg_surface_get_popup(xid_menu->xdg_surface, parent_xid->xdg_surface,
positioner);
xdg_positioner_destroy(positioner);
struct win_positioner *win_pos = new struct win_positioner;
win_pos->window = xid_menu;
win_pos->x = popup_x;
win_pos->y = popup_y;
win_pos->child_popup = NULL;
xdg_popup_add_listener(xid_menu->xdg_popup, &popup_listener, win_pos);
wl_surface_commit(xid_menu->wl_surface);
wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); struct win_positioner *old_win_pos =
(struct win_positioner*)xdg_popup_get_user_data(old_popup);
xdg_popup_destroy(old_popup);
delete old_win_pos;
xdg_surface_destroy(old_xdg);
destroy_surface_caution_pointer_focus(old_surface, scr_driver->seat);
this->y(y);
}
void Fl_Wayland_Window_Driver::menu_window_area(int &X, int &Y, int &W, int &H, int nscreen) {
int HH;
Fl_Window *parent = Fl_Window_Driver::menu_parent(&HH);
if (parent) {
if (pWindow->menu_window() && popup_window() && pWindow->h() > HH) {
int ih = Fl_Window_Driver::menu_itemheight(pWindow);
X = -50000;
W = 1000000;
H = HH - 2 * ih;
Fl_Window *origin = Fl_Window_Driver::menu_leftorigin(pWindow);
if (origin) { int selected = fl_max(Fl_Window_Driver::menu_selected(origin), 0);
Y = origin->y() + (selected + 0.5) * ih;
} else if (!Fl_Window_Driver::menu_bartitle(pWindow)) { static int y_offset = 0;
if (new_popup) {
y_offset = pWindow->y()- ih;
new_popup = false;
}
Y = 1.5 * ih + y_offset;
} else { Y = 1.5 * ih;
}
} else { X = -50000;
Y = -50000;
W = 1000000;
H = 1000000;
}
} else Fl_Window_Driver::menu_window_area(X, Y, W, H, nscreen);
}
int Fl_Wayland_Window_Driver::wld_scale() {
Fl_X *flx = Fl_X::flx(pWindow);
struct wld_window *xid = (flx ? (struct wld_window *)flx->xid : NULL);
if (!xid || wl_list_empty(&xid->outputs)) {
int scale = 1;
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
Fl_Wayland_Screen_Driver::output *output;
wl_list_for_each(output, &(scr_driver->outputs), link) {
scale = fl_max(scale, output->wld_scale);
}
return scale;
}
struct surface_output *s_output;
s_output = wl_container_of(xid->outputs.next, s_output, link);
return s_output->output->wld_scale;
}
FL_EXPORT struct wl_surface *fl_wl_surface(struct wld_window *xid) {
return xid->wl_surface;
}
cairo_t *fl_wl_gc() {
return ((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->cr();
}
Fl_Window *fl_wl_find(struct wld_window *xid) {
return Fl_Window_Driver::find((fl_uintptr_t)xid);
}
struct wld_window *fl_wl_xid(const Fl_Window *win) {
return (struct wld_window *)Fl_Window_Driver::xid(win);
}
struct wl_compositor *fl_wl_compositor() {
Fl_Wayland_Screen_Driver *screen_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
return screen_driver->wl_compositor;
}
int fl_wl_buffer_scale(Fl_Window *window) {
return Fl_Wayland_Window_Driver::driver(window)->wld_scale();
}
Fl_Wayland_Plugin *Fl_Wayland_Window_Driver::gl_plugin() {
static Fl_Wayland_Plugin *plugin = NULL;
if (!plugin) {
Fl_Plugin_Manager pm("wayland.fltk.org");
plugin = (Fl_Wayland_Plugin*)pm.plugin("gl.wayland.fltk.org");
}
return plugin;
}
void Fl_Wayland_Window_Driver::maximize() {
struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid;
if (xid->kind == DECORATED) libdecor_frame_set_maximized(xid->frame);
else Fl_Window_Driver::maximize();
}
void Fl_Wayland_Window_Driver::un_maximize() {
struct wld_window *xid = (struct wld_window *)Fl_X::flx(pWindow)->xid;
if (xid->kind == DECORATED) libdecor_frame_unset_maximized(xid->frame);
else Fl_Window_Driver::un_maximize();
}