Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
|
59994f9783 | ||
|
747df23103 | ||
|
4e7b53f21e | ||
|
3a63b34fa9 | ||
|
c2d43a0b32 | ||
|
5220245016 | ||
|
cc5a3ea530 |
9 changed files with 143 additions and 30 deletions
17
debian/control
vendored
17
debian/control
vendored
|
@ -13,8 +13,6 @@ Build-Depends: debhelper (>= 9),
|
||||||
dpkg-dev (>= 1.16.1~),
|
dpkg-dev (>= 1.16.1~),
|
||||||
fcitx-libs-dev [linux-any],
|
fcitx-libs-dev [linux-any],
|
||||||
libasound2-dev [linux-any],
|
libasound2-dev [linux-any],
|
||||||
libgl1-mesa-dev,
|
|
||||||
libpulse-dev,
|
|
||||||
libudev-dev [linux-any],
|
libudev-dev [linux-any],
|
||||||
libdbus-1-dev [linux-any],
|
libdbus-1-dev [linux-any],
|
||||||
libibus-1.0-dev[linux-any],
|
libibus-1.0-dev[linux-any],
|
||||||
|
@ -30,16 +28,16 @@ Build-Depends: debhelper (>= 9),
|
||||||
libxxf86vm-dev
|
libxxf86vm-dev
|
||||||
Homepage: http://www.libsdl.org/
|
Homepage: http://www.libsdl.org/
|
||||||
|
|
||||||
Package: libsdl2
|
Package: libsdl2-2.0-0
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Pre-Depends: ${misc:Pre-Depends}
|
Pre-Depends: ${misc:Pre-Depends}
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
libudev0 [linux-any],
|
libudev0 | libudev1 [linux-any],
|
||||||
libdbus-1-3 [linux-any]
|
libdbus-1-3 [linux-any]
|
||||||
Conflicts: libsdl-1.3-0
|
Conflicts: libsdl-1.3-0, libsdl2
|
||||||
Replaces: libsdl-1.3-0
|
Replaces: libsdl-1.3-0, libsdl2
|
||||||
Description: Simple DirectMedia Layer
|
Description: Simple DirectMedia Layer
|
||||||
SDL is a library that allows programs portable low level access to
|
SDL is a library that allows programs portable low level access to
|
||||||
a video framebuffer, audio output, mouse, and keyboard.
|
a video framebuffer, audio output, mouse, and keyboard.
|
||||||
|
@ -51,9 +49,10 @@ Section: libdevel
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
libsdl2 (= ${binary:Version}),
|
libsdl2-2.0-0 (= ${binary:Version}),
|
||||||
libc6-dev,
|
libc6-dev,
|
||||||
libgl1-mesa-dev
|
libgl1-mesa-dev,
|
||||||
|
libx11-dev
|
||||||
Conflicts: libsdl-1.3-dev
|
Conflicts: libsdl-1.3-dev
|
||||||
Replaces: libsdl-1.3-dev
|
Replaces: libsdl-1.3-dev
|
||||||
Description: Simple DirectMedia Layer development files
|
Description: Simple DirectMedia Layer development files
|
||||||
|
@ -68,7 +67,7 @@ Section: debug
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Depends: ${misc:Depends},
|
Depends: ${misc:Depends},
|
||||||
libsdl2 (= ${binary:Version}),
|
libsdl2-2.0-0 (= ${binary:Version}),
|
||||||
Description: Simple DirectMedia Layer debug files
|
Description: Simple DirectMedia Layer debug files
|
||||||
SDL is a library that allows programs portable low level access to a video
|
SDL is a library that allows programs portable low level access to a video
|
||||||
framebuffer, audio output, mouse, and keyboard.
|
framebuffer, audio output, mouse, and keyboard.
|
||||||
|
|
5
debian/rules
vendored
5
debian/rules
vendored
|
@ -6,7 +6,10 @@ DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
|
||||||
confflags = --disable-rpath --disable-video-directfb \
|
confflags = --disable-rpath --disable-video-directfb \
|
||||||
--disable-nas --disable-esd --disable-arts \
|
--disable-nas --disable-esd --disable-arts \
|
||||||
--disable-alsa-shared --disable-pulseaudio-shared \
|
--disable-alsa-shared --disable-pulseaudio-shared \
|
||||||
--disable-x11-shared
|
--disable-x11-shared --disable-video-opengl --enable-video-opengles \
|
||||||
|
--disable-esd --disable-pulseaudio \
|
||||||
|
--disable-input-tslib \
|
||||||
|
--host=armv6l-raspberry-linux-gnueabihf
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --parallel
|
dh $@ --parallel
|
||||||
|
|
|
@ -745,6 +745,33 @@ extern "C" {
|
||||||
*/
|
*/
|
||||||
#define SDL_HINT_NO_SIGNAL_HANDLERS "SDL_NO_SIGNAL_HANDLERS"
|
#define SDL_HINT_NO_SIGNAL_HANDLERS "SDL_NO_SIGNAL_HANDLERS"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tell dispmanx to stretch the SDL window to fill the display.
|
||||||
|
*
|
||||||
|
* This hint only applies to the rpi video driver.
|
||||||
|
*
|
||||||
|
* The variable can be set to the following values:
|
||||||
|
* "0" - Window resolution is desktop resolution.
|
||||||
|
* This is the behaviour of SDL <= 2.0.4. (default)
|
||||||
|
* "1" - Requested video resolution will be scaled to desktop resolution.
|
||||||
|
* Aspect ratio of requested video resolution will be respected.
|
||||||
|
* "2" - Requested video resolution will be scaled to desktop resolution.
|
||||||
|
* "3" - Requested video resolution will be scaled to desktop resolution.
|
||||||
|
* Aspect ratio of requested video resolution will be respected.
|
||||||
|
* If possible output resolution will be integral multiple of video
|
||||||
|
* resolution.
|
||||||
|
*/
|
||||||
|
#define SDL_HINT_VIDEO_RPI_SCALE_MODE "SDL_VIDEO_RPI_SCALE_MODE"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Tell dispmanx to set a specific aspect ratio.
|
||||||
|
*
|
||||||
|
* This hint only applies to the rpi video driver.
|
||||||
|
*
|
||||||
|
* Must be set together with SDL_HINT_VIDEO_RPI_SCALE_MODE=1.
|
||||||
|
*/
|
||||||
|
#define SDL_HINT_VIDEO_RPI_RATIO "SDL_VIDEO_RPI_RATIO"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Tell SDL not to generate window-close events for Alt+F4 on Windows.
|
* \brief Tell SDL not to generate window-close events for Alt+F4 on Windows.
|
||||||
*
|
*
|
||||||
|
|
|
@ -396,11 +396,13 @@ SDL_EVDEV_translate_keycode(int keycode)
|
||||||
if (keycode < SDL_arraysize(linux_scancode_table))
|
if (keycode < SDL_arraysize(linux_scancode_table))
|
||||||
scancode = linux_scancode_table[keycode];
|
scancode = linux_scancode_table[keycode];
|
||||||
|
|
||||||
|
/*
|
||||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||||
SDL_Log("The key you just pressed is not recognized by SDL. To help "
|
SDL_Log("The key you just pressed is not recognized by SDL. To help "
|
||||||
"get this fixed, please report this to the SDL forums/mailing list "
|
"get this fixed, please report this to the SDL forums/mailing list "
|
||||||
"<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
|
"<https://discourse.libsdl.org/> EVDEV KeyCode %d", keycode);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return scancode;
|
return scancode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -649,7 +649,7 @@ ConfigJoystick(SDL_Joystick * joystick, int fd)
|
||||||
++joystick->nbuttons;
|
++joystick->nbuttons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; i < ABS_MAX; ++i) {
|
for (i = 0; i < ABS_MISC; ++i) {
|
||||||
/* Skip hats */
|
/* Skip hats */
|
||||||
if (i == ABS_HAT0X) {
|
if (i == ABS_HAT0X) {
|
||||||
i = ABS_HAT3Y;
|
i = ABS_HAT3Y;
|
||||||
|
@ -913,6 +913,10 @@ HandleInputEvents(SDL_Joystick * joystick)
|
||||||
events[i].value);
|
events[i].value);
|
||||||
break;
|
break;
|
||||||
case EV_ABS:
|
case EV_ABS:
|
||||||
|
if (code >= ABS_MISC) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case ABS_HAT0X:
|
case ABS_HAT0X:
|
||||||
case ABS_HAT0Y:
|
case ABS_HAT0Y:
|
||||||
|
|
|
@ -44,12 +44,12 @@
|
||||||
|
|
||||||
#if SDL_VIDEO_DRIVER_RPI
|
#if SDL_VIDEO_DRIVER_RPI
|
||||||
/* Raspbian places the OpenGL ES/EGL binaries in a non standard path */
|
/* Raspbian places the OpenGL ES/EGL binaries in a non standard path */
|
||||||
#define DEFAULT_EGL "/opt/vc/lib/libbrcmEGL.so"
|
#define DEFAULT_EGL ( vc4 ? "libEGL.so.1" : "/opt/vc/lib/libbrcmEGL.so" )
|
||||||
#define DEFAULT_OGL_ES2 "/opt/vc/lib/libbrcmGLESv2.so"
|
#define DEFAULT_OGL_ES2 ( vc4 ? "libGLESv2.so.2" : "/opt/vc/lib/libbrcmGLESv2.so" )
|
||||||
#define ALT_EGL "/opt/vc/lib/libEGL.so"
|
#define ALT_EGL "/opt/vc/lib/libEGL.so"
|
||||||
#define ALT_OGL_ES2 "/opt/vc/lib/libGLESv2.so"
|
#define ALT_OGL_ES2 "/opt/vc/lib/libGLESv2.so"
|
||||||
#define DEFAULT_OGL_ES_PVR "/opt/vc/lib/libGLES_CM.so"
|
#define DEFAULT_OGL_ES_PVR ( vc4 ? "libGLES_CM.so.1" : "/opt/vc/lib/libbrcmGLESv2.so" )
|
||||||
#define DEFAULT_OGL_ES "/opt/vc/lib/libGLESv1_CM.so"
|
#define DEFAULT_OGL_ES ( vc4 ? "libGLESv1_CM.so.1" : "/opt/vc/lib/libbrcmGLESv2.so" )
|
||||||
|
|
||||||
#elif SDL_VIDEO_DRIVER_ANDROID || SDL_VIDEO_DRIVER_VIVANTE
|
#elif SDL_VIDEO_DRIVER_ANDROID || SDL_VIDEO_DRIVER_VIVANTE
|
||||||
/* Android */
|
/* Android */
|
||||||
|
@ -256,6 +256,9 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
|
||||||
#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
|
#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
|
||||||
const char *d3dcompiler;
|
const char *d3dcompiler;
|
||||||
#endif
|
#endif
|
||||||
|
#if SDL_VIDEO_DRIVER_RPI
|
||||||
|
SDL_bool vc4 = (0 == access("/sys/module/vc4/", F_OK));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_this->egl_data) {
|
if (_this->egl_data) {
|
||||||
return SDL_SetError("OpenGL ES context already created");
|
return SDL_SetError("OpenGL ES context already created");
|
||||||
|
@ -295,7 +298,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
|
||||||
path = DEFAULT_OGL_ES2;
|
path = DEFAULT_OGL_ES2;
|
||||||
egl_dll_handle = SDL_LoadObject(path);
|
egl_dll_handle = SDL_LoadObject(path);
|
||||||
#ifdef ALT_OGL_ES2
|
#ifdef ALT_OGL_ES2
|
||||||
if (egl_dll_handle == NULL) {
|
if (egl_dll_handle == NULL && !vc4) {
|
||||||
path = ALT_OGL_ES2;
|
path = ALT_OGL_ES2;
|
||||||
egl_dll_handle = SDL_LoadObject(path);
|
egl_dll_handle = SDL_LoadObject(path);
|
||||||
}
|
}
|
||||||
|
@ -308,6 +311,12 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
|
||||||
path = DEFAULT_OGL_ES_PVR;
|
path = DEFAULT_OGL_ES_PVR;
|
||||||
egl_dll_handle = SDL_LoadObject(path);
|
egl_dll_handle = SDL_LoadObject(path);
|
||||||
}
|
}
|
||||||
|
#ifdef ALT_OGL_ES2
|
||||||
|
if (egl_dll_handle == NULL && !vc4) {
|
||||||
|
path = ALT_OGL_ES2;
|
||||||
|
egl_dll_handle = SDL_LoadObject(path);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEFAULT_OGL
|
#ifdef DEFAULT_OGL
|
||||||
|
@ -339,7 +348,7 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
|
||||||
dll_handle = SDL_LoadObject(path);
|
dll_handle = SDL_LoadObject(path);
|
||||||
|
|
||||||
#ifdef ALT_EGL
|
#ifdef ALT_EGL
|
||||||
if (dll_handle == NULL) {
|
if (dll_handle == NULL && !vc4) {
|
||||||
path = ALT_EGL;
|
path = ALT_EGL;
|
||||||
dll_handle = SDL_LoadObject(path);
|
dll_handle = SDL_LoadObject(path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,12 +97,12 @@ static VideoBootStrap *bootstrap[] = {
|
||||||
#if SDL_VIDEO_DRIVER_PSP
|
#if SDL_VIDEO_DRIVER_PSP
|
||||||
&PSP_bootstrap,
|
&PSP_bootstrap,
|
||||||
#endif
|
#endif
|
||||||
#if SDL_VIDEO_DRIVER_RPI
|
|
||||||
&RPI_bootstrap,
|
|
||||||
#endif
|
|
||||||
#if SDL_VIDEO_DRIVER_KMSDRM
|
#if SDL_VIDEO_DRIVER_KMSDRM
|
||||||
&KMSDRM_bootstrap,
|
&KMSDRM_bootstrap,
|
||||||
#endif
|
#endif
|
||||||
|
#if SDL_VIDEO_DRIVER_RPI
|
||||||
|
&RPI_bootstrap,
|
||||||
|
#endif
|
||||||
#if SDL_VIDEO_DRIVER_NACL
|
#if SDL_VIDEO_DRIVER_NACL
|
||||||
&NACL_bootstrap,
|
&NACL_bootstrap,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* SDL internals */
|
/* SDL internals */
|
||||||
|
#include "SDL_hints.h"
|
||||||
#include "../SDL_sysvideo.h"
|
#include "../SDL_sysvideo.h"
|
||||||
#include "SDL_version.h"
|
#include "SDL_version.h"
|
||||||
#include "SDL_syswm.h"
|
#include "SDL_syswm.h"
|
||||||
|
@ -217,6 +218,9 @@ RPI_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
|
||||||
int
|
int
|
||||||
RPI_CreateWindow(_THIS, SDL_Window * window)
|
RPI_CreateWindow(_THIS, SDL_Window * window)
|
||||||
{
|
{
|
||||||
|
const char *hintScale = SDL_GetHint(SDL_HINT_VIDEO_RPI_SCALE_MODE);
|
||||||
|
const char *hintRatio = SDL_GetHint(SDL_HINT_VIDEO_RPI_RATIO);
|
||||||
|
char scalemode = '0';
|
||||||
SDL_WindowData *wdata;
|
SDL_WindowData *wdata;
|
||||||
SDL_VideoDisplay *display;
|
SDL_VideoDisplay *display;
|
||||||
SDL_DisplayData *displaydata;
|
SDL_DisplayData *displaydata;
|
||||||
|
@ -226,6 +230,10 @@ RPI_CreateWindow(_THIS, SDL_Window * window)
|
||||||
DISPMANX_UPDATE_HANDLE_T dispman_update;
|
DISPMANX_UPDATE_HANDLE_T dispman_update;
|
||||||
uint32_t layer = SDL_RPI_VIDEOLAYER;
|
uint32_t layer = SDL_RPI_VIDEOLAYER;
|
||||||
const char *env;
|
const char *env;
|
||||||
|
float srcAspect = 1;
|
||||||
|
float dstAspect = 1;
|
||||||
|
int factor_x = 0;
|
||||||
|
int factor_y = 0;
|
||||||
|
|
||||||
/* Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) */
|
/* Disable alpha, otherwise the app looks composed with whatever dispman is showing (X11, console,etc) */
|
||||||
dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
|
dispman_alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
|
||||||
|
@ -240,24 +248,85 @@ RPI_CreateWindow(_THIS, SDL_Window * window)
|
||||||
display = SDL_GetDisplayForWindow(window);
|
display = SDL_GetDisplayForWindow(window);
|
||||||
displaydata = (SDL_DisplayData *) display->driverdata;
|
displaydata = (SDL_DisplayData *) display->driverdata;
|
||||||
|
|
||||||
/* Windows have one size for now */
|
if (hintScale != NULL)
|
||||||
window->w = display->desktop_mode.w;
|
scalemode = *hintScale;
|
||||||
window->h = display->desktop_mode.h;
|
|
||||||
|
|
||||||
/* OpenGL ES is the law here, buddy */
|
|
||||||
window->flags |= SDL_WINDOW_OPENGL;
|
|
||||||
|
|
||||||
/* Create a dispman element and associate a window to it */
|
/* Create a dispman element and associate a window to it */
|
||||||
dst_rect.x = 0;
|
switch(scalemode) {
|
||||||
dst_rect.y = 0;
|
case '3':
|
||||||
dst_rect.width = window->w;
|
/* Pixel perfect scaling mode. */
|
||||||
dst_rect.height = window->h;
|
factor_x = (display->desktop_mode.w / window->w);
|
||||||
|
factor_y = (display->desktop_mode.h / window->h);
|
||||||
|
if ((factor_x != 0) && (factor_y != 0)) {
|
||||||
|
if (factor_x >= factor_y) {
|
||||||
|
dst_rect.width = window->w * factor_y;
|
||||||
|
dst_rect.height = window->h * factor_y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dst_rect.width = window->w * factor_x;
|
||||||
|
dst_rect.height = window->h * factor_x;
|
||||||
|
}
|
||||||
|
/* Center window. */
|
||||||
|
dst_rect.x = (display->desktop_mode.w - dst_rect.width) / 2;
|
||||||
|
dst_rect.y = (display->desktop_mode.h - dst_rect.height) / 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* If integer scale is not possible fallback to mode 1. */
|
||||||
|
case '1':
|
||||||
|
/* Fullscreen mode. */
|
||||||
|
/* Calculate source and destination aspect ratios. */
|
||||||
|
if (hintRatio != NULL)
|
||||||
|
srcAspect = strtof(hintRatio, NULL);
|
||||||
|
else
|
||||||
|
srcAspect = (float)window->w / (float)window->h;
|
||||||
|
/* only allow sensible aspect ratios */
|
||||||
|
if (srcAspect < 0.2f || srcAspect > 6.0f)
|
||||||
|
srcAspect = (float)window->w / (float)window->h;
|
||||||
|
dstAspect = (float)display->desktop_mode.w / (float)display->desktop_mode.h;
|
||||||
|
/* If source and destination aspect ratios are not equal correct destination width. */
|
||||||
|
if (srcAspect < dstAspect) {
|
||||||
|
dst_rect.width = (unsigned)(display->desktop_mode.h * srcAspect);
|
||||||
|
dst_rect.height = display->desktop_mode.h;
|
||||||
|
}
|
||||||
|
else if (srcAspect > dstAspect) {
|
||||||
|
dst_rect.width = display->desktop_mode.w;
|
||||||
|
dst_rect.height = (unsigned)((float)display->desktop_mode.w / srcAspect);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dst_rect.width = display->desktop_mode.w;
|
||||||
|
dst_rect.height = display->desktop_mode.h;
|
||||||
|
}
|
||||||
|
/* Center window. */
|
||||||
|
dst_rect.x = (display->desktop_mode.w - dst_rect.width) / 2;
|
||||||
|
dst_rect.y = (display->desktop_mode.h - dst_rect.height) / 2;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
/* Fullscreen streched mode. */
|
||||||
|
dst_rect.x = 0;
|
||||||
|
dst_rect.y = 0;
|
||||||
|
dst_rect.width = display->desktop_mode.w;
|
||||||
|
dst_rect.height = display->desktop_mode.h;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Default mode. */
|
||||||
|
window->w = display->desktop_mode.w;
|
||||||
|
window->h = display->desktop_mode.h;
|
||||||
|
|
||||||
|
dst_rect.x = 0;
|
||||||
|
dst_rect.y = 0;
|
||||||
|
dst_rect.width = window->w;
|
||||||
|
dst_rect.height = window->h;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
src_rect.x = 0;
|
src_rect.x = 0;
|
||||||
src_rect.y = 0;
|
src_rect.y = 0;
|
||||||
src_rect.width = window->w << 16;
|
src_rect.width = window->w << 16;
|
||||||
src_rect.height = window->h << 16;
|
src_rect.height = window->h << 16;
|
||||||
|
|
||||||
|
/* OpenGL ES is the law here, buddy */
|
||||||
|
window->flags |= SDL_WINDOW_OPENGL;
|
||||||
|
|
||||||
env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
|
env = SDL_GetHint(SDL_HINT_RPI_VIDEO_LAYER);
|
||||||
if (env) {
|
if (env) {
|
||||||
layer = SDL_atoi(env);
|
layer = SDL_atoi(env);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue