mirror of
				https://github.com/labwc/labwc.git
				synced 2025-11-03 09:01:51 -05:00 
			
		
		
		
	output: add output_init() and refactor
This commit is contained in:
		
							parent
							
								
									53b360dd11
								
							
						
					
					
						commit
						763f5c3455
					
				
					 3 changed files with 69 additions and 79 deletions
				
			
		| 
						 | 
					@ -45,6 +45,7 @@ enum cursor_mode {
 | 
				
			||||||
struct server {
 | 
					struct server {
 | 
				
			||||||
	struct wl_display *wl_display;
 | 
						struct wl_display *wl_display;
 | 
				
			||||||
	struct wlr_renderer *renderer;
 | 
						struct wlr_renderer *renderer;
 | 
				
			||||||
 | 
						struct wlr_backend *backend;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_xdg_shell *xdg_shell;
 | 
						struct wlr_xdg_shell *xdg_shell;
 | 
				
			||||||
	struct wl_listener new_xdg_surface;
 | 
						struct wl_listener new_xdg_surface;
 | 
				
			||||||
| 
						 | 
					@ -88,6 +89,7 @@ struct output {
 | 
				
			||||||
	struct server *server;
 | 
						struct server *server;
 | 
				
			||||||
	struct wlr_output *wlr_output;
 | 
						struct wlr_output *wlr_output;
 | 
				
			||||||
	struct wl_listener frame;
 | 
						struct wl_listener frame;
 | 
				
			||||||
 | 
						struct wl_listener destroy;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum view_type { LAB_XDG_SHELL_VIEW, LAB_XWAYLAND_VIEW };
 | 
					enum view_type { LAB_XDG_SHELL_VIEW, LAB_XWAYLAND_VIEW };
 | 
				
			||||||
| 
						 | 
					@ -232,8 +234,7 @@ void cursor_new(struct server *server, struct wlr_input_device *device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void keyboard_new(struct server *server, struct wlr_input_device *device);
 | 
					void keyboard_new(struct server *server, struct wlr_input_device *device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void output_frame(struct wl_listener *listener, void *data);
 | 
					void output_init(struct server *server);
 | 
				
			||||||
void output_new(struct wl_listener *listener, void *data);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct border deco_thickness(struct view *view);
 | 
					struct border deco_thickness(struct view *view);
 | 
				
			||||||
struct wlr_box deco_max_extents(struct view *view);
 | 
					struct wlr_box deco_max_extents(struct view *view);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										97
									
								
								src/output.c
									
										
									
									
									
								
							
							
						
						
									
										97
									
								
								src/output.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,3 +1,4 @@
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_xdg_output_v1.h>
 | 
				
			||||||
#include "labwc.h"
 | 
					#include "labwc.h"
 | 
				
			||||||
#include "theme/theme.h"
 | 
					#include "theme/theme.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -231,11 +232,13 @@ render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
 | 
				
			||||||
	wlr_surface_send_frame_done(surface, rdata->when);
 | 
						wlr_surface_send_frame_done(surface, rdata->when);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					static void
 | 
				
			||||||
output_frame(struct wl_listener *listener, void *data)
 | 
					output_frame_notify(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* This function is called every time an output is ready to display a
 | 
						/*
 | 
				
			||||||
	 * frame, generally at the output's refresh rate (e.g. 60Hz). */
 | 
						 * This function is called every time an output is ready to display a
 | 
				
			||||||
 | 
						 * frame, generally at the output's refresh rate (e.g. 60Hz).
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	struct output *output = wl_container_of(listener, output, frame);
 | 
						struct output *output = wl_container_of(listener, output, frame);
 | 
				
			||||||
	struct wlr_renderer *renderer = output->server->renderer;
 | 
						struct wlr_renderer *renderer = output->server->renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -246,19 +249,17 @@ output_frame(struct wl_listener *listener, void *data)
 | 
				
			||||||
	if (!wlr_output_attach_render(output->wlr_output, NULL)) {
 | 
						if (!wlr_output_attach_render(output->wlr_output, NULL)) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* The "effective" resolution can change if you rotate your outputs. */
 | 
						/* The "effective" resolution can change if you rotate your outputs. */
 | 
				
			||||||
	int width, height;
 | 
						int width, height;
 | 
				
			||||||
	wlr_output_effective_resolution(output->wlr_output, &width, &height);
 | 
						wlr_output_effective_resolution(output->wlr_output, &width, &height);
 | 
				
			||||||
	/* Begin the renderer (calls glViewport and some other GL sanity checks)
 | 
					
 | 
				
			||||||
	 */
 | 
						/* Calls glViewport and some other GL sanity checks */
 | 
				
			||||||
	wlr_renderer_begin(renderer, width, height);
 | 
						wlr_renderer_begin(renderer, width, height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	float color[4] = { 0.3, 0.3, 0.3, 1.0 };
 | 
						float color[4] = { 0.3, 0.3, 0.3, 1.0 };
 | 
				
			||||||
	wlr_renderer_clear(renderer, color);
 | 
						wlr_renderer_clear(renderer, color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Each subsequent window we render is rendered on top of the last.
 | 
					 | 
				
			||||||
	 * Because our view list is ordered front-to-back, we iterate over it
 | 
					 | 
				
			||||||
	 * backwards. */
 | 
					 | 
				
			||||||
	struct view *view;
 | 
						struct view *view;
 | 
				
			||||||
	wl_list_for_each_reverse (view, &output->server->views, link) {
 | 
						wl_list_for_each_reverse (view, &output->server->views, link) {
 | 
				
			||||||
		if (!view->mapped)
 | 
							if (!view->mapped)
 | 
				
			||||||
| 
						 | 
					@ -305,23 +306,23 @@ output_frame(struct wl_listener *listener, void *data)
 | 
				
			||||||
		render_surface(s, 0, 0, &rdata);
 | 
							render_surface(s, 0, 0, &rdata);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Hardware cursors are rendered by the GPU on a separate plane, and can
 | 
						/* Just in case hardware cursors not supported by GPU */
 | 
				
			||||||
	 * be moved around without re-rendering what's beneath them - which is
 | 
					 | 
				
			||||||
	 * more efficient. However, not all hardware supports hardware cursors.
 | 
					 | 
				
			||||||
	 * For this reason, wlroots provides a software fallback, which we ask
 | 
					 | 
				
			||||||
	 * it to render here. wlr_cursor handles configuring hardware vs
 | 
					 | 
				
			||||||
	 * software cursors for you,
 | 
					 | 
				
			||||||
	 * and this function is a no-op when hardware cursors are in use. */
 | 
					 | 
				
			||||||
	wlr_output_render_software_cursors(output->wlr_output, NULL);
 | 
						wlr_output_render_software_cursors(output->wlr_output, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Conclude rendering and swap the buffers, showing the final frame
 | 
					 | 
				
			||||||
	 * on-screen. */
 | 
					 | 
				
			||||||
	wlr_renderer_end(renderer);
 | 
						wlr_renderer_end(renderer);
 | 
				
			||||||
	wlr_output_commit(output->wlr_output);
 | 
						wlr_output_commit(output->wlr_output);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					static void
 | 
				
			||||||
output_new(struct wl_listener *listener, void *data)
 | 
					output_destroy_notify(struct wl_listener *listener, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					        struct output *output = wl_container_of(listener, output, destroy);
 | 
				
			||||||
 | 
					        wl_list_remove(&output->link);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					new_output_notify(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* This event is rasied by the backend when a new output (aka a display
 | 
						/* This event is rasied by the backend when a new output (aka a display
 | 
				
			||||||
	 * or monitor) becomes available. */
 | 
						 * or monitor) becomes available. */
 | 
				
			||||||
| 
						 | 
					@ -329,39 +330,51 @@ output_new(struct wl_listener *listener, void *data)
 | 
				
			||||||
	struct wlr_output *wlr_output = data;
 | 
						struct wlr_output *wlr_output = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Some backends don't have modes. DRM+KMS does, and we need to set a
 | 
						 * The mode is a tuple of (width, height, refresh rate).
 | 
				
			||||||
	 * mode before we can use the output. The mode is a tuple of (width,
 | 
					 | 
				
			||||||
	 * height, refresh rate), and each monitor supports only a specific set
 | 
					 | 
				
			||||||
	 * of modes. We just pick the monitor's preferred mode.
 | 
					 | 
				
			||||||
	 * TODO: support user configuration
 | 
						 * TODO: support user configuration
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!wl_list_empty(&wlr_output->modes)) {
 | 
						struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
 | 
				
			||||||
		struct wlr_output_mode *mode =
 | 
						if (mode) {
 | 
				
			||||||
			wlr_output_preferred_mode(wlr_output);
 | 
					 | 
				
			||||||
		wlr_output_set_mode(wlr_output, mode);
 | 
							wlr_output_set_mode(wlr_output, mode);
 | 
				
			||||||
		wlr_output_enable(wlr_output, true);
 | 
							wlr_output_commit(wlr_output);
 | 
				
			||||||
		if (!wlr_output_commit(wlr_output)) {
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Allocates and configures our state for this output */
 | 
					 | 
				
			||||||
	struct output *output = calloc(1, sizeof(struct output));
 | 
						struct output *output = calloc(1, sizeof(struct output));
 | 
				
			||||||
	output->wlr_output = wlr_output;
 | 
						output->wlr_output = wlr_output;
 | 
				
			||||||
	output->server = server;
 | 
						output->server = server;
 | 
				
			||||||
	/* Sets up a listener for the frame notify event. */
 | 
						output->frame.notify = output_frame_notify;
 | 
				
			||||||
	output->frame.notify = output_frame;
 | 
					 | 
				
			||||||
	wl_signal_add(&wlr_output->events.frame, &output->frame);
 | 
						wl_signal_add(&wlr_output->events.frame, &output->frame);
 | 
				
			||||||
	wl_list_insert(&server->outputs, &output->link);
 | 
						wl_list_insert(&server->outputs, &output->link);
 | 
				
			||||||
 | 
						output->destroy.notify = output_destroy_notify;
 | 
				
			||||||
 | 
						wl_signal_add(&wlr_output->events.destroy, &output->destroy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Adds this to the output layout. The add_auto function arranges
 | 
						/*
 | 
				
			||||||
	 * outputs from left-to-right in the order they appear. A more
 | 
						 * Arrange outputs from left-to-right in the order they appear.
 | 
				
			||||||
	 * sophisticated compositor would let the user configure the arrangement
 | 
						 * TODO: support configuration in run-time
 | 
				
			||||||
	 * of outputs in the layout.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * The output layout utility automatically adds a wl_output global to
 | 
					 | 
				
			||||||
	 * the display, which Wayland clients can see to find out information
 | 
					 | 
				
			||||||
	 * about the output (such as DPI, scale factor, manufacturer, etc).
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	wlr_output_layout_add_auto(server->output_layout, wlr_output);
 | 
						wlr_output_layout_add_auto(server->output_layout, wlr_output);
 | 
				
			||||||
 | 
						wlr_output_schedule_frame(wlr_output);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					output_init(struct server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						server->new_output.notify = new_output_notify;
 | 
				
			||||||
 | 
						wl_signal_add(&server->backend->events.new_output, &server->new_output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Create an output layout, which is a wlroots utility for working with
 | 
				
			||||||
 | 
						 * an arrangement of screens in a physical layout.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						server->output_layout = wlr_output_layout_create();
 | 
				
			||||||
 | 
						if (!server->output_layout) {
 | 
				
			||||||
 | 
							wlr_log(WLR_ERROR, "unable to create output layout");
 | 
				
			||||||
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Enable screen recording with wf-recorder */
 | 
				
			||||||
 | 
						wlr_xdg_output_manager_v1_create(server->wl_display,
 | 
				
			||||||
 | 
							server->output_layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wl_list_init(&server->outputs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										46
									
								
								src/server.c
									
										
									
									
									
								
							
							
						
						
									
										46
									
								
								src/server.c
									
										
									
									
									
								
							| 
						 | 
					@ -9,9 +9,7 @@
 | 
				
			||||||
#include <wlr/types/wlr_gamma_control_v1.h>
 | 
					#include <wlr/types/wlr_gamma_control_v1.h>
 | 
				
			||||||
#include <wlr/types/wlr_primary_selection_v1.h>
 | 
					#include <wlr/types/wlr_primary_selection_v1.h>
 | 
				
			||||||
#include <wlr/types/wlr_screencopy_v1.h>
 | 
					#include <wlr/types/wlr_screencopy_v1.h>
 | 
				
			||||||
#include <wlr/types/wlr_xdg_output_v1.h>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct wlr_backend *backend;
 | 
					 | 
				
			||||||
static struct wlr_compositor *compositor;
 | 
					static struct wlr_compositor *compositor;
 | 
				
			||||||
static struct wl_event_source *sighup_source;
 | 
					static struct wl_event_source *sighup_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,18 +115,14 @@ server_init(struct server *server)
 | 
				
			||||||
		event_loop, SIGHUP, handle_signal, &server->wl_display);
 | 
							event_loop, SIGHUP, handle_signal, &server->wl_display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * The backend is a wlroots feature which abstracts the underlying
 | 
						 * The backend is a feature which abstracts the underlying input and
 | 
				
			||||||
	 * input and output hardware. the autocreate option will choose the
 | 
						 * output hardware. The autocreate option will choose the most suitable
 | 
				
			||||||
	 * most suitable backend based on the current environment, such as
 | 
						 * backend based on the current environment, such as opening an x11
 | 
				
			||||||
	 * opening an x11 window if an x11 server is running. the null
 | 
						 * window if an x11 server is running.
 | 
				
			||||||
	 * argument here optionally allows you to pass in a custom renderer if
 | 
					 | 
				
			||||||
	 * wlr_renderer doesn't meet your needs. the backend uses the
 | 
					 | 
				
			||||||
	 * renderer, for example, to fall back to software cursors if the
 | 
					 | 
				
			||||||
	 * backend does not support hardware cursors (some older gpus don't).
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	backend = wlr_backend_autocreate(server->wl_display, NULL);
 | 
						server->backend = wlr_backend_autocreate(server->wl_display, NULL);
 | 
				
			||||||
	if (!backend) {
 | 
						if (!server->backend) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "unable to create the wlroots backend");
 | 
							wlr_log(WLR_ERROR, "unable to create backend");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,22 +132,11 @@ server_init(struct server *server)
 | 
				
			||||||
	 * formats it supports for shared memory, this configures that for
 | 
						 * formats it supports for shared memory, this configures that for
 | 
				
			||||||
	 * clients.
 | 
						 * clients.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	server->renderer = wlr_backend_get_renderer(backend);
 | 
						server->renderer = wlr_backend_get_renderer(server->backend);
 | 
				
			||||||
	wlr_renderer_init_wl_display(server->renderer, server->wl_display);
 | 
						wlr_renderer_init_wl_display(server->renderer, server->wl_display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_init(&server->views);
 | 
						wl_list_init(&server->views);
 | 
				
			||||||
	wl_list_init(&server->unmanaged_surfaces);
 | 
						wl_list_init(&server->unmanaged_surfaces);
 | 
				
			||||||
	wl_list_init(&server->outputs);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Create an output layout, which a wlroots utility for working with
 | 
					 | 
				
			||||||
	 * an arrangement of screens in a physical layout.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	server->output_layout = wlr_output_layout_create();
 | 
					 | 
				
			||||||
	if (!server->output_layout) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "unable to create output layout");
 | 
					 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Create some hands-off wlroots interfaces. The compositor is
 | 
						 * Create some hands-off wlroots interfaces. The compositor is
 | 
				
			||||||
| 
						 | 
					@ -176,12 +159,7 @@ server_init(struct server *server)
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						output_init(server);
 | 
				
			||||||
	 * Configure a listener to be notified when new outputs are available
 | 
					 | 
				
			||||||
	 * on the backend.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	server->new_output.notify = output_new;
 | 
					 | 
				
			||||||
	wl_signal_add(&backend->events.new_output, &server->new_output);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Configures a seat, which is a single "seat" at which a user sits
 | 
						 * Configures a seat, which is a single "seat" at which a user sits
 | 
				
			||||||
| 
						 | 
					@ -218,7 +196,7 @@ server_init(struct server *server)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_init(&server->keyboards);
 | 
						wl_list_init(&server->keyboards);
 | 
				
			||||||
	server->new_input.notify = server_new_input;
 | 
						server->new_input.notify = server_new_input;
 | 
				
			||||||
	wl_signal_add(&backend->events.new_input, &server->new_input);
 | 
						wl_signal_add(&server->backend->events.new_input, &server->new_input);
 | 
				
			||||||
	server->request_cursor.notify = seat_request_cursor;
 | 
						server->request_cursor.notify = seat_request_cursor;
 | 
				
			||||||
	wl_signal_add(&server->seat->events.request_set_cursor,
 | 
						wl_signal_add(&server->seat->events.request_set_cursor,
 | 
				
			||||||
		      &server->request_cursor);
 | 
							      &server->request_cursor);
 | 
				
			||||||
| 
						 | 
					@ -263,8 +241,6 @@ server_init(struct server *server)
 | 
				
			||||||
	wlr_data_control_manager_v1_create(server->wl_display);
 | 
						wlr_data_control_manager_v1_create(server->wl_display);
 | 
				
			||||||
	wlr_gamma_control_manager_v1_create(server->wl_display);
 | 
						wlr_gamma_control_manager_v1_create(server->wl_display);
 | 
				
			||||||
	wlr_primary_selection_v1_device_manager_create(server->wl_display);
 | 
						wlr_primary_selection_v1_device_manager_create(server->wl_display);
 | 
				
			||||||
	wlr_xdg_output_manager_v1_create(server->wl_display,
 | 
					 | 
				
			||||||
					 server->output_layout);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Init xwayland */
 | 
						/* Init xwayland */
 | 
				
			||||||
	server->xwayland =
 | 
						server->xwayland =
 | 
				
			||||||
| 
						 | 
					@ -320,7 +296,7 @@ server_start(struct server *server)
 | 
				
			||||||
	 * Start the backend. This will enumerate outputs and inputs, become
 | 
						 * Start the backend. This will enumerate outputs and inputs, become
 | 
				
			||||||
	 * the DRM master, etc
 | 
						 * the DRM master, etc
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!wlr_backend_start(backend)) {
 | 
						if (!wlr_backend_start(server->backend)) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "unable to start the wlroots backend");
 | 
							wlr_log(WLR_ERROR, "unable to start the wlroots backend");
 | 
				
			||||||
		exit(EXIT_FAILURE);
 | 
							exit(EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue