mirror of https://github.com/FreeRDP/FreeRDP
xfreerdp: remove backbuffer window in RemoteApp mode
This commit is contained in:
parent
2aa3d6f731
commit
3fa7d4a3d4
|
@ -29,18 +29,37 @@ void xf_send_mouse_motion_event(rdpInput* input, boolean down, uint32 button, ui
|
||||||
|
|
||||||
boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app)
|
boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
{
|
{
|
||||||
int x;
|
int x, y;
|
||||||
int y;
|
int cx, cy;
|
||||||
int cx;
|
|
||||||
int cy;
|
|
||||||
|
|
||||||
if (event->xexpose.window == xfi->window->handle)
|
if (app != True)
|
||||||
{
|
{
|
||||||
x = event->xexpose.x;
|
x = event->xexpose.x;
|
||||||
y = event->xexpose.y;
|
y = event->xexpose.y;
|
||||||
cx = event->xexpose.width;
|
cx = event->xexpose.width;
|
||||||
cy = event->xexpose.height;
|
cy = event->xexpose.height;
|
||||||
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc_default, x, y, cx, cy, x, y);
|
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, cx, cy, x, y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xfWindow* xfw;
|
||||||
|
rdpWindow* window;
|
||||||
|
|
||||||
|
window = window_list_get_by_extra_id(xfi->rail->list, (void*) event->xany.window);
|
||||||
|
|
||||||
|
if (window != NULL)
|
||||||
|
{
|
||||||
|
xfw = (xfWindow*) window->extra;
|
||||||
|
|
||||||
|
XPutImage(xfi->display, xfi->primary, xfw->gc, xfi->image,
|
||||||
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
|
window->windowWidth, window->windowHeight);
|
||||||
|
|
||||||
|
XCopyArea(xfi->display, xfi->primary, xfw->handle, xfw->gc,
|
||||||
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
|
window->windowWidth, window->windowHeight, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
@ -48,7 +67,7 @@ boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
|
|
||||||
boolean xf_event_VisibilityNotify(xfInfo* xfi, XEvent* event, boolean app)
|
boolean xf_event_VisibilityNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
{
|
{
|
||||||
if (event->xvisibility.window == xfi->window->handle)
|
if (app != True)
|
||||||
xfi->unobscured = event->xvisibility.state == VisibilityUnobscured;
|
xfi->unobscured = event->xvisibility.state == VisibilityUnobscured;
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
|
@ -60,7 +79,7 @@ boolean xf_event_MotionNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
|
|
||||||
input = xfi->instance->input;
|
input = xfi->instance->input;
|
||||||
|
|
||||||
if (event->xmotion.window == xfi->window->handle)
|
if (app != True)
|
||||||
{
|
{
|
||||||
if (xfi->mouse_motion != True)
|
if (xfi->mouse_motion != True)
|
||||||
{
|
{
|
||||||
|
@ -69,10 +88,10 @@ boolean xf_event_MotionNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
}
|
}
|
||||||
|
|
||||||
input->MouseEvent(input, PTR_FLAGS_MOVE, event->xmotion.x, event->xmotion.y);
|
input->MouseEvent(input, PTR_FLAGS_MOVE, event->xmotion.x, event->xmotion.y);
|
||||||
}
|
|
||||||
|
|
||||||
if (xfi->fullscreen)
|
if (xfi->fullscreen)
|
||||||
XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
|
XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
@ -258,7 +277,7 @@ boolean xf_event_FocusIn(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
|
|
||||||
xfi->focused = True;
|
xfi->focused = True;
|
||||||
|
|
||||||
if (xfi->mouse_active)
|
if (xfi->mouse_active && (app != True))
|
||||||
XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||||
|
|
||||||
xf_kbd_focus_in(xfi);
|
xf_kbd_focus_in(xfi);
|
||||||
|
@ -309,13 +328,16 @@ boolean xf_event_ClientMessage(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
|
|
||||||
boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app)
|
boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
{
|
{
|
||||||
xfi->mouse_active = True;
|
if (app != True)
|
||||||
|
{
|
||||||
|
xfi->mouse_active = True;
|
||||||
|
|
||||||
if (xfi->fullscreen)
|
if (xfi->fullscreen)
|
||||||
XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
|
XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
|
||||||
|
|
||||||
if (xfi->focused)
|
if (xfi->focused)
|
||||||
XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
@ -328,14 +350,26 @@ boolean xf_event_LeaveNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app)
|
||||||
|
{
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
boolean xf_event_process(freerdp* instance, XEvent* event)
|
boolean xf_event_process(freerdp* instance, XEvent* event)
|
||||||
{
|
{
|
||||||
boolean app = False;
|
boolean app = False;
|
||||||
boolean status = True;
|
boolean status = True;
|
||||||
xfInfo* xfi = GET_XFI(instance);
|
xfInfo* xfi = GET_XFI(instance);
|
||||||
|
|
||||||
if (event->xany.window != xfi->window->handle)
|
if (xfi->remote_app == True)
|
||||||
|
{
|
||||||
app = True;
|
app = True;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (event->xany.window != xfi->window->handle)
|
||||||
|
app = True;
|
||||||
|
}
|
||||||
|
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
|
@ -390,6 +424,7 @@ boolean xf_event_process(freerdp* instance, XEvent* event)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigureNotify:
|
case ConfigureNotify:
|
||||||
|
status = xf_event_ConfigureNotify(xfi, event, app);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MapNotify:
|
case MapNotify:
|
||||||
|
|
|
@ -103,11 +103,14 @@ void xf_kbd_send_key(xfInfo* xfi, boolean down, uint8 keycode)
|
||||||
int xf_kbd_read_keyboard_state(xfInfo* xfi)
|
int xf_kbd_read_keyboard_state(xfInfo* xfi)
|
||||||
{
|
{
|
||||||
int dummy;
|
int dummy;
|
||||||
uint32 state;
|
|
||||||
Window wdummy;
|
Window wdummy;
|
||||||
|
uint32 state = 0;
|
||||||
|
|
||||||
XQueryPointer(xfi->display, xfi->window->handle,
|
if (xfi->remote_app != True)
|
||||||
|
{
|
||||||
|
XQueryPointer(xfi->display, xfi->window->handle,
|
||||||
&wdummy, &wdummy, &dummy, &dummy, &dummy, &dummy, &state);
|
&wdummy, &wdummy, &dummy, &dummy, &dummy, &dummy, &state);
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,14 @@ void xf_rail_paint(xfInfo* xfi, rdpRail* rail)
|
||||||
window = window_list_get_next(rail->list);
|
window = window_list_get_next(rail->list);
|
||||||
xfw = (xfWindow*) window->extra;
|
xfw = (xfWindow*) window->extra;
|
||||||
|
|
||||||
//printf("painting window 0x%08X\n", window->windowId);
|
XPutImage(xfi->display, xfi->primary, xfw->gc, xfi->image,
|
||||||
|
|
||||||
XCopyArea(xfi->display, xfi->window->handle, xfw->handle, xfw->gc,
|
|
||||||
window->windowOffsetX, window->windowOffsetY,
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
window->windowWidth, window->windowHeight,
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
0, 0);
|
window->windowWidth, window->windowHeight);
|
||||||
|
|
||||||
|
XCopyArea(xfi->display, xfi->primary, xfw->handle, xfw->gc,
|
||||||
|
window->windowOffsetX, window->windowOffsetY,
|
||||||
|
window->windowWidth, window->windowHeight, 0, 0);
|
||||||
|
|
||||||
XFlush(xfi->display);
|
XFlush(xfi->display);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +75,8 @@ void xf_rail_MoveWindow(rdpRail* rail, rdpWindow* window)
|
||||||
window->windowOffsetX + xfi->workArea.x,
|
window->windowOffsetX + xfi->workArea.x,
|
||||||
window->windowOffsetY + xfi->workArea.y,
|
window->windowOffsetY + xfi->workArea.y,
|
||||||
window->windowWidth, window->windowHeight);
|
window->windowWidth, window->windowHeight);
|
||||||
|
|
||||||
|
XFlush(xfi->display);
|
||||||
}
|
}
|
||||||
|
|
||||||
void xf_rail_DestroyWindow(rdpRail* rail, rdpWindow* window)
|
void xf_rail_DestroyWindow(rdpRail* rail, rdpWindow* window)
|
||||||
|
|
|
@ -116,8 +116,6 @@ boolean window_GetWorkArea(xfInfo* xfi)
|
||||||
xfi->workArea.height = plong[xfi->current_desktop * 4 + 3];
|
xfi->workArea.height = plong[xfi->current_desktop * 4 + 3];
|
||||||
xfree(prop);
|
xfree(prop);
|
||||||
|
|
||||||
printf("x:%d y:%d w:%d h:%d\n", xfi->workArea.x, xfi->workArea.y, xfi->workArea.width, xfi->workArea.height);
|
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +146,7 @@ void window_show_decorations(xfInfo* xfi, xfWindow* window, boolean show)
|
||||||
window->decorations = show;
|
window->decorations = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
xfWindow* window_create(xfInfo* xfi, char* name)
|
xfWindow* desktop_create(xfInfo* xfi, char* name)
|
||||||
{
|
{
|
||||||
xfWindow* window;
|
xfWindow* window;
|
||||||
|
|
||||||
|
@ -272,6 +270,7 @@ xfWindow* xf_CreateWindow(xfInfo* xfi, int x, int y, int width, int height, char
|
||||||
|
|
||||||
void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height)
|
void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
|
XSizeHints* size_hints;
|
||||||
XWindowChanges changes;
|
XWindowChanges changes;
|
||||||
|
|
||||||
changes.x = x;
|
changes.x = x;
|
||||||
|
@ -280,6 +279,22 @@ void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int h
|
||||||
changes.height = height;
|
changes.height = height;
|
||||||
|
|
||||||
XConfigureWindow(xfi->display, window->handle, CWX | CWY | CWWidth | CWHeight, &changes);
|
XConfigureWindow(xfi->display, window->handle, CWX | CWY | CWWidth | CWHeight, &changes);
|
||||||
|
|
||||||
|
window->width = width;
|
||||||
|
window->height = height;
|
||||||
|
XFreePixmap(xfi->display, window->surface);
|
||||||
|
window->surface = XCreatePixmap(xfi->display, window->handle, window->width, window->height, xfi->depth);
|
||||||
|
|
||||||
|
size_hints = XAllocSizeHints();
|
||||||
|
|
||||||
|
if (size_hints)
|
||||||
|
{
|
||||||
|
size_hints->flags = PMinSize | PMaxSize;
|
||||||
|
size_hints->min_width = size_hints->max_width = window->width;
|
||||||
|
size_hints->min_height = size_hints->max_height = window->height;
|
||||||
|
XSetWMNormalHints(xfi->display, window->handle, size_hints);
|
||||||
|
XFree(size_hints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void xf_DestroyWindow(xfInfo* xfi, xfWindow* window)
|
void xf_DestroyWindow(xfInfo* xfi, xfWindow* window)
|
||||||
|
|
|
@ -45,7 +45,7 @@ boolean window_GetWorkArea(xfInfo* xfi);
|
||||||
void window_fullscreen(xfInfo* xfi, xfWindow* window, boolean fullscreen);
|
void window_fullscreen(xfInfo* xfi, xfWindow* window, boolean fullscreen);
|
||||||
void window_show_decorations(xfInfo* xfi, xfWindow* window, boolean show);
|
void window_show_decorations(xfInfo* xfi, xfWindow* window, boolean show);
|
||||||
|
|
||||||
xfWindow* window_create(xfInfo* xfi, char* name);
|
xfWindow* desktop_create(xfInfo* xfi, char* name);
|
||||||
|
|
||||||
xfWindow* xf_CreateWindow(xfInfo* xfi, int x, int y, int width, int height, char* name);
|
xfWindow* xf_CreateWindow(xfInfo* xfi, int x, int y, int width, int height, char* name);
|
||||||
void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height);
|
void xf_MoveWindow(xfInfo* xfi, xfWindow* window, int x, int y, int width, int height);
|
||||||
|
|
|
@ -71,12 +71,16 @@ void xf_end_paint(rdpUpdate* update)
|
||||||
w = gdi->primary->hdc->hwnd->invalid->w;
|
w = gdi->primary->hdc->hwnd->invalid->w;
|
||||||
h = gdi->primary->hdc->hwnd->invalid->h;
|
h = gdi->primary->hdc->hwnd->invalid->h;
|
||||||
|
|
||||||
XPutImage(xfi->display, xfi->primary, xfi->gc_default, xfi->image, x, y, x, y, w, h);
|
if (xfi->remote_app != True)
|
||||||
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc_default, x, y, w, h, x, y);
|
{
|
||||||
XFlush(xfi->display);
|
XPutImage(xfi->display, xfi->primary, xfi->gc, xfi->image, x, y, x, y, w, h);
|
||||||
|
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
|
||||||
if (xfi->remote_app == True)
|
XFlush(xfi->display);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
xf_rail_paint(xfi, update->rail);
|
xf_rail_paint(xfi, update->rail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean xf_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount)
|
boolean xf_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount)
|
||||||
|
@ -283,39 +287,35 @@ boolean xf_post_connect(freerdp* instance)
|
||||||
xfi->attribs.override_redirect = xfi->fullscreen;
|
xfi->attribs.override_redirect = xfi->fullscreen;
|
||||||
xfi->attribs.colormap = xfi->colormap;
|
xfi->attribs.colormap = xfi->colormap;
|
||||||
|
|
||||||
xfi->window = window_create(xfi, "xfreerdp");
|
if (xfi->remote_app != True)
|
||||||
|
|
||||||
window_show_decorations(xfi, xfi->window, xfi->decoration);
|
|
||||||
window_fullscreen(xfi, xfi->window, xfi->fullscreen);
|
|
||||||
|
|
||||||
/* wait for VisibilityNotify */
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
XMaskEvent(xfi->display, VisibilityChangeMask, &xevent);
|
xfi->window = desktop_create(xfi, "xfreerdp");
|
||||||
|
|
||||||
|
window_show_decorations(xfi, xfi->window, xfi->decoration);
|
||||||
|
window_fullscreen(xfi, xfi->window, xfi->fullscreen);
|
||||||
|
|
||||||
|
/* wait for VisibilityNotify */
|
||||||
|
do
|
||||||
|
{
|
||||||
|
XMaskEvent(xfi->display, VisibilityChangeMask, &xevent);
|
||||||
|
}
|
||||||
|
while (xevent.type != VisibilityNotify);
|
||||||
|
|
||||||
|
xfi->unobscured = (xevent.xvisibility.state == VisibilityUnobscured);
|
||||||
|
|
||||||
|
protocol_atom = XInternAtom(xfi->display, "WM_PROTOCOLS", True);
|
||||||
|
kill_atom = XInternAtom(xfi->display, "WM_DELETE_WINDOW", True);
|
||||||
|
XSetWMProtocols(xfi->display, xfi->window->handle, &kill_atom, 1);
|
||||||
}
|
}
|
||||||
while (xevent.type != VisibilityNotify);
|
|
||||||
|
|
||||||
xfi->unobscured = (xevent.xvisibility.state == VisibilityUnobscured);
|
|
||||||
memset(&gcv, 0, sizeof(gcv));
|
memset(&gcv, 0, sizeof(gcv));
|
||||||
|
xfi->modifier_map = XGetModifierMapping(xfi->display);
|
||||||
|
|
||||||
protocol_atom = XInternAtom(xfi->display, "WM_PROTOCOLS", True);
|
xfi->gc = XCreateGC(xfi->display, DefaultRootWindow(xfi->display), GCGraphicsExposures, &gcv);
|
||||||
kill_atom = XInternAtom(xfi->display, "WM_DELETE_WINDOW", True);
|
xfi->primary = XCreatePixmap(xfi->display, DefaultRootWindow(xfi->display), xfi->width, xfi->height, xfi->depth);
|
||||||
XSetWMProtocols(xfi->display, xfi->window->handle, &kill_atom, 1);
|
|
||||||
|
|
||||||
if (!xfi->gc)
|
|
||||||
xfi->gc = XCreateGC(xfi->display, xfi->window->handle, GCGraphicsExposures, &gcv);
|
|
||||||
|
|
||||||
if (!xfi->primary)
|
|
||||||
xfi->primary = XCreatePixmap(xfi->display, xfi->window->handle, xfi->width, xfi->height, xfi->depth);
|
|
||||||
|
|
||||||
xfi->drawing = xfi->primary;
|
|
||||||
|
|
||||||
xfi->bitmap_mono = XCreatePixmap(xfi->display, xfi->window->handle, 8, 8, 1);
|
|
||||||
xfi->gc_mono = XCreateGC(xfi->display, xfi->bitmap_mono, GCGraphicsExposures, &gcv);
|
|
||||||
xfi->gc_default = XCreateGC(xfi->display, xfi->window->handle, GCGraphicsExposures, &gcv);
|
|
||||||
XSetForeground(xfi->display, xfi->gc, BlackPixelOfScreen(xfi->screen));
|
XSetForeground(xfi->display, xfi->gc, BlackPixelOfScreen(xfi->screen));
|
||||||
XFillRectangle(xfi->display, xfi->primary, xfi->gc, 0, 0, xfi->width, xfi->height);
|
XFillRectangle(xfi->display, xfi->primary, xfi->gc, 0, 0, xfi->width, xfi->height);
|
||||||
xfi->modifier_map = XGetModifierMapping(xfi->display);
|
|
||||||
|
|
||||||
xfi->image = XCreateImage(xfi->display, xfi->visual, xfi->depth, ZPixmap, 0,
|
xfi->image = XCreateImage(xfi->display, xfi->visual, xfi->depth, ZPixmap, 0,
|
||||||
(char*) gdi->primary_buffer, gdi->width, gdi->height, xfi->scanline_pad, 0);
|
(char*) gdi->primary_buffer, gdi->width, gdi->height, xfi->scanline_pad, 0);
|
||||||
|
@ -399,16 +399,6 @@ void xf_window_free(xfInfo* xfi)
|
||||||
XFreeModifiermap(xfi->modifier_map);
|
XFreeModifiermap(xfi->modifier_map);
|
||||||
xfi->modifier_map = 0;
|
xfi->modifier_map = 0;
|
||||||
|
|
||||||
XFreeGC(xfi->display, xfi->gc_default);
|
|
||||||
xfi->gc_default = 0;
|
|
||||||
|
|
||||||
XFreeGC(xfi->display, xfi->gc_mono);
|
|
||||||
xfi->gc_mono = 0;
|
|
||||||
|
|
||||||
/* Note: valgrind reports this at lost no matter what */
|
|
||||||
XFreePixmap(xfi->display, xfi->bitmap_mono);
|
|
||||||
xfi->bitmap_mono = 0;
|
|
||||||
|
|
||||||
XFreeGC(xfi->display, xfi->gc);
|
XFreeGC(xfi->display, xfi->gc);
|
||||||
xfi->gc = 0;
|
xfi->gc = 0;
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ struct xf_info
|
||||||
Screen* screen;
|
Screen* screen;
|
||||||
XImage* image;
|
XImage* image;
|
||||||
Pixmap primary;
|
Pixmap primary;
|
||||||
Drawable drawing;
|
|
||||||
Visual* visual;
|
Visual* visual;
|
||||||
Display* display;
|
Display* display;
|
||||||
Colormap colormap;
|
Colormap colormap;
|
||||||
|
@ -74,10 +73,6 @@ struct xf_info
|
||||||
boolean remote_app;
|
boolean remote_app;
|
||||||
rdpRail* rail;
|
rdpRail* rail;
|
||||||
|
|
||||||
GC gc_mono;
|
|
||||||
GC gc_default;
|
|
||||||
Pixmap bitmap_mono;
|
|
||||||
|
|
||||||
boolean focused;
|
boolean focused;
|
||||||
boolean mouse_active;
|
boolean mouse_active;
|
||||||
boolean mouse_motion;
|
boolean mouse_motion;
|
||||||
|
|
Loading…
Reference in New Issue