diff options
author | jk7744.park <jk7744.park@samsung.com> | 2015-10-24 15:28:19 +0900 |
---|---|---|
committer | jk7744.park <jk7744.park@samsung.com> | 2015-10-24 15:28:19 +0900 |
commit | 841a80cb5613b15fd5d7d256e21ab0a69df081e9 (patch) | |
tree | 56e13bf65224f1554bd6cf4033f223b3ceb5d0e4 /tests | |
parent | cb41fec8f453fd5dc9fb1d4a92a6d49f043578fc (diff) | |
download | xserver-xorg-video-exynos-accepted/tizen_2.4_mobile.tar.gz xserver-xorg-video-exynos-accepted/tizen_2.4_mobile.tar.bz2 xserver-xorg-video-exynos-accepted/tizen_2.4_mobile.zip |
tizen 2.4 releasetizen_2.4_mobile_releasesubmit/tizen_2.4/20151028.062326accepted/tizen/2.4/mobile/20151029.042339tizen_2.4accepted/tizen_2.4_mobile
Diffstat (limited to 'tests')
35 files changed, 7496 insertions, 0 deletions
diff --git a/tests/functional/Makefile.am b/tests/functional/Makefile.am new file mode 100644 index 0000000..1990d80 --- /dev/null +++ b/tests/functional/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = xv_test hwc_test hwa_test pixmap_copy_test diff --git a/tests/functional/hwa_test/Makefile.am b/tests/functional/hwa_test/Makefile.am new file mode 100644 index 0000000..51df582 --- /dev/null +++ b/tests/functional/hwa_test/Makefile.am @@ -0,0 +1,9 @@ +if HAVE_FT + bin_PROGRAMS = hwa_sample + +hwa_sample_CFLAGS = $(HWA_TEST_CFLAGS) +hwa_sample_LDADD = $(HWA_TEST_LIBS) +hwa_sample_SOURCES = \ +@top_srcdir@/tests/functional/hwa_test/hwa_sample.c + +endif diff --git a/tests/functional/hwa_test/hwa_sample.c b/tests/functional/hwa_test/hwa_sample.c new file mode 100644 index 0000000..7fc6ee2 --- /dev/null +++ b/tests/functional/hwa_test/hwa_sample.c @@ -0,0 +1,531 @@ + +/* Standart headers */ +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#include <getopt.h> + +/* X Server's headers */ +#include <X11/Xlib.h> +#include <xcb/xcb.h> +#include <xcb/hwa.h> +#include <xcb/dri3.h> +#include <xcb/randr.h> + +#include "hwa_sample.h" + +void test_hwc( void ); + +xcb_connection_t *c; +xcb_generic_error_t *error; + +xcb_randr_crtc_t randr_crtc = 0; +char con_name[20] = {0,}; + +int main( int argc, char **argv ) +{ + int rez; + int option_index; + int enable; + int angle; + char *scale_params = NULL; + const char* short_options = "ic:r:a:x:s:h:o:"; + + const struct option long_options[] = + { + {"help",no_argument,NULL,'?'}, + {"info",no_argument,NULL,'i'}, + {"version",no_argument,NULL,'v'}, + {"cursore",required_argument,NULL,'c'}, + {"cursorr",required_argument,NULL,'r'}, + {"accessibility",required_argument,NULL,'a'}, + {"scale",required_argument,NULL,'x'}, + {"show",required_argument,NULL,'s'}, + {"hide",required_argument,NULL,'h'}, + {"output",required_argument,NULL,'o'}, + {NULL,0,NULL,0} + }; + + c = xcb_connect (NULL, NULL); + + while ( ( rez = getopt_long_only( argc,argv, short_options, long_options,&option_index ) ) != -1 ) + { + switch(rez) + { + case '?': + help(); + break; + + case 'i': + extensions_info(); + break; + + case 'v': + hwa_query_version(); + break; + + case 'c': + enable = atoi(optarg); + hwa_cursor_enable( enable ); + break; + + case 'r': + angle = atoi(optarg); + hwa_cursor_rotate( angle ); + break; + + case 'a': + enable = atoi(optarg); + hwa_accessibility( enable ); + + break; + + case 'x': + scale_params = (char *)calloc(30, sizeof( char ) ); + + enable = 0; + + optind--; + for( ;optind < argc; optind++) + { + if ( 2 == optind ) + enable = atoi(argv[optind]); + else + strcpy( scale_params, argv[optind] ); + } + + hwa_scale( enable, scale_params ); + free( scale_params ); + + break; + + case 's': + printf("show layer = %s\n",optarg); + hwa_show_layer(atoi(optarg)); + + break; + + case 'h': + printf("hide layer= %s\n",optarg); + hwa_hide_layer(atoi(optarg)); + break; + + /* + * TODO + * output selector need implement + * */ + case 'o': + printf("[%s] output chosen \n",optarg); + strcpy( con_name, optarg); + printf("con_name = [%s] \n",con_name); + break; + + default: + PRINT(4); + printf("ERROR: found unknown option\n\n"); + + help(); + break; + } + } + + xcb_disconnect(c); + + return 0; +} + +void extensions_info( void ) +{ + xcb_list_extensions_reply_t *rep; + xcb_query_extension_reply_t *q_rep; + + xcb_list_extensions_cookie_t ext_cookie; + xcb_query_extension_cookie_t query_c; + + ext_cookie = xcb_list_extensions( c ); + rep = xcb_list_extensions_reply( c, ext_cookie, &error ); + xcb_flush(c); + + + /* List all Extensions which X Server supports */ + printf( "\n-----------------------------------\n"); + + xcb_str_iterator_t str_iter = xcb_list_extensions_names_iterator( rep ); + + printf( "+++ index %d\n+++ %d\n", str_iter.index, str_iter.rem ); + + while (str_iter.rem) + { + printf("%d bytes: ", xcb_str_name_length(str_iter.data)); + fwrite(xcb_str_name(str_iter.data), 1, xcb_str_name_length(str_iter.data), stdout); + printf("\n"); + fixed_xcb_str_next(&str_iter); + } + + printf( "\n-----------------------------------\n"); + + /* Check does X Server support HWA */ + query_c = xcb_query_extension( c, 3, "HWA" ); + q_rep = xcb_query_extension_reply( c, query_c, &error ); + + printf( "\n-----------------------------------\n"); + printf( "Check HWA:\n" + "present: %d\n" + "major opcode: %d\n" + "first event: %d\n" + "first error: %d\n", + q_rep->present, + q_rep->major_opcode, + q_rep->first_event, + q_rep->first_error ); + + /* TODO process errors */ + + free(rep); + free(q_rep); + printf( "-----------------------------------\n"); +} + +void hwa_query_version( void ) +{ + xcb_hwa_query_version_cookie_t hwa_version_cookie; + xcb_hwa_query_version_reply_t *hwa_version_reply = NULL; + + /* 111 and 222 are just random numbers */ + hwa_version_cookie = xcb_hwa_query_version ( c, 111, 222 ); + xcb_flush(c); + + hwa_version_reply = xcb_hwa_query_version_reply( c, hwa_version_cookie, &error ); + + if(hwa_version_reply) + { + printf( "\n-----------------------------------\n"); + printf("Query Version:\n"); + printf("hwa_query_version: Call was ok\nmajor_version=%d\nminor_version=%d \n", + hwa_version_reply->major_version, + hwa_version_reply->minor_version ); + printf( "-----------------------------------\n"); + + free(hwa_version_reply); + } + else + { + printf("hwa_query_version: ERROR" ); + } + + +} + +void hwa_cursor_enable( int enable ) +{ + xcb_hwa_cursor_enable_cookie_t cursor_cookie; + xcb_hwa_cursor_enable_reply_t *cursor_enb_reply = NULL; + + cursor_cookie = xcb_hwa_cursor_enable_unchecked( c, enable ); + xcb_flush(c); + + cursor_enb_reply = xcb_hwa_cursor_enable_reply( c,cursor_cookie, &error); + + if(cursor_enb_reply) + { + printf( "\n-----------------------------------\n"); + printf("Cursor Enable:\n"); + printf("hwa_cursor_enable: Call was ok\nreply.status=%d \n", cursor_enb_reply->status ); + printf( "-----------------------------------\n"); + + free(cursor_enb_reply); + } + else + { + printf("hwa_cursor_enable: ERROR" ); + exit(1); + } +} + +void hwa_cursor_rotate( int angle ) +{ + xcb_hwa_cursor_rotate_cookie_t cursor_rotate_cookie; + xcb_hwa_cursor_rotate_reply_t *cursor_rot_reply = NULL; + + /*hwa_cursor_enable( ON );*/ + + randr_crtc = (xcb_randr_crtc_t) get_crtc_via_randr("LVDS1"); + + cursor_rotate_cookie = xcb_hwa_cursor_rotate_unchecked (c, randr_crtc, angle ); + xcb_flush(c); + cursor_rot_reply = xcb_hwa_cursor_rotate_reply ( c, cursor_rotate_cookie, &error); + + if(cursor_rot_reply) + { + printf( "\n-----------------------------------\n"); + printf("Cursor Rotate:\n"); + printf("hwa_cursor_rotate: Call was ok\nreply.status=%d, angle = %d \n ", cursor_rot_reply->status, angle ); + printf( "-----------------------------------\n"); + + free(cursor_rot_reply); + } + else + { + printf("hwa_cursor_rotate: ERROR" ); + } +} + +void hwa_accessibility( int enable ) +{ + xcb_hwa_screen_invert_negative_cookie_t hwa_scrn_invert_cookie = {0,}; + xcb_hwa_screen_invert_negative_reply_t *hwa_scrn_invert_reply = NULL; + + int crtc = get_crtc_via_randr("LVDS1"); + if (-1 == crtc) + { + printf ("Didn't find randr crtc!! :\n"); + exit (0); + } + + hwa_scrn_invert_cookie = xcb_hwa_screen_invert_negative( c, crtc, enable ); + xcb_flush(c); + + hwa_scrn_invert_reply = xcb_hwa_screen_invert_negative_reply( c, hwa_scrn_invert_cookie, &error ); + + if(hwa_scrn_invert_reply) + { + printf( "\n-----------------------------------\n"); + printf("Accessibility:\n"); + printf("hwa_accessibility: Call was ok\nreply.status=%d \n", hwa_scrn_invert_reply->status ); + printf( "-----------------------------------\n"); + + free(hwa_scrn_invert_reply); + } + else + { + printf("hwa_accessibility: ERROR" ); + } +} + +void hwa_scale( int enable, char* scale_param ) +{ + xcb_hwa_screen_scale_cookie_t hwa_scale_cookie; + xcb_hwa_screen_scale_reply_t *hwa_scale_reply; + + int x = 0, y = 0, width = 0, height = 0; + int crtc = -1; + + if( enable && !scale_param ) + { + printf( "hwa_scale: ERROR, parameters was not specified\n"); + help(); + exit(1); + } + else if ( enable && scale_param ) + { + int *xywh = pars_params( scale_param ); + + printf("x=%d y=%d w=%d h=%d\n", + xywh[0], + xywh[1], + xywh[2], + xywh[3] ); + + int i = 0; + for( ; i < NUM_PARAMS; i++ ) + { + if( 0 > xywh[i] ) + { + printf( "hwa_scale: ERROR, At least four parameters must be specified herein\n" + " Parameters must not to be negative\n"); + free(xywh); + exit(1); + } + } + + x = xywh[0]; + y = xywh[1]; + width = xywh[2]; + height = xywh[3]; + + free(xywh); + } + else if( 0 == enable ) + { + x = 0; + y = 0; + width = 0; + height = 0; + } + + crtc = get_crtc_via_randr("LVDS1"); + if (-1 == crtc) + { + printf ("Didn't find randr crtc!! :\n"); + exit (0); + } + + hwa_scale_cookie = xcb_hwa_screen_scale( c, crtc , enable, x, y, width, height ); + hwa_scale_reply = xcb_hwa_screen_scale_reply ( c, hwa_scale_cookie, &error ); + + if(hwa_scale_reply) + { + printf( "\n-----------------------------------\n"); + printf("Scale:\n"); + printf("hwa_scale: Call was ok\nreply.status=%d \n", hwa_scale_reply->status ); + printf( "-----------------------------------\n"); + + free(hwa_scale_reply); + } + else + { + printf("hwa_scale: ERROR" ); + } +} + +void hwa_show_layer(int layer) +{ + randr_crtc = (xcb_randr_crtc_t) get_crtc_via_randr("LVDS1"); + if (-1 == randr_crtc) + { + printf ("Didn't find randr crtc!! :\n"); + exit (0); + } + + printf ("<=== Overlay Layer Show ==> \n"); + printf ("<=== Randr crtc=%d, layer=%d ==> \n", randr_crtc, layer); + xcb_hwa_overlay_show_layer (c, (xcb_randr_crtc_t) randr_crtc, layer); + xcb_flush (c); + +} + +void hwa_hide_layer( int layer ) +{ + randr_crtc = (xcb_randr_crtc_t) get_crtc_via_randr("LVDS1"); + if (-1 == randr_crtc) + { + printf ("Didn't find randr crtc!! :\n"); + exit (0); + } + + printf ("<=== Overlay Layer Hide ==> \n"); + printf ("<=== Randr crtc=%d, layer=%d ==> \n", randr_crtc, layer); + xcb_hwa_overlay_hide_layer (c, (xcb_randr_crtc_t) randr_crtc, layer); + xcb_flush (c); + +} + +/* + * output = LVDS, HDMI, Virtual + * + * */ +int get_crtc_via_randr( char* output_to_find ) +{ + xcb_randr_get_screen_resources_cookie_t screenresc_cookie; + xcb_randr_get_screen_resources_reply_t* screenresc_reply = NULL; + xcb_randr_crtc_t randrcrtc_first; + int i = 0, k = 0; + + /* Get the first X screen */ + xcb_screen_t* x_first_screen = xcb_setup_roots_iterator( xcb_get_setup(c)).data; + + xcb_window_t x_window_dummy = xcb_generate_id(c); + + /* Create dummy X window */ + xcb_create_window(c, 0, x_window_dummy, x_first_screen->root, 0, 0, 1, 1, 0, 0, 0, 0, 0); + + //Flush pending requests to the X server + xcb_flush(c); + + //Send a request for screen resources to the X server + screenresc_cookie = xcb_randr_get_screen_resources( c, x_window_dummy ); + + /* Take reply from Xserver */ + screenresc_reply = xcb_randr_get_screen_resources_reply( c, screenresc_cookie, 0); + + if(screenresc_reply) + { + + /* Take each crtc, and create screen for each of them*/ + xcb_randr_crtc_t *randr_crtcs = xcb_randr_get_screen_resources_crtcs(screenresc_reply); + + for (i = 0; i < screenresc_reply->num_crtcs; ++i) + { + /* We take information of the output crtc */ + xcb_randr_get_crtc_info_cookie_t crtc_info_cookie = + xcb_randr_get_crtc_info (c, randr_crtcs [i], XCB_CURRENT_TIME); + xcb_randr_get_crtc_info_reply_t *crtc_randr_info = + xcb_randr_get_crtc_info_reply (c, crtc_info_cookie, NULL); + + /* Ignore CRTC if it hasn't OUTPUT */ + if (!xcb_randr_get_crtc_info_outputs_length (crtc_randr_info)) + continue; + + xcb_randr_output_t *randr_outputs = xcb_randr_get_crtc_info_outputs (crtc_randr_info); + + for (k = 0; k < xcb_randr_get_crtc_info_outputs_length (crtc_randr_info); ++k) + { + xcb_randr_get_output_info_cookie_t output_info_cookie = + xcb_randr_get_output_info (c, randr_outputs [k], XCB_CURRENT_TIME); + xcb_randr_get_output_info_reply_t *output_randr_info = + xcb_randr_get_output_info_reply (c, output_info_cookie, NULL); + + if (!output_randr_info) + continue; + + int length = xcb_randr_get_output_info_name_length (output_randr_info); + + /* Add null to name, it doesn't '\0' terminated */ + char *outputname = malloc ((length + 1) * sizeof(char *)); + memcpy (outputname, xcb_randr_get_output_info_name (output_randr_info), length); + outputname [length] = '\0'; + + /* + printf ("<-Screen[%i] INFO: (%s):\n", j, name); + printf (" width_mm: %i\n", output_info_r->mm_width); + printf (" height_mm: %i\n", output_info_r->mm_height); + */ + + if (!strcmp (outputname, output_to_find)){ + randrcrtc_first = output_randr_info->crtc; + free (output_randr_info); + free (crtc_randr_info); + printf ("<-RANDR CRTCID= %d\n",randrcrtc_first ); + return randrcrtc_first; + } + + free (output_randr_info); + } + + free (crtc_randr_info); + } + + free (screenresc_reply); + } + else + return -1; + + return -1; +} + +int* pars_params( char* scale_param ) +{ + int indx = 0; + int i = 0; + int* xywh = (int*)calloc( NUM_PARAMS, sizeof( int ) ); + char *delimeter = ":"; + + for( ; i < NUM_PARAMS; i++ ) + xywh[i] = -1; + + char *token = (char *)strtok( scale_param, delimeter ); + + while( token != NULL ) + { + xywh[indx++] = atoi(token); + token = (char *)strtok( NULL, delimeter ); + } + + return xywh; +} + + + + + diff --git a/tests/functional/hwa_test/hwa_sample.h b/tests/functional/hwa_test/hwa_sample.h new file mode 100644 index 0000000..cfc6e44 --- /dev/null +++ b/tests/functional/hwa_test/hwa_sample.h @@ -0,0 +1,79 @@ +/* + * hwa_sample.h + * + * Created on: Mar 20, 2015 + * Author: roma + */ + +#ifndef HWA_SAMPLE_H_ +#define HWA_SAMPLE_H_ + +/* Standart headers */ +#include <stdio.h> +#include <stdlib.h> + +#include <getopt.h> + +/* X Server's headers */ +#include <X11/Xlib.h> +#include <xcb/xcb.h> +#include <xcb/hwa.h> +#include <xcb/dri3.h> +#include <xcb/randr.h> + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#define NUM_ARG 2 +#define NUM_PARAMS 4 + +#define ON 1 +#define OFF 1 + +#define PRINT( a ) ( printf("%d\n", a) ) + +void +fixed_xcb_str_next(xcb_str_iterator_t* it) +{ + it->data = (void*) ((char*) it->data + it->data->name_len + 1); + --it->rem; +} + +void xcb_init( void ); + +void help( void ) +{ + printf( " \nUsage options:\n\n" + " --version HWA query version\n" + " --info list all extension and get some data about HWA \n" + " -c --cursore [0-1] 0:disable 1:enable\n" + " -r --cursorr [0-90-180-270-360] are angle degrees to be turned cursor.\n\n" + " -a --accessibility [0-1] 0:disable 1:enable.\n\n" + " -x --scale [0-1] [x:y:width:height] 0:disable 1:enable \n" + " if enable was chosen x:y:width:height must be specified.\n\n" + " <-SHOW/HIDE Layer options:-> \n" + " -s --show [0-1] 0: UI Layer, 1: XV Layer.\n" + " -h --hide [0-1] 0: UI Layer, 1: XV Layer.\n" + ); +} + +void hwa_query_version( void ); +void hwa_cursor_enable( int enable ); +void hwa_cursor_rotate( int angle ); +void hwa_accessibility( int enable ); +void hwa_scale( int enable, char* scale_param ); +void extensions_info( void ); + +int get_crtc_via_randr( char* output_to_find ); +int* pars_params( char* scale_param ); + +void hwa_show_layer( int layer ); +void hwa_hide_layer( int layer ); + + + + + + +#endif /* HWA_SAMPLE_H_ */ diff --git a/tests/functional/hwc_test/Makefile.am b/tests/functional/hwc_test/Makefile.am new file mode 100644 index 0000000..3a3cfdf --- /dev/null +++ b/tests/functional/hwc_test/Makefile.am @@ -0,0 +1,41 @@ +if HAVE_FT + + lib_LTLIBRARIES = libdri2_dri3.la + libdri2_dri3_la_CFLAGS = $(HWC_TEST_CFLAGS) + libdri2_dri3_la_LIBADD = $(HWC_TEST_LIBS) -lpthread + libdri2_dri3_la_LDFLAGS = -avoid-version + libdri2_dri3_la_SOURCES = aux_dri2_dri3_lib/dri2_dri3.c \ + aux_dri2_dri3_lib/dri2.c \ + aux_dri2_dri3_lib/dri3.c \ + aux_dri2_dri3_lib/fps.c \ + aux_dri2_dri3_lib/aux_draw.c + + bin_PROGRAMS = hwc-sample square-bubbles clock snowflake wander-stripe + + hwc_sample_CFLAGS = $(HWC_TEST_CFLAGS) + hwc_sample_LDADD = $(HWC_TEST_LIBS) -lpthread + hwc_sample_SOURCES = ./hwc_sample/hwc-sample.c \ + ./hwc_sample/hwc-thread.c + + square_bubbles_CFLAGS = $(HWC_TEST_CFLAGS) + square_bubbles_LDADD = $(HWC_TEST_LIBS) -lm -lX11 + square_bubbles_SOURCES = ./square_bubbles/square_bubbles.c + + clock_CFLAGS = $(HWC_TEST_CFLAGS) -I@top_srcdir@/tests/functional/hwc_test/aux_dri2_dri3_lib + clock_LDADD = $(HWC_TEST_LIBS) libdri2_dri3.la -lm + clock_SOURCES = ./clock.c \ + ./aux_apps.c + + snowflake_CFLAGS = $(HWC_TEST_CFLAGS) -I@top_srcdir@/tests/functional/hwc_test/aux_dri2_dri3_lib + snowflake_LDADD = $(HWC_TEST_LIBS) libdri2_dri3.la -lm + snowflake_SOURCES = ./snowflake.c \ + ./aux_apps.c + + wander_stripe_CFLAGS = $(HWC_TEST_CFLAGS) -I@top_srcdir@/tests/functional/hwc_test/aux_dri2_dri3_lib + wander_stripe_LDADD = $(HWC_TEST_LIBS) libdri2_dri3.la + wander_stripe_SOURCES = ./wander_stripe.c \ + ./aux_apps.c + +# install-data-hook: + dist_data_DATA = launch.sh +endif diff --git a/tests/functional/hwc_test/aux_apps.c b/tests/functional/hwc_test/aux_apps.c new file mode 100644 index 0000000..5a1d2fb --- /dev/null +++ b/tests/functional/hwc_test/aux_apps.c @@ -0,0 +1,324 @@ +/************************************************************************** + + auxilary module's source + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <xcb/composite.h> + +#include "aux_apps.h" +#include "dri2_dri3.h" + +extern xcb_rectangle_t wnd_pos; +extern int mode; +extern uint32_t present_bufs; +extern uint32_t swap_interval; +extern uint32_t root_parent; + +// get name of app, which uses this library +//======================================================================== +char* +app_name (void) +{ + FILE* f; + char* slash; + static int initialized; + static char app_name[128]; + + if (initialized) + return app_name; + + // get the application name + f = fopen ("/proc/self/cmdline", "r"); + + if (!f) + return NULL; + + if (fgets (app_name, sizeof(app_name), f) == NULL) + { + fclose (f); + return NULL; + } + + fclose (f); + + if ((slash = strrchr (app_name, '/')) != NULL) + { + memmove (app_name, slash + 1, strlen(slash)); + } + + initialized = 1; + + return app_name; +} + +// +//======================================================================== +void +print_help (void) +{ + printf ("%s - test application.\n" + "This app can be used to test dri2, dri3, present and hwc extensions.\n" + "You can choose work mode among dri2, present or dri3 + present modes.\n" + "You can use this app wiht hwc-sample to test hwc extension (with dri2, present or dri3 + present).\n" + "You can set amount of buffers for present work mode, by default app uses triple buffering in present\n" + "mode, amount of buffers in dri2 mode depends on ddx driver implementation.\n" + "You can use this app with square-bubbles to test properly window's size & position changes.\n" + "To test flip mode, you must launch app in full-screen mode.\n" + "By default frame rate is limited to (vblank)vertical blank (60 Hz), so you cann't see fps more then 60,\n" + "you can disable binding to vblank - set swap interval to 0.\n\n" + "available options:\n" + "\t -geo 'width'x'height' -- set window's size,\n" + "\t -geo 'width'x'height' 'x' 'y' -- set window's geometry,\n" + "\t -dri3 -- dri3 + present Exts. mode (default mode),\n" + "\t -dri2 -- dri2 Exts. mode,\n" + "\t -present -- present Exts. mode,\n" + "\t -present_bufs -- amount of buffers used in no-dri2 mode (default value: 3),\n" + "\t -s_i -- buffers swap interval (both dri2 and present) (default value: 1),\n" + "\t -root_parent -- is parent a root window (default value: 1).\n", app_name()); +} + +// parse command line +// if you want to do some specific for your app - implement yourself's cmd parsing +//======================================================================== +void +cmd_parse (int argc, char* const argv[]) +{ + int i; + + if (argc < 2) return; + + // if we want to set window's size and position + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-geo")) + { + if ((i + 1 < argc) && (i + 2 >= argc)) + sscanf (argv[i + 1], "%hux%hu", &wnd_pos.width, &wnd_pos.height); + else if (i + 3 < argc) + { + sscanf (argv[i + 1], "%hux%hu", &wnd_pos.width, &wnd_pos.height); + sscanf (argv[i + 2], "%hd", &wnd_pos.x); + sscanf (argv[i + 3], "%hd", &wnd_pos.y); + } + break; + } + } + + // if we want to use dri3 + present Exts. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-dri3")) + { + mode = DRI3_PRESENT_MODE; + break; + } + } + + // if we want to use dri2 Ext. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-dri2")) + { + mode = DRI2_MODE; + break; + } + } + + // if we want to use present Exts. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-present")) + { + mode = PRESENT_MODE; + break; + } + } + + // if we want to set amount of buffers for present usage. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-present_bufs") && (argc - i > 1)) + { + sscanf (argv[i + 1], "%u", &present_bufs); + break; + } + } + + // if we want to set buffer swap interval. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-s_i") && (argc - i > 1)) + { + sscanf (argv[i + 1], "%u", &swap_interval); + break; + } + } + + // if we want to insert window between root and window to render. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-root_parent") && (argc - i > 1)) + { + sscanf (argv[i + 1], "%u", &root_parent); + break; + } + } + + // if we want to print help + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "--help") || !strcmp (argv[i], "-help") || !strcmp (argv[i], "-h")) + { + print_help (); + exit (0); + } + } +} + +// check is composite extension existed +//======================================================================== +int +check_composite_ext (xcb_connection_t* dpy) +{ + if (!dpy) return 0; + + xcb_composite_query_version_cookie_t q_c; + xcb_composite_query_version_reply_t* q_r; + xcb_generic_error_t* xcb_errors = NULL; + + q_c = xcb_composite_query_version (dpy, XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MINOR_VERSION); + q_r = xcb_composite_query_version_reply (dpy, q_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + printf ("error while composite_query_version call.\n"); + return 0; + } + else + printf ("composite extension: %d.%d.\n", q_r->major_version, q_r->minor_version); + + free (q_r); + + return 1; +} + +// create window with 'background' background +// root_parent - if cleared, then two windows will be created (parent and child), +// and id of child window will be returned +//=================================================================== +xcb_window_t +create_window (xcb_connection_t* dpy, uint32_t background, uint32_t root_parent, char* name) +{ + uint32_t window_mask; + uint32_t window_values[3]; + xcb_window_t wnd, wnd_child; + xcb_screen_t* screen; + uint16_t border_width; + uint32_t gravity; + int is_composite_ext = 0; + + if (!dpy || !name) + return 0; + + if(root_parent) + is_composite_ext = 0;//check_composite_ext (dpy); + + // copy xeyes behavior... :-) + border_width = root_parent ? 0 : 1; + gravity = root_parent ? XCB_GRAVITY_BIT_FORGET : XCB_GRAVITY_NORTH_WEST; + + // get first screen of display + screen = xcb_setup_roots_iterator (xcb_get_setup (dpy)).data; + + window_mask = XCB_CW_BACK_PIXEL | XCB_CW_BIT_GRAVITY | XCB_CW_EVENT_MASK; + window_values[0] = 0x00;//background; + window_values[1] = gravity; + window_values[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS; + + wnd = xcb_generate_id (dpy); + xcb_create_window (dpy, DEPTH, wnd, screen->root, wnd_pos.x, wnd_pos.y, + wnd_pos.width, wnd_pos.height, border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, window_mask, window_values); + + // hwc-sample and square_bubbles manipulate only named windows + xcb_change_property (dpy, XCB_PROP_MODE_REPLACE, wnd, XCB_ATOM_WM_NAME, + XCB_ATOM_STRING, 8, strlen (name), name); + + // see XSizeHints struct definintion in X11/Xutil.h + // The x, y, width, and height members are now obsolete, but wihtout them + // WM doesn't listen our request about keep window's size. + uint32_t x_size_hints[] = + { + 3, // user specified x, y & user specified width, height + (uint32_t)wnd_pos.x, (uint32_t)wnd_pos.y, + (uint32_t)wnd_pos.width, (uint32_t)wnd_pos.height, + 0, 0, // int min_width, min_height; + 0, 0, // int max_width, max_height; + 0, 0, // int width_inc, height_inc; + 0,0, 0,0, // struct { int x; int y; } min_aspect, max_aspect; + 0, 0, // int base_width, base_height; + 0 // int win_gravity; + }; + + // set WM_NORMAL_HINTS property + xcb_change_property (dpy, XCB_PROP_MODE_REPLACE, wnd, XCB_ATOM_WM_NORMAL_HINTS, + XCB_ATOM_WM_SIZE_HINTS, 32, sizeof(x_size_hints)/sizeof(uint32_t), + x_size_hints); + + if(root_parent && is_composite_ext) + { + printf ("wnd: 0x%x will be redirected.\n", wnd); + xcb_composite_redirect_window (dpy, wnd, XCB_COMPOSITE_REDIRECT_MANUAL); + } + + // Make window visible + xcb_map_window (dpy, wnd); + + // copy xeyes behavior... :-) + if(!root_parent) + { + window_values[1] = XCB_GRAVITY_BIT_FORGET; + + wnd_child = xcb_generate_id (dpy); + xcb_create_window (dpy, DEPTH, wnd_child, wnd, 0, 0, + wnd_pos.width, wnd_pos.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, window_mask, window_values); + + // Make window visible + xcb_map_window (dpy, wnd_child); + + return wnd_child; + } + + return wnd; +} + diff --git a/tests/functional/hwc_test/aux_apps.h b/tests/functional/hwc_test/aux_apps.h new file mode 100644 index 0000000..8292bd6 --- /dev/null +++ b/tests/functional/hwc_test/aux_apps.h @@ -0,0 +1,47 @@ +/************************************************************************** + + auxilary module's header + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#ifndef _AUX_APPS_H +#define _AUX_APPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <xcb/xcb.h> + +void cmd_parse (int argc, char* const argv[]); +xcb_window_t create_window (xcb_connection_t* dpy, uint32_t background, uint32_t root_parent, char* name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/aux_draw.c b/tests/functional/hwc_test/aux_dri2_dri3_lib/aux_draw.c new file mode 100644 index 0000000..0fa5ecf --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/aux_draw.c @@ -0,0 +1,265 @@ +/************************************************************************** + + source of auxiliary functions for direct rendering + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "dri2_dri3.h" + +// set pixel with 'color' in 'buf' on (x, y) position +// no memory bound out checks, do it within caller !!! +//======================================================================== +static inline void +draw_point (int x, int y, void* buf, uint16_t stride, unsigned int color) +{ + *((int*)(buf + x*(BPP/8) + y*stride)) = color; +} + +// draws line(s) into bo (Bresenham algorithm) +// points_num - number of elements of xcb_point_t array +// points - array of points, which will be used to draw line(s) +// line(s) is(are) drawn between each pair of points +// any attempts to draw line(s), which is(are) out of pixmap/bo size are rejected +//========================================================================= +int raw_draw_line (const bo_t* bo_e, uint32_t points_num, const xcb_point_t* points, + unsigned int color) +{ + tbm_bo bo = NULL; + tbm_bo_handle hndl; + uint16_t stride; + uint16_t width, height; + + int x, y, x1, x2, y1, y2; + int dx, dy, error, direct, invert; + int temp, i; + + if (!points || points_num < 2 || !bo_e) + { + printf ("raw_draw_line: invalid input parameters.\n"); + return 1; + } + + hndl.ptr = NULL; + bo = bo_e->bo; + stride = bo_e->stride; + width = bo_e->width; + height = bo_e->height; + + hndl = tbm_bo_map (bo, TBM_DEVICE_CPU, TBM_OPTION_WRITE); + if (!hndl.ptr) + { + printf ("raw_draw_line: Error while tbm_bo_map.\n"); + exit (1); + } + + // x1, y1 - first point (in pixels) + // x2, y2 - second point (in pixels) + // x grows from left to right + // y grows from up to down + // draw (points_num - 1) line(s) + for (i = 0; i < points_num - 1; i++) + { + x1 = points->x; + y1 = points++->y; + x2 = points->x; + y2 = points->y; + + if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0) + { + printf ("raw_draw_line: invalid input parameters.\n"); + continue; + } + + if (x1 > (width - 1) || x2 > (width - 1) || y1 > (height - 1) || y2 > (height - 1)) + { + printf ("raw_draw_line: you are beyond image boundaries:\n" + " x1 = %d, y1 = %d, x2 = %d, y2 = %d, \twidth = %hu, height = %hu\n", + x1, y1, x2, y2, width, height); + continue; + } + + // to draw vertical lines + if (x1 == x2) + { + // we bypass image from bottom to top coordinates, so we must + // swap points if it necessary + if (y2 < y1) + { + temp = y1; + y1 = y2; + y2 = temp; + } + + for (y = y1; y <= y2; y++) + draw_point (x1, y, hndl.ptr, stride, color); + + continue; + } + + // if y-component of line is changed quickly then x-component, + // we must exchange axis to use Bresenham algorithm more effectively, + // due to that we have for cycle from x1 to x2, therefore we can change + // body of for cycle insted exchange axis, but this's not pretty + invert = abs (y2 - y1) > abs (x2 - x1); + if (invert) + { + temp = x1; + x1 = y1; + y1 = temp; + + temp = x2; + x2 = y2; + y2 = temp; + } + + // algorithm can draw line only if x of first point is less + // then x of second point, so we simple swap points if it's necessary + if (x1 > x2) + { + temp = x1; + x1 = x2; + x2 = temp; + + temp = y1; + y1 = y2; + y2 = temp; + } + + // where do we go while draw line: up(or straight) or down + if (y2 < y1) + direct = -1; + else + direct = 1; + + error = 0; + dx = x2 - x1; + dy = abs(y2 - y1); + + // to draw another cases of line (it's Bresenham algorithm implementation, + // which supports varios line's directions) + for (x = x1, y = y1; x <= x2; x++) + { + draw_point (invert ? y : x, invert ? x : y, hndl.ptr, stride, color); + + error += dy; + + if (error * 2 >= dx) + { + y += direct; + error -= dx; + } + } + } // for (i = 0; i < points_num - 1; i++) + + tbm_bo_unmap (bo); + + return 0; +} + +// draw filled rect into bo +// rect_num - amount of rectangles to draw (in current implemantation must be '1') +// rects - array of rectangles to draw +// any attempts to draw rectangle, which is out of pixmap/bo size are rejected +// draw only one rectangle !!! +//========================================================================= +int raw_fill_rect (const bo_t* bo_e, uint32_t rect_num, const xcb_rectangle_t* rects, + unsigned int color) +{ + tbm_bo bo = NULL; + tbm_bo_handle hndl; + uint16_t height; + uint16_t width; + uint16_t stride; + int i, j; + + if ( + rects->x < 0 || rects->y < 0 || rects->width <= 0 || rects->height <= 0 || + !rect_num || rect_num > 1 || !rects || !bo_e + ) + { + printf ("raw_fill_rect: invalid input parameters.\n"); + return 1; + } + + bo = bo_e->bo; + stride = bo_e->stride; + width = bo_e->width; + height = bo_e->height; + + if (rects->x + rects->width > width || rects->y + rects->height > height) + { + printf ("raw_fill_rect: you are beyond image boundaries:\n" + " x = %d, y = %d, width = %d, height = %d, widht_bo = %hu, height_bo = %hu.\n", + rects->x, rects->y, rects->width, rects->height, width, height); + return 1; + } + + hndl = tbm_bo_map (bo, TBM_DEVICE_CPU, TBM_OPTION_WRITE); + if (!hndl.ptr) + { + printf ("raw_fill_rect: Error while tbm_bo_map.\n"); + exit (1); + } + + // this code works quikly then below code. + // if caller wants to clear (black color) all bo. + // another colors cann't be used due to memset semantic + // (memset use only low byte of second parameter) + if (width == rects->width && height == rects->height && !color) + { + memset (hndl.ptr, 0, bo_e->stride * bo_e->height); + goto success; + } + + // x grows from left to right + // y grows from up to down + + // shift to pixel, from which we must draw + hndl.ptr += rects->x*(BPP/8) + rects->y*stride; + + // memory boundaries were checked above + for (i = 0; i < rects->height; i++) + { + for (j = 0; j < rects->width; j++) + { + *((int*)hndl.ptr) = (int)color; + hndl.ptr += (BPP/8); + } + + // shift to pixel, from which we must draw + hndl.ptr -= j*(BPP/8) - stride; + } + +success: + tbm_bo_unmap (bo); + + return 0; +} diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2.c b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2.c new file mode 100644 index 0000000..0340f13 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2.c @@ -0,0 +1,394 @@ +/************************************************************************** + + dri2 backend + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implements preparing dri2 extension to use and dri2 events handling + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#include <fcntl.h> + +#include <xcb/dri2.h> + +#include "dri2_dri3_inner.h" + + +#define CACHE_BO_SIZE (3) + +typedef struct dri2_info_t +{ + int drm_fd; + drm_magic_t drm_magic; + char* device_name; + char* driver_name; + tbm_bufmgr bufmgr; + const xcb_query_extension_reply_t* dri2_ext; +}dri2_info_t; + +static dri2_info_t dri2_info; + +// cycle bo's cache +static bo_t bo_cache[CACHE_BO_SIZE]; +static uint32_t bo_cache_idx; + + +// next two functions avoid call to tbm_bo_import, when we have tbm_bo for +// appropriate buf_name, returned by dri2 extension + +// returns, if exist, tbm_bo for gem object's name 'buf_name' +//======================================================================== +static tbm_bo +get_tbm_bo_from_cache (uint32_t buf_name) +{ + int i; + + for (i = 0; i < CACHE_BO_SIZE; i++) + { + if (bo_cache[i].name == buf_name) return bo_cache[i].bo; + } + + return NULL; +} + +// add bo to cycle bo's cache +//======================================================================== +static void +add_bo_to_cache (const bo_t* bo) +{ + if (!bo) return; + + if (bo_cache[bo_cache_idx].bo) + tbm_bo_unref(bo_cache[bo_cache_idx].bo); + + memcpy ((bo_cache + bo_cache_idx++), bo, sizeof(bo_t)); + bo_cache_idx %= CACHE_BO_SIZE; +} + +// wrap over DRI2GetBuffers request +// return all information for rendering +//======================================================================== +static bo_t +get_bo (void) +{ + bo_t bo; + uint32_t attachments; + xcb_dri2_get_buffers_cookie_t get_buf_c; + xcb_dri2_get_buffers_reply_t* get_buf_r = NULL; + xcb_generic_error_t* xcb_errors = NULL; + xcb_dri2_dri2_buffer_t* dri2_buf = NULL; + + attachments = XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT; + + get_buf_c = xcb_dri2_get_buffers (display.dpy, display.wnd, 1, 1, &attachments); + get_buf_r = xcb_dri2_get_buffers_reply (display.dpy, get_buf_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + set_error ("Error while xcb_dri2_get_buffers call.\n"); + pthread_cancel(thread_id); + } + + dri2_buf = xcb_dri2_get_buffers_buffers (get_buf_r); + if (!dri2_buf) + { + free (get_buf_r); + set_error ("dri2_buf = NULL.\n"); + pthread_cancel(thread_id); + } + + bo.stride = dri2_buf->pitch; + bo.width = get_buf_r->width; + bo.height = get_buf_r->height; + bo.name = dri2_buf->name; + + // cache bos + bo.bo = get_tbm_bo_from_cache (bo.name); + if (!bo.bo) + { + bo.bo = tbm_bo_import (dri2_info.bufmgr, bo.name); + add_bo_to_cache (&bo); + } + + // this memory will be used for next xcb_dri2_get_buffers_buffers calls, + // so don't need to free it + //free (dri2_buf); + + DEBUG_OUT ("buf_name: %u, bo: %p, stride: %u, cpp: %u, flags: %u, width: %u, " + "height: %u, count: %u\n", + bo.name, bo.bo, bo.stride, dri2_buf->cpp, dri2_buf->flags, + bo.width, bo.height, get_buf_r->count); + + free (get_buf_r); + + return bo; +} + +// this function is called in event handling and calls app's callbacks to draw +//======================================================================== +static void +draw (void) +{ + bo_t bo; + + TIME_START(); + bo = get_bo (); + TIME_END ("get_bo"); + + TIME_START(); + + if (gr_ctx.draw_funcs.raw_clear) + gr_ctx.draw_funcs.raw_clear (&bo); + + if (gr_ctx.draw_funcs.raw_draw) + gr_ctx.draw_funcs.raw_draw (&bo); + + TIME_END ("draw"); +} + +// wrap over DRI2SwapBuffers request +// this function is called in event handling +//======================================================================== +static void +swap_buffers (uint32_t msc_hi, uint32_t msc_lo) +{ + DEBUG_OUT ("ask to swap buffers for window: 0x%x at msc_hi: %u, msc_lo: %u\n", + display.wnd, msc_hi, msc_lo); + + TIME_START(); + xcb_dri2_swap_buffers (display.dpy, display.wnd, msc_hi, msc_lo, 0, 0, 0 ,0); + TIME_END ("xcb_dri2_swap_buffers"); + +#ifdef _DEBUG_ + printf ("----------------------------\n"); +#endif +} + + +//======================================================================== +//======================================================================== +//======================================================================== + + +// check dri2 extension, connect, authenticate, create dri2 drawable +// and initiate tizen buffer manager +// return 0 if success, 1 otherwise +//=================================================================== +int +prepare_dri2_ext (void) +{ + xcb_dri2_query_version_cookie_t query_ver_c; + xcb_dri2_query_version_reply_t* query_ver_r = NULL; + xcb_generic_error_t* xcb_errors = NULL; + + xcb_dri2_connect_cookie_t connect_c; + xcb_dri2_connect_reply_t* connect_r = NULL; + + xcb_dri2_authenticate_cookie_t auth_c; + xcb_dri2_authenticate_reply_t* auth_r = NULL; + + int res; + + // whait for main thread + usleep (500000); + + // dri2_query_version + + query_ver_c = xcb_dri2_query_version (display.dpy, XCB_DRI2_MAJOR_VERSION, XCB_DRI2_MINOR_VERSION); + query_ver_r = xcb_dri2_query_version_reply (display.dpy, query_ver_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + set_error ("Error while dri2_query_version call.\n"); + return 1; + } + + printf ("DRI2 Extension: %u.%u.\n", query_ver_r->major_version, query_ver_r->minor_version); + + free (query_ver_r); + + // dri2_connect + + connect_c = xcb_dri2_connect (display.dpy, display.screen->root, XCB_DRI2_DRIVER_TYPE_DRI); + connect_r = xcb_dri2_connect_reply (display.dpy, connect_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + set_error ("Error while dri2_connect call.\n"); + return 1; + } + + dri2_info.driver_name = xcb_dri2_connect_driver_name (connect_r); + dri2_info.device_name = xcb_dri2_connect_device_name (connect_r); + free (connect_r); + + printf ("DRI2 driver_name: %s, device_name: %s.\n", dri2_info.driver_name, dri2_info.device_name); + + // open drm device + dri2_info.drm_fd = open (dri2_info.device_name, O_RDWR); + if (dri2_info.drm_fd < 0) + { + char* temp = malloc (512); + + sprintf (temp, "Error while open call for file: %s.\n", dri2_info.device_name); + set_error (temp); + free (temp); + + return 1; + } + + // dri2_authenticate + res = drmGetMagic (dri2_info.drm_fd, &dri2_info.drm_magic); + if (res) + { + close (dri2_info.drm_fd); + set_error ("Error while drmGetMagic call.\n"); + return 1; + } + + auth_c = xcb_dri2_authenticate (display.dpy, display.screen->root, (uint32_t)dri2_info.drm_magic); + auth_r = xcb_dri2_authenticate_reply (display.dpy, auth_c, &xcb_errors); + if (xcb_errors) + { + close (dri2_info.drm_fd); + free (xcb_errors); + set_error ("Error while dri2_authenticate call.\n"); + return 1; + } + + if (!auth_r->authenticated) + { + close (dri2_info.drm_fd); + set_error ("Error while dri2_authenticate call.\n"); + return 1; + } + + free (auth_r); + + xcb_dri2_create_drawable (display.dpy, display.wnd); + + // for determine dri2's events + dri2_info.dri2_ext = xcb_get_extension_data (display.dpy, &xcb_dri2_id); + + dri2_info.bufmgr = tbm_bufmgr_init (dri2_info.drm_fd); + + return 0; +} + +// dri2 events loop +//======================================================================== +void +dri2_loop (void) +{ + xcb_generic_event_t* event = NULL; +#ifndef _DEBUG_ + struct timespec start; +#endif + uint64_t target_msc = 0; + + draw (); + swap_buffers (0, 0); + + while (1) + { + xcb_flush (display.dpy); + DEBUG_OUT("before xcb_wait_for_event.\n"); + event = xcb_wait_for_event (display.dpy); + DEBUG_OUT("after xcb_wait_for_event.\n"); + if (!event) + { + printf("event is NULL.\n"); + break; + } + + switch (event->response_type) + { + default: + { + // if XCB_DRI2_BUFFER_SWAP_COMPLETE event + if (event->response_type == dri2_info.dri2_ext->first_event + + XCB_DRI2_BUFFER_SWAP_COMPLETE) + { + xcb_dri2_buffer_swap_complete_event_t* ev; + ev = (xcb_dri2_buffer_swap_complete_event_t*)event; + +#ifdef _DEBUG_ + DEBUG_OUT ("XCB_DRI2_BUFFER_SWAP_COMPLETE:\n"); + switch (ev->event_type) + { + case XCB_DRI2_EVENT_TYPE_EXCHANGE_COMPLETE: + DEBUG_OUT (" EXCHANGE_COMPLETE.\n"); + break; + case XCB_DRI2_EVENT_TYPE_BLIT_COMPLETE: + DEBUG_OUT (" BLIT_COMPLETE.\n"); + break; + case XCB_DRI2_EVENT_TYPE_FLIP_COMPLETE: + DEBUG_OUT (" FLIP_COMPLETE.\n"); + break; + } + DEBUG_OUT (" drawable: 0x%x, msc_hi: %u, msc_lo: %u, sbc: %u, " + "ust_hi: %u, ust_lo: %u\n", + ev->drawable, ev->msc_hi, ev->msc_lo, ev->sbc, + ev->ust_hi, ev->ust_lo); + +#endif + // adjust target_msc to real msc + target_msc = ((uint64_t)ev->msc_hi << 32) | (uint64_t)ev->msc_lo; + + // prepare target_msc for use when DRI2InvalidateBuffers event is came + target_msc += 2*gr_ctx.params.swap_interval; + } + else if (event->response_type == dri2_info.dri2_ext->first_event + + XCB_DRI2_INVALIDATE_BUFFERS) + { +#ifdef _DEBUG_ + xcb_dri2_invalidate_buffers_event_t* ev; + ev = (xcb_dri2_invalidate_buffers_event_t*)event; + + DEBUG_OUT ("XCB_DRI2_INVALIDATE_BUFFERS.\n drawable: 0x%x\n", ev->drawable); +#else + fps_calc( &start ); + clock_gettime( CLOCK_REALTIME, &start ); +#endif + // we draw frame when obtained DRI2InvalidateBuffers event due to be able to + // handle situation when DRI2InvalidateBuffers event signals about mismatching + // drawable and buffer sizes and DRI2BufferSwapComplete event will not be issued and + // delivered to us and we will hang out in xcb_wait_for_event, so we draw frame + // here and for swap it use target_msc obtained from DRI2BufferSwapComplete event + // on previous swap + draw (); + swap_buffers ((uint32_t)(target_msc >> 32), (uint32_t)(target_msc & 0xFFFFFFFF)); + } + } + break; + } + }// while (1) +} diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.c b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.c new file mode 100644 index 0000000..b59cc57 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.c @@ -0,0 +1,227 @@ +/************************************************************************** + + dri2_dri3 dispatcher's source + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implemets dri2/dri3 dispatcher and some auxiliary functions + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <pthread.h> + +#include "dri2_dri3_inner.h" + +display_t display; +graphics_ctx gr_ctx; +pthread_t thread_id; +xcb_rectangle_t wnd_pos; + +static char* error; + +static void* dri2_dri3 (void* arg); +static int init_graphic_context (void); + +// this function gets copy of passed arguments !!! +// dpy - connection to Xserver, initialized by client, used for obtain wnd's geometry +// draw_funcs - set of callback function to drawing, which functions to use +// will be dedicated according to mode value +// mode - mode in which dri2_dri3 module will work +// window - window's id in which drawing will be occured +// params - dri2/dri3 configure parameters +//========================================================================= +int init_dri2_dri3 (xcb_connection_t* dpy, const draw_funcs_t* draw_funcs, + int mode, xcb_window_t window, const dri2_dri3_params_t* params) +{ + int res; + + if (!draw_funcs || !dpy || !params) + { + set_error ("invalid input arguments"); + return 1; + } + + display.client_dpy = dpy; // used for obtain window's geometry only + display.dpy = xcb_connect (NULL, NULL); // dpy from client cann't be used + // for processing dri2/dri3 events + // get first screen of display + display.screen = xcb_setup_roots_iterator (xcb_get_setup (display.dpy)).data; + display.wnd = window; + + gr_ctx.mode = mode; + memcpy (&gr_ctx.draw_funcs, draw_funcs, sizeof (gr_ctx.draw_funcs)); + memcpy (&gr_ctx.params, params, sizeof (gr_ctx.params)); + + get_wnd_geometry (display.wnd, &wnd_pos); + + res = pthread_create (&thread_id, NULL, dri2_dri3, NULL); + if (res) + { + set_error ("error while pthread_create"); + return 1; + } + + return 0; +} + +// any dri2_dri3 module's functions return error code or message, +// only DRI2_DRI3_TRUE or DRI2_DRI3_FALSE +// use this function, if you need, to obtain more detail information about error +// you must free memory, pointed by returned value +//========================================================================= +char* get_last_error (void) +{ + char* temp; + + if (!error) return NULL; + + // TODO: need thread synchronize + temp = strdup (error); + free (error); + error = NULL; + + return temp; +} + + +//======================================================================== +//======================================================================== +//======================================================================== + + +// dri2/dri3 xcb events loop +//======================================================================== +static void* +dri2_dri3 (void* arg) +{ + int res; + + res = init_graphic_context (); + if (res) + pthread_cancel(thread_id); + + switch (gr_ctx.mode) + { + case DRI2_MODE: + { + dri2_loop (); + } + break; + + case DRI3_PRESENT_MODE: + case PRESENT_MODE: + { + dri3_loop (); + } + break; + } + + return NULL; +} + +// fill gr_ctx struct to use it on drawing and presenting on screen phases +// return 0 if success, 1 otherwise +//=================================================================== +static int +init_graphic_context (void) +{ + int res; + + switch (gr_ctx.mode) + { + case DRI2_MODE: + { + res = prepare_dri2_ext (); + if (res) return 1; + + printf ("usecase: dri2.\n"); + } + break; + + case DRI3_PRESENT_MODE: + case PRESENT_MODE: + { + res = prepare_dri3_present_ext (); + if (res) return 1; + + if (gr_ctx.mode == DRI3_PRESENT_MODE) + printf ("usecase: dri3 + present.\n"); + else + printf ("usecase: present.\n"); + } + break; + + default: + printf ("invalid gr_ctx.mode.\n"); + break; + } + + return 0; +} + +// return geometry of specified, by win_id, window +//======================================================================== +void +get_wnd_geometry (xcb_window_t win_id, xcb_rectangle_t* geometry) +{ + xcb_get_geometry_cookie_t cookie; + xcb_get_geometry_reply_t* reply = NULL; + xcb_generic_error_t* error = NULL; + + if (!geometry) + { + printf ("error while get_wnd_geometry call.\n"); + exit (1); + } + + cookie = xcb_get_geometry (display.client_dpy, win_id); + reply = xcb_get_geometry_reply (display.client_dpy, cookie, &error); + if (error) + { + printf ("error while xcb_get_geometry call.\n"); + free (error); + exit (1); + } + + geometry->x = reply->x; + geometry->y = reply->y; + geometry->width = reply->width; + geometry->height = reply->height; + + free (reply); +} + +// for inner use +//=================================================================== +void +set_error (const char* str) +{ + // to prevent rewriting of previous error message + if (!error) error = strdup (str); +} diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.h b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.h new file mode 100644 index 0000000..5b12e44 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3.h @@ -0,0 +1,135 @@ +/************************************************************************** + + dri2_dri3 library's source + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// dri2_dri3 library's API + +#ifndef _DRI2_DRI3_H +#define _DRI2_DRI3_H + +#include <xcb/xcb.h> +#include <tbm_bufmgr.h> + +#ifdef _DEBUG_ + #define DEBUG_OUT(format, args...) { printf("%s: "format, __FUNCTION__, ##args); } +#else + #define DEBUG_OUT(...) {;} +#endif + +#define BPP (32) // bits per pixel +#define DEPTH (24) + +#define DRI2_DRI3_TRUE 0x1 +#define DRI2_DRI3_FALSE 0x0 + +// modes for use in init_dri2_dri3() function +#define DRI2_MODE 0x0 +#define DRI3_MODE 0x1 +#define DRI3_PRESENT_MODE 0x2 +#define PRESENT_MODE 0x4 + +typedef struct dri2_dri3_params_t +{ + uint32_t num_of_bufs; // number of buffers for use with Present extension only + uint64_t swap_interval; // buffer(frame) swap interval +} dri2_dri3_params_t; + +// describes buffer for direct access +typedef struct bo_t +{ + tbm_bo bo; // these bos represents such gem object as pxmap in graphics_ctx_t structure + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t name; // name of gem object, used only by dri2 + int dma_buf_fd; // fd of dmabuf file, used only by dri3 +} bo_t; + +typedef void (*xcb_clear_func_ptr)(xcb_drawable_t drawable, uint32_t draw_w, uint32_t draw_h); +typedef void (*xcb_draw_func_ptr)(xcb_drawable_t drawable, uint32_t draw_w, uint32_t draw_h); + +typedef void (*raw_clear_func_ptr)(const bo_t* bo_e); +typedef void (*raw_draw_func_ptr)(const bo_t* bo_e); + +typedef struct draw_funcs_t +{ + xcb_clear_func_ptr xcb_clear; + xcb_draw_func_ptr xcb_draw; + raw_clear_func_ptr raw_clear; + raw_draw_func_ptr raw_draw; +} draw_funcs_t; + + +// this function gets copy of passed arguments !!! +// dpy - connection to Xserver, initialized by client, used for obtain wnd's geometry +// draw_funcs - set of callback function to drawing, which functions to use +// will be dedicated according to mode value +// mode - mode in which dri2_dri3 module will work +// window - window's id in which drawing will be occured +// params - dri2/dri3 configure parameters +// Note: if you use PRESENT_MODE or DRI3_PRESENT_MODE size of pixmaps or bos +// mayn't be equal size of window, now it isn't used, but maybe in future... +// therefore xcb_clear_func_ptr and xcb_draw_func_ptr functions have passed +// real size of pixmaps or bos as second and third arguments +__attribute__ ((visibility ("default"))) +int init_dri2_dri3 (xcb_connection_t* dpy, const draw_funcs_t* draw_funcs, + int mode, xcb_window_t window, const dri2_dri3_params_t* params); + +// any dri2_dri3 module's functions return error code or message, +// only DRI2_DRI3_TRUE or DRI2_DRI3_FALSE +// use this function, if you need, to obtain more detail information about error +// you must free memory, pointed by returned value +__attribute__ ((visibility ("default"))) +char* get_last_error (void); + +// you can use this auxiliary functions to direct drawing in bo + +// draws line(s) into tbm_bo (Bresenham algorithm) +// bo_e - pointer to struct returned by raw_draw or raw_clear function +// points_num - number of elements of xcb_point_t array +// points - array of points, which will be used to draw line(s) +// color - color of line(s) +// line(s) is(are) drawn between each pair of points +// any attempts to draw line(s), which is(are) out of pixmap/bo size are rejected +__attribute__ ((visibility ("default"))) +int raw_draw_line (const bo_t* bo_e, uint32_t points_num, const xcb_point_t* points, + unsigned int color); + +// draw filled rect into tbm_bo +// bo_e - pointer to struct returned by raw_draw or raw_clear function +// rect_num - amount of rectangles to draw (in current implemantation must be '1') +// rects - array of rectangles to draw +// color - color of rectangle +// any attempts to draw rectangle, which is out of pixmap/bo size are rejected +// draw only one rectangle !!! +__attribute__ ((visibility ("default"))) +int raw_fill_rect (const bo_t* bo_e, uint32_t rect_num, const xcb_rectangle_t* rects, + unsigned int color); + +#endif diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3_inner.h b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3_inner.h new file mode 100644 index 0000000..491d267 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri2_dri3_inner.h @@ -0,0 +1,82 @@ +/************************************************************************** + + dri2_dri3 library's inner header + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#ifndef _DRI2_DRI3_INNER_H +#define _DRI2_DRI3_INNER_H + +#include <xf86drm.h> + +#include "dri2_dri3.h" +#include "fps.h" + +#ifdef _DEBUG_ + #define TIME_START()\ + {\ + struct timespec start;\ + clock_gettime (CLOCK_REALTIME, &start); + #define TIME_END(str) \ + DEBUG_OUT ("%s: %f.\n", str, get_time_diff (&start) / 1000000.0f);\ + } +#else + #define TIME_START() {;} + #define TIME_END(...) {;} +#endif + +typedef struct display_t +{ + xcb_connection_t* dpy; + xcb_connection_t* client_dpy; + xcb_screen_t* screen; + xcb_window_t wnd; +} display_t; + +typedef struct graphics_ctx_t +{ + int mode; + draw_funcs_t draw_funcs; + dri2_dri3_params_t params; +} graphics_ctx; + +extern display_t display; +extern graphics_ctx gr_ctx; +extern pthread_t thread_id; + +extern xcb_rectangle_t wnd_pos; + +void get_wnd_geometry (xcb_window_t win_id, xcb_rectangle_t* geometry); +void set_error (const char* str); + +int prepare_dri2_ext (void); +void dri2_loop (void); + +int prepare_dri3_present_ext (void); +void dri3_loop (void); + +#endif diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/dri3.c b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri3.c new file mode 100644 index 0000000..0075fc6 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/dri3.c @@ -0,0 +1,542 @@ +/************************************************************************** + + dri3 backend + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implements preparing dri3 extension to use and dri3 events handling + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> + +#include <xcb/dri3.h> +#pragma pack(push, 1) +#include <xcb/present.h> +#pragma pack(pop) + + +#include "dri2_dri3_inner.h" + +typedef struct dri3_buffer_t +{ + xcb_pixmap_t pxmap; + bo_t bo; + xcb_rectangle_t pxmap_size; // bo size is equal pixmap size and can be found in bo field + uint32_t busy; // whether pixmap busy or not + + // whether pixmap must be reallocated, due to wnd size changes, or not + uint32_t buff_must_be_realloc; +} dri3_buffer_t; + +typedef struct dri3_present_info_t +{ + int drm_fd; + tbm_bufmgr bufmgr; + const xcb_query_extension_reply_t* present_ext; +} dri3_present_info_t; + +static dri3_buffer_t* dri3_buffers; +static dri3_present_info_t dri3_present_info; + +static uint64_t target_msc; + +// check present extenstion +// return 0 if success, 1 otherwise +//=================================================================== +static int +check_present_ext (void) +{ + xcb_present_query_version_cookie_t c; + xcb_present_query_version_reply_t* r = NULL; + xcb_generic_error_t* xcb_errors = NULL; + + c = xcb_present_query_version (display.dpy, XCB_PRESENT_MAJOR_VERSION, XCB_PRESENT_MINOR_VERSION); + r = xcb_present_query_version_reply (display.dpy, c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("Error while Present Extension query_version call.\n"); + return 1; + } + else + printf ("Present Extension: %d.%d.\n", r->major_version, r->minor_version); + + free (r); + + return 0; +} + +// check dri3 extenstion and ask X server to send us auth drm_fd (fd of drm device file) +// return -1, if error was occured, +// otherwise drm_fd +//=================================================================== +static int +prepare_dri3_ext (void) +{ + int* drm_fd_p = NULL; + int drm_fd; + xcb_dri3_query_version_cookie_t query_version_c; + xcb_dri3_query_version_reply_t* query_version_r = NULL; + xcb_generic_error_t* xcb_errors = NULL; + + xcb_dri3_open_cookie_t open_c; + xcb_dri3_open_reply_t* open_r = NULL; + + // query version --------------------- + + query_version_c = xcb_dri3_query_version (display.dpy, XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION); + query_version_r = xcb_dri3_query_version_reply (display.dpy, query_version_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("Error while DRI3 Extension query_version call.\n"); + return -1; + } + else + printf ("DRI3 Extension: %d.%d.\n", query_version_r->major_version, query_version_r->minor_version); + + free (query_version_r); + + // open ------------------------------ + + // I don't know why open call require drawable, maybe it dedicates screen and drm device + // which is responsible for showing this screen ? + // about third parameter I'm still in confuse. + open_c = xcb_dri3_open (display.dpy, (xcb_drawable_t)display.screen->root, 0); + open_r = xcb_dri3_open_reply (display.dpy, open_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("Error while DRI3 Extension open call.\n"); + return -1; + } + + drm_fd_p = xcb_dri3_open_reply_fds (display.dpy, open_r); + drm_fd = *drm_fd_p; + + printf (" drm fd provided by DRI3: %d.\n", drm_fd); + printf (" OBSERVE: nfd returned by open_dri: %d.\n", open_r->nfd); + + // this memory will be used for next xcb_dri3_open_reply_fds calls, if we will do them, + // so don't need to free it + //free (drm_fd_p); + free (open_r); + + return drm_fd; +} + +// obtains tbm_bo(bo_t) from pixmap via dri3 extension +// aborts app, if error was occured, +// otherwise returns tbm_bo(bo_t), that represents such gem object that pixmap represents +//=================================================================== +static bo_t +get_bo_from_pixmap (xcb_pixmap_t pixmap) +{ + bo_t bo; + int* dma_buf_fd = NULL; + xcb_dri3_buffer_from_pixmap_cookie_t bf_from_pixmap_c; + xcb_dri3_buffer_from_pixmap_reply_t* bf_from_pixmap_r = NULL; + xcb_generic_error_t* xcb_errors = NULL; + + DEBUG_OUT ("before xcb_dri3_buffer_from_pixmap.\n"); + bf_from_pixmap_c = xcb_dri3_buffer_from_pixmap (display.dpy, pixmap); + + DEBUG_OUT ("before xcb_dri3_buffer_from_pixmap_reply.\n"); + bf_from_pixmap_r = xcb_dri3_buffer_from_pixmap_reply (display.dpy, bf_from_pixmap_c, + &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + printf ("Error while DRI3 Extension buffer_from_pixmap call.\n"); + exit (1); + } + + DEBUG_OUT ("size: %u, w: %hu, h: %hu, stride: %hu, bpp: %hhu, depth: %hhu.\n", + (bf_from_pixmap_r)->size, (bf_from_pixmap_r)->width, + (bf_from_pixmap_r)->height, (bf_from_pixmap_r)->stride, + (bf_from_pixmap_r)->bpp, (bf_from_pixmap_r)->depth); + + dma_buf_fd = xcb_dri3_buffer_from_pixmap_reply_fds (display.dpy, bf_from_pixmap_r); + + DEBUG_OUT ("dma_buf fd, returned by dri3, for pixmap[0x%06x]: %d.\n", pixmap, *dma_buf_fd); + + bo.bo = tbm_bo_import_fd (dri3_present_info.bufmgr, (tbm_fd)(*dma_buf_fd)); + if (!bo.bo) + { + free (bf_from_pixmap_r); + printf ("Error while tbm_bo_import_fd call.\n"); + exit (1); + } + + bo.dma_buf_fd = *dma_buf_fd; + bo.width = bf_from_pixmap_r->width; + bo.height = bf_from_pixmap_r->height; + bo.stride = bf_from_pixmap_r->stride; + // now, I don't see any necessity to use these values + // bf_from_pixmap_r->depth; + // bf_from_pixmap_r->bpp; + + DEBUG_OUT ("OBSERVE: nfd returned by buffer_from_pixmap: %d.\n", bf_from_pixmap_r->nfd); + + // this memory will be used for next xcb_dri3_buffer_from_pixmap_reply_fds calls, + // so don't need to free it + //free (dma_buf_fd); + free (bf_from_pixmap_r); + + return bo; +} + +// this function is called in event handling and calls app's callbacks to draw +//======================================================================== +static void +draw (uint32_t idx) +{ + if (gr_ctx.mode == DRI3_PRESENT_MODE || gr_ctx.mode == DRI3_MODE) + { + bo_t back_bo; + + // library client must not be able to do any changes in real bo object, + // now it isn't important, but what about a future ? + memcpy (&back_bo, &dri3_buffers[idx].bo, sizeof (back_bo)); + + if (gr_ctx.draw_funcs.raw_clear) + gr_ctx.draw_funcs.raw_clear (&back_bo); + + if (gr_ctx.draw_funcs.raw_draw) + gr_ctx.draw_funcs.raw_draw (&back_bo); + + DEBUG_OUT ("render in tbm bo: 0x%p (tied pixmap: 0x%x)\n", dri3_buffers[idx].bo.bo, + dri3_buffers[idx].pxmap); + } + else if (gr_ctx.mode == PRESENT_MODE) + { + xcb_pixmap_t back_pxmap; + xcb_rectangle_t pxmap_size; + + back_pxmap = dri3_buffers[idx].pxmap; + pxmap_size = dri3_buffers[idx].pxmap_size; + + if (gr_ctx.draw_funcs.xcb_clear) + gr_ctx.draw_funcs.xcb_clear (back_pxmap, pxmap_size.width, pxmap_size.height); + + if (gr_ctx.draw_funcs.xcb_draw) + gr_ctx.draw_funcs.xcb_draw (back_pxmap, pxmap_size.width, pxmap_size.height); + + DEBUG_OUT ("render in pixmap: 0x%x.\n", back_pxmap); + } +} + +// wrap over present pixmap request +//======================================================================== +static void +swap_buffers (uint64_t msc, uint32_t idx) +{ + xcb_pixmap_t back_pxmap; + + back_pxmap = dri3_buffers[idx].pxmap; + + xcb_present_pixmap (display.dpy, display.wnd, back_pxmap, idx, + 0, 0, 0, 0, 0, 0, 0, XCB_PRESENT_OPTION_NONE, msc, 0, 0, 0, NULL); + + DEBUG_OUT ("swap pixmap[%d]: 0x%x, msc: %llu\n", idx, back_pxmap, msc); + + // block pixmap/bo to next draw and swap operations + dri3_buffers[idx].busy = 1; +} + +// reallocate dri3_buff and corresponding pixmap and tbm bo +// idx - index inner array of dri3 buffers +//=================================================================== +static void +realloc_dri3_buff (uint32_t idx, uint16_t width, uint16_t height) +{ + if (idx >= gr_ctx.params.num_of_bufs) return; + + // release dmabuf file, old bo and pixmap + if (gr_ctx.mode != PRESENT_MODE) + { + if (dri3_buffers[idx].bo.bo) + { + tbm_bo_unref(dri3_buffers[idx].bo.bo); + DEBUG_OUT ("tbm bo: 0x%p has been unrefed.\n", dri3_buffers[idx].bo.bo); + } + + if (dri3_buffers[idx].bo.dma_buf_fd) + close (dri3_buffers[idx].bo.dma_buf_fd); + } + + if (dri3_buffers[idx].pxmap) + { + xcb_free_pixmap(display.dpy, dri3_buffers[idx].pxmap); + DEBUG_OUT ("pixmap: 0x%x has been freed.\n", dri3_buffers[idx].pxmap); + } + + // create new bo and pixmap + dri3_buffers[idx].pxmap_size.width = width; + dri3_buffers[idx].pxmap_size.height = height; + dri3_buffers[idx].pxmap = xcb_generate_id (display.dpy); + xcb_create_pixmap (display.dpy, DEPTH, dri3_buffers[idx].pxmap, display.wnd, + dri3_buffers[idx].pxmap_size.width, + dri3_buffers[idx].pxmap_size.height); + + DEBUG_OUT ("pixmap: 0x%x has been created.\n", dri3_buffers[idx].pxmap); + + // creates bo, tied toward pixmap, to do direct drawing in it + if (gr_ctx.mode != PRESENT_MODE) + dri3_buffers[idx].bo = get_bo_from_pixmap (dri3_buffers[idx].pxmap); + + dri3_buffers[idx].buff_must_be_realloc = 0; + + DEBUG_OUT ("buffer: %u has been reallocated (tbm_bo: 0x%p, pixamp: 0x%x, wxh: %hux%hu).\n", + idx, dri3_buffers[idx].bo.bo, dri3_buffers[idx].pxmap, width, height); +} + +//======================================================================== +//======================================================================== +//======================================================================== + + +// checks dri3 and present extensions, does dri3_open request and initiates +// tizen buffer manager +// return 0 if success, 1 otherwise +//=================================================================== +int +prepare_dri3_present_ext (void) +{ + uint32_t present_eid; + int i; + + // present initialization + + if (check_present_ext ()) return 1; + + // ask present extension to notify us about events + present_eid = xcb_generate_id (display.dpy); + xcb_present_select_input (display.dpy, present_eid, display.wnd, + XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY | + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY | + XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); + + // for determine dri3's events + dri3_present_info.present_ext = xcb_get_extension_data (display.dpy, &xcb_present_id); + if (!dri3_present_info.present_ext) + { + printf ("Error while xcb_get_extension_data call.\n"); + return 1; + } + + // dri3 initialization + + if (gr_ctx.mode != PRESENT_MODE) + { + dri3_present_info.drm_fd = prepare_dri3_ext(); + if (dri3_present_info.drm_fd < 0) return 1; + dri3_present_info.bufmgr = tbm_bufmgr_init (dri3_present_info.drm_fd); + } + + // allocate neccessary memory for dri3 buffers + dri3_buffers = calloc (gr_ctx.params.num_of_bufs, sizeof (dri3_buffer_t)); + + // set up dri3 buffers + for (i = 0; i < gr_ctx.params.num_of_bufs; i++) + realloc_dri3_buff (i, wnd_pos.width, wnd_pos.height); + + printf ("%u buffer(s) will be used.\n", gr_ctx.params.num_of_bufs); + + return 0; +} + +// dri3 events loop +//======================================================================== +void +dri3_loop (void) +{ + xcb_generic_event_t* event = NULL; + xcb_ge_generic_event_t* generic_event = NULL; + int i; +#ifndef _DEBUG_ + struct timespec start; +#endif + + // ask to be notifed by present extension about current msc + xcb_present_notify_msc (display.dpy, display.wnd, 0, 0, 0, 0); + + while (1) + { + xcb_flush (display.dpy); + event = xcb_wait_for_event (display.dpy); + if (!event) + { + DEBUG_OUT ("event returned by xcb_wait_for_event is NULL.\n"); + break; + } + + switch (event->response_type) + { + case XCB_GE_GENERIC: + { + generic_event = (xcb_ge_generic_event_t*)event; + + // if event is from present Ext. + if (generic_event->extension == dri3_present_info.present_ext->major_opcode) + { + switch (generic_event->event_type) + { + case XCB_PRESENT_CONFIGURE_NOTIFY: + { + xcb_present_configure_notify_event_t* config_ev; + config_ev = (xcb_present_configure_notify_event_t*)event; + + DEBUG_OUT ("XCB_PRESENT_CONFIGURE_NOTIFY: (%hd, %hd) [%hux%hu]\n", + config_ev->x, config_ev->y, config_ev->width, config_ev->height); + + for (i = 0; i < gr_ctx.params.num_of_bufs; i++) + dri3_buffers[i].buff_must_be_realloc = 1; + + wnd_pos.x = config_ev->x; + wnd_pos.y = config_ev->y; + wnd_pos.width = config_ev->width; + wnd_pos.height = config_ev->height; + } + break; + + case XCB_PRESENT_COMPLETE_NOTIFY: + { + DEBUG_OUT ("XCB_PRESENT_COMPLETE_NOTIFY:\n"); + + xcb_present_complete_notify_event_t* complete_ev; + complete_ev = (xcb_present_complete_notify_event_t*)event; + + if (complete_ev->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) + { +#ifdef _DEBUG_ + DEBUG_OUT (" XCB_PRESENT_COMPLETE_KIND_PIXMAP, serial = %u, msc = %llu, ", + complete_ev->serial, complete_ev->msc); + switch (complete_ev->mode) + { + case XCB_PRESENT_COMPLETE_MODE_COPY: + printf ("COPY.\n"); + break; + case XCB_PRESENT_COMPLETE_MODE_FLIP: + printf ("FLIP.\n"); + break; + case XCB_PRESENT_COMPLETE_MODE_SKIP: + printf ("SKIP.\n"); + break; + + default: + printf (" What is it?\n"); + break; + } +#endif + } + else if (complete_ev->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) + { + DEBUG_OUT ("XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC\n"); + + // compute msc to use with present_pixmap request + target_msc = complete_ev->msc + gr_ctx.params.swap_interval; + } +#ifndef _DEBUG_ + fps_calc( &start ); + clock_gettime( CLOCK_REALTIME, &start ); +#endif + // if some delays are occured + if (target_msc <= complete_ev->msc) + target_msc = complete_ev->msc + gr_ctx.params.swap_interval; + + // complete_ev->msc - number of frame that is displayed now, + // or will be displayed soon, after vblank end. + // so we draw new frames in idle pixmaps and ask to present + // our frames to next mscs with some interval. + + // if we obtained XCB_PRESENT_IDLE_NOTIFY event without + // XCB_PRESENT_COMPLETE_NOTIFY, this means that present + // drop our frame and we can draw new frame, and ask to + // present it immediately, without waiting for XCB_PRESENT_COMPLETE_NOTIFY + // event, that will be delivered with some delay, but in this case, + // we don't know about real msc and new present may results new drop, + // so we will do present only when handling XCB_PRESENT_COMPLETE_NOTIFY event, + // despite of some performance decrease + for (i = 0; i < gr_ctx.params.num_of_bufs; i++) + { + // if buffer is busy (non-idle) + if (dri3_buffers[i].busy) + continue; + + draw (i); // draw our mesh + swap_buffers (target_msc, i); + + target_msc += gr_ctx.params.swap_interval; + } + } + break; + + case XCB_PRESENT_IDLE_NOTIFY: + { + xcb_present_idle_notify_event_t* idle_ev; + idle_ev = (xcb_present_idle_notify_event_t*)event; + + if (idle_ev->serial >= gr_ctx.params.num_of_bufs) + { + set_error ("invalid parameters from present extension.\n"); + pthread_cancel(thread_id); + } + + DEBUG_OUT ("----------------------------\n" + "XCB_PRESENT_IDLE_NOTIFY: pixmap: 0x%x (tied tbm bo: 0x%p), serial: %u, event: %u\n", + idle_ev->pixmap, dri3_buffers[idle_ev->serial].bo.bo, idle_ev->serial, idle_ev->event); + + // unblock pixmap/bo to next draw and swap operations + dri3_buffers[idle_ev->serial].busy = 0; + + // adjust pixmap/bo size to wnd's size before start render into it + if (dri3_buffers[idle_ev->serial].buff_must_be_realloc) + realloc_dri3_buff (idle_ev->serial, wnd_pos.width, wnd_pos.height); + } + break; + + default: + break; + } + } + } //case XCB_GE_GENERIC: + break; + + default: + DEBUG_OUT ("dri3_loop: default case.\n event->response_type = %d.\n", + event->response_type); + break; + } //switch (event->response_type) + } //while (1) +} diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.c b/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.c new file mode 100755 index 0000000..29df183 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.c @@ -0,0 +1,108 @@ +/************************************************************************** + + fps computation module's source + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#include <stdio.h> + +#include "fps.h" + +#define BILLION 1000000000 +#define BILLION_F 1000000000.0f + +#define FPS_AVERAGE 1 // fps average value (in seconds) + +// for fps calculation +//===================================================================================== +void +fps_calc (const struct timespec* start) +{ + struct timespec end; + double time_diff; + double fps; + + static double sum_fps; + static time_t prev_time; + static int cnt = 1; + + if (!start) return; + + // note: start variable contains start time of time-execution measured code section + + // get time end of time-execution measured code section + clock_gettime (CLOCK_REALTIME, &end); + + // get difference in nanoseconds + time_diff = (double)( ( end.tv_sec - start->tv_sec ) * BILLION + + ( end.tv_nsec - start->tv_nsec ) ); + + // get frames per second value + fps = BILLION_F / time_diff; + + // average fps calculation + if (end.tv_sec - prev_time >= FPS_AVERAGE) + { + printf ("\033[8D"); + printf (" "); + printf ("\033[8D"); + printf ("%8.1f", sum_fps/cnt); + fflush (stdout); + + cnt = 0; + sum_fps = 0; + + prev_time = end.tv_sec; + } + else + { + cnt++; + sum_fps += fps; + } +} + +// return time diff (between time in 'start' and time when this function is called) +// in nanoseconds +//===================================================================================== +double +get_time_diff (const struct timespec* start) +{ + struct timespec end; + double time_diff; + + if (!start) return 0; + + // note: start variable contains start time of time-execution measured code section + + // get time end of time-execution measured code section + clock_gettime (CLOCK_REALTIME, &end); + + // get difference in nanoseconds + time_diff = (double)( ( end.tv_sec - start->tv_sec ) * BILLION + + ( end.tv_nsec - start->tv_nsec ) ); + return time_diff; +} diff --git a/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.h b/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.h new file mode 100755 index 0000000..bef0100 --- /dev/null +++ b/tests/functional/hwc_test/aux_dri2_dri3_lib/fps.h @@ -0,0 +1,47 @@ +/************************************************************************** + + fps computation module's header + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +#ifndef _FPS_H +#define _FPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <time.h> + +void fps_calc (const struct timespec* start); +double get_time_diff (const struct timespec* start); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/functional/hwc_test/clock.c b/tests/functional/hwc_test/clock.c new file mode 100644 index 0000000..97b24e8 --- /dev/null +++ b/tests/functional/hwc_test/clock.c @@ -0,0 +1,316 @@ +/************************************************************************** + + watch + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Roman Marchenko <r.marchenko@samsung.com> + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implements analog watch for using in semiautomatic tests for +// keep safe ddx driver devoloping + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#include <math.h> + +#include "aux_apps.h" +#include "dri2_dri3.h" + + +#define DEGREE_PER_SECOND (6) + + +typedef struct _point +{ + int x, y; + int color; +} PointRec, *PointPtr; + +typedef struct _circle +{ + PointRec center; + int R; +} CircleRec, *CirclePtr; + +typedef struct _warch +{ + CircleRec crcl; + int time; +} WatchRec, *WatchPtr; + + +xcb_connection_t* dpy = NULL; +xcb_screen_t* screen = NULL; +xcb_rectangle_t wnd_pos = {100, 100, 200, 200}; + +// work behavior flags: (see aux_apps.c for details) + +// mode of work (dri2/dri3+present) +int mode = DRI3_PRESENT_MODE; // by default dri3 + present will be used +int stop; +WatchRec watch; +uint32_t present_bufs = 3; +uint32_t swap_interval = 1; +uint32_t root_parent = 1; + +// functions set for render watch: + +inline void +set_point(PointPtr p, uint32_t *map, uint32_t pitch, uint32_t size) +{ + int off = (pitch * p->y + p->x); + if (off < size && off >= 0) + map[off] = p->color; +} + +inline void +draw_point(PointPtr p, uint32_t *map, uint32_t pitch, uint32_t size) +{ + set_point(p, map, pitch, size); + + p->x += 1; + set_point(p, map, pitch, size); + p->x -= 1; + + p->y += 1; + set_point(p, map, pitch, size); + p->y -= 1; + + p->x -= 1; + set_point(p, map, pitch, size); + p->x += 1; + + p->y -= 1; + set_point(p, map, pitch, size); + p->y += 1; +} + +void +draw_circle(CirclePtr c, uint32_t *map, uint32_t pitch, uint32_t size) +{ + double x0 = c->center.x; + double y0 = c->center.y; + double R = c->R; + + PointRec p = { 0, 0, c->center.color }; + + double fi; + int a; + for (a = 0; a < 360; a += DEGREE_PER_SECOND) + { + fi = M_PI / 180 * a; + p.x = (int) (x0 + R * cos(fi)); + p.y = (int) (y0 + R * sin(fi)); + draw_point(&p, map, pitch, size); + } +} + +void +draw_watch(WatchPtr w, uint32_t *map, uint32_t pitch, uint32_t size) +{ + draw_circle(&w->crcl, map, pitch, size); + + //drawing hand of the clock, use the change of radius + PointRec p = { 0, 0, w->crcl.center.color }; + + double fi = M_PI / 180 * (w->time * DEGREE_PER_SECOND); + + int R; + for (R = 0; R < (w->crcl.R - 3); R += 10) + { + p.x = (int) (w->crcl.center.x + R * cos(fi)); + p.y = (int) (w->crcl.center.y + R * sin(fi)); + draw_point(&p, map, pitch, size); + } +} + +/* +// +//=================================================================== +void +rotate (void) +{ + #define REFRESHE_TIME (16.0) // in ms + + struct timespec current; + static struct timespec prev; + double time_diff; + + clock_gettime (CLOCK_REALTIME, ¤t); + + // get difference in nanoseconds + time_diff = (double)( ( current.tv_sec - prev.tv_sec ) * 1000000000 + + ( current.tv_nsec - prev.tv_nsec ) ); + + if (time_diff / 1000000.0 > REFRESHE_TIME) + { + prev = current; + + watch.time++; + } + + #undef REFRESHE_TIME +} +*/ + +// direct accessing memory clear callback +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_clear (const bo_t* bo_e) +{ + xcb_rectangle_t rect; + + if (!bo_e) + { + printf ("raw_clear: invalid parameters.\n"); + return; + } + + rect.x = 0; + rect.y = 0; + rect.width = bo_e->width; + rect.height = bo_e->height; + + // simple draw black rectangle with size as window has + raw_fill_rect (bo_e, 1, &rect, screen->black_pixel); +} + +// direct accessing memory draw callback (watch drawing) +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_draw (const bo_t* bo_e) +{ + tbm_bo_handle hndl; + int* temp_map; + int i; + + if (!bo_e) + { + printf ("raw_draw: invalid parameters.\n"); + return; + } + + watch.crcl.R = (bo_e->width > bo_e->height) ? bo_e->height/2 : bo_e->width/2; + watch.crcl.center.x = bo_e->width / 2; + watch.crcl.center.y = bo_e->height / 2; + + hndl = tbm_bo_map (bo_e->bo, TBM_DEVICE_CPU, TBM_OPTION_WRITE); + if (!hndl.ptr) + { + printf ("raw_draw_line: Error while tbm_bo_map.\n"); + exit (1); + } + + // clear buffer + // we set all transparency bits to make buffer content fully + // un-transparency, actually to see this content over other buffers + temp_map = (int*)hndl.ptr; + for (i = 0; i < (bo_e->stride * bo_e->height)/sizeof(int); i++) + *temp_map++ = 0xff000000; + + draw_watch (&watch, (uint32_t*)hndl.ptr, bo_e->stride/4, + tbm_bo_size (bo_e->bo)/4); + + if (!stop) + watch.time++;//rotate (); + + tbm_bo_unmap (bo_e->bo); +} + +// +//=================================================================== +int +main (int argc, char** argv) +{ + xcb_generic_event_t* event = NULL; + xcb_window_t main_wnd; + draw_funcs_t draw_func; + dri2_dri3_params_t params; + int res; + + cmd_parse (argc, argv); + + dpy = xcb_connect (NULL, NULL); + + // get first screen of display + screen = xcb_setup_roots_iterator (xcb_get_setup (dpy)).data; + + main_wnd = create_window (dpy, 0xff, root_parent, "clock"); + + watch.crcl.R = (wnd_pos.width > wnd_pos.height) ? wnd_pos.height/2 : wnd_pos.width/2; + watch.crcl.center.x = wnd_pos.width / 2; + watch.crcl.center.y = wnd_pos.height / 2; + watch.crcl.center.color = 0xff00ff00; // set all transparency bits + + draw_func.raw_clear = NULL; + draw_func.raw_draw = raw_draw; + draw_func.xcb_clear = NULL; + draw_func.xcb_draw = NULL; + + params.num_of_bufs = present_bufs; + params.swap_interval = swap_interval; + + // inside init_dri2_dri3 new thread, responsible for dri2/dri3/present events + // handling, will be created + res = init_dri2_dri3 (dpy, &draw_func, mode, main_wnd, ¶ms); + if (res) + { + printf ("init_dri2_dri3: %s.\n", get_last_error()); + exit (1); + } + + printf ("before xcb loop.\n"); + + while (1) + { + xcb_flush (dpy); + event = xcb_wait_for_event (dpy); + if (!event) break; + + switch (event->response_type) + { + case XCB_EXPOSE: + DEBUG_OUT ("XCB_MAP_WINDOW\n"); + break; + + // if we are notified by XCB_BUTTON_PRESS event, we start/stop + // to change our frames, simple draw the same thing + case XCB_BUTTON_PRESS: + stop ^= 1; + break; + + default: + break; + } + } + + return 0; +} diff --git a/tests/functional/hwc_test/hwc_sample/hwc-sample.c b/tests/functional/hwc_test/hwc_sample/hwc-sample.c new file mode 100755 index 0000000..4e8ce0e --- /dev/null +++ b/tests/functional/hwc_test/hwc_sample/hwc-sample.c @@ -0,0 +1,1377 @@ +/************************************************************************** + + hwc_sample + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: SooChan Lim <sc1.lim@samsung.com> + Contact: Olexandr Rozov <o.rozov@samsung.com> + Contact: Roman Marchenko <r.marchenko@samsung.com> + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +/* This file contains implementation of software/hardware composition manager. + This app uses hwc, composite and damage extensions to create image of screen + from window's contents by using copy_area/drm planes.*/ + +#include <stdio.h> +#include <stdlib.h> + +#include <dlog.h> + +#include <xcb/composite.h> +#include <xcb/damage.h> +#include <xcb/hwc.h> + +#include "list.h" + +#define _DEBUG_ +#ifdef _DEBUG_ + #define DEBUG_OUT(format, args...) { ALOG(LOG_INFO, "HWC_SAMPLE", format, ##args); } +#else + #define DEBUG_OUT(...) {;} +#endif + +typedef struct cmd_options_t +{ + int not_use_hw_layers; // whether we want to use hardware layers to make compositing or not +} cmd_options_t; + +// thus structure describes window in window's list +typedef struct window_t +{ + struct list_head list_member; + xcb_window_t wnd_id; // X11 client sees only this id + xcb_window_t parent_id; // parent, after reparenting ! + xcb_pixmap_t wnd_content; // refered to off-screen storage of redirected window + xcb_hwc_draw_info_t hwc_info; // info for use in hwc_set_drawable + uint8_t use_hw_layer; // whether this window must be compositing via hardware layer + uint8_t off_hw_layer; // user can specify use or not hw layer for this window + uint8_t is_hidden; // is window hidden, this means window doesn't participate + // in composition + xcb_damage_damage_t damage; +} window_t; + +typedef struct window_manager_t +{ + struct list_head wnds_list_head; // list of grabbed windows, arranged by stack order position + u_int32_t amount; // amout of windows in above list + const xcb_query_extension_reply_t* damage_ext; // for determine damage's events + u_int32_t max_amount_hw_layers; // maximum available hardware layers + u_int32_t cur_amount_hw_layers; // current amount of utilized hardware layers + xcb_gcontext_t gc_soft_composite; // used for software compositing + xcb_gcontext_t gc_fill_root_buf; // used to fill root buffer + + // to avoid flickering, we just composite all windows to this pixmap, then, after + // composition of all window completed, composite this pixmap to root window + xcb_pixmap_t root_buffer; + + // information about windows which will be composited via hardware layers + xcb_hwc_draw_info_t* hwc_info; + cmd_options_t cmd_opts; // cmd line options + + // window pushed out from hw layer stack, when some window was set on top of this stack, + // this variable used for composite pushed out window in software only mode in time + // when hw composition for this (some) window occur + struct window_t* pushed_from_hw_stack; + +} window_manager_t; + +xcb_connection_t* xcb_dpy; +xcb_screen_t* xcb_screen; + +window_manager_t wm; + +// update root window +//======================================================================== +static void +_update_root (void) +{ + // composite root_buffer to root window to show on screen + xcb_copy_area (xcb_dpy, wm.root_buffer, xcb_screen->root, wm.gc_soft_composite, 0, 0, + 0, 0, xcb_screen->width_in_pixels, xcb_screen->height_in_pixels); + DEBUG_OUT (" composite root_buffer to root wnd.\n"); +} + +// update root_buffer, that can be then copied to root window via _update_root() +//======================================================================== +static void +_update_root_buf (window_t* wnd) +{ + if (!wnd) return; + + // composite window to root_buffer + xcb_copy_area (xcb_dpy, wnd->wnd_content, wm.root_buffer, wm.gc_soft_composite, 0, 0, + wnd->hwc_info.dstX, wnd->hwc_info.dstY, wnd->hwc_info.dstWidth, + wnd->hwc_info.dstHeight); + DEBUG_OUT (" off-screen storage: 0x%06x of wnd: 0x%06x (child wnd: 0x%06x) has been" + " composited to root_buffer at %hd, %hd, [%hux%hu].\n", wnd->wnd_content, + wnd->parent_id, wnd->wnd_id, wnd->hwc_info.dstX, wnd->hwc_info.dstY, + wnd->hwc_info.dstWidth, wnd->hwc_info.dstHeight); +} + +// fill root buffer before composite on it +//======================================================================== +static void +_fill_root_buffer (void) +{ + xcb_rectangle_t rect; + + rect.x = 0; + rect.y = 0; + rect.width = xcb_screen->width_in_pixels; + rect.height = xcb_screen->height_in_pixels; + + xcb_poly_fill_rectangle (xcb_dpy, wm.root_buffer, wm.gc_fill_root_buf, 1, &rect); + + DEBUG_OUT ("root_buffer has been filled.\n"); +} + +// +//======================================================================== +static void +_hwc_set_print_info (void) +{ + int i; + + DEBUG_OUT (" hwc_set_drawable:\n"); + + for (i = 0; i < wm.cur_amount_hw_layers; i++) + DEBUG_OUT (" drawable: 0x%06x, composite_method: %d, from %hd, %hd [ %hux%hu], " + "to %hd, %hd [%hux%hu].\n", + wm.hwc_info[i].drawable, wm.hwc_info[i].composite_methods, + wm.hwc_info[i].srcX, wm.hwc_info[i].srcY, wm.hwc_info[i].srcWidth, wm.hwc_info[i].srcHeight, + wm.hwc_info[i].dstX, wm.hwc_info[i].dstY, wm.hwc_info[i].dstWidth, wm.hwc_info[i].dstHeight); +} + +// do compositing of one drawable +// drawable - window id user application knows about which (passed root wnd will be ignored) !!! +//======================================================================== +static void +composite_drawable (xcb_drawable_t drawable) +{ + window_t* wnd; + int update_root = 0; + + // iterate over grabbed windows list, from topmost window to lowermost window + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + // look for window which must be recomposited + if (wnd->wnd_id != drawable || wnd->wnd_id == xcb_screen->root) continue; + + // if we in software only composite mode + if (wm.cmd_opts.not_use_hw_layers) + { + _update_root_buf (wnd); + _update_root (); + + break; + } + else + { + // if wnd isn't on hw layer, copy window content to root window + if (!wnd->use_hw_layer) + { + update_root = 1; + _update_root_buf (wnd); + } + + // if window is on hw layer it can pushed out, from hw layer stack, window that + // was on hw layer previously (it can be occurred after MapRequest for some window), + // so we must do sw composition for this pushed out window + else if (wm.pushed_from_hw_stack) + { + update_root = 1; + _update_root_buf (wm.pushed_from_hw_stack); + wm.pushed_from_hw_stack = NULL; + } + + // composite root_buffer to root window to show on screen, if root buffer was changed + if (update_root) _update_root (); + + // we must reset drawables, the main reason to do this, for windows which are on hw + // layers, is that present extension in X server makes swap of buffers, not copy + // (see present/present.c in Xorg) and we cann't distinguish no-dri, dri2 and present windows + xcb_hwc_set_drawable (xcb_dpy, 0, wm.cur_amount_hw_layers, wm.hwc_info); + _hwc_set_print_info (); + + break; + } + } + + xcb_flush (xcb_dpy); +} + +// do compositing of all windows, in any cases root window will be updated +//======================================================================== +static void +composite_all (void) +{ + window_t* wnd; + + DEBUG_OUT ("compositing of all windows:\n"); + + // fill root buffer before composite on it + _fill_root_buffer (); + + // if user doesn't want to do hardware compositing + if (wm.cmd_opts.not_use_hw_layers) + { + // iterate over grabbed windows list, from lowermost window to topmost window + // NOTE: this implementation of list ala stack, so we must use _prev suffix + list_for_each_prev_entry (wnd, &wm.wnds_list_head, list_member) + { + if (wnd->wnd_id == xcb_screen->root) + continue; + + _update_root_buf (wnd); + } + + _update_root (); + } + else + { + list_for_each_prev_entry (wnd, &wm.wnds_list_head, list_member) + { + if (wnd->use_hw_layer || wnd->wnd_id == xcb_screen->root) continue; + + _update_root_buf (wnd); + } + + // composite root_buffer to root window to show on screen, + _update_root (); + + xcb_hwc_set_drawable (xcb_dpy, 0, wm.cur_amount_hw_layers, wm.hwc_info); + _hwc_set_print_info (); + } + + xcb_flush (xcb_dpy); +} + +// return geometry of specified, by win_id, window (via second parameter) +// return -1 if fail +//======================================================================== +static int +get_wnd_geometry (xcb_window_t win_id, xcb_get_geometry_reply_t* geo) +{ + xcb_get_geometry_cookie_t gg_c; + xcb_get_geometry_reply_t* gg_r = NULL; + xcb_generic_error_t* xcb_error = NULL; + + if (!geo) return -1; + + gg_c = xcb_get_geometry (xcb_dpy, win_id); + gg_r = xcb_get_geometry_reply (xcb_dpy, gg_c, &xcb_error); + if (xcb_error) + { + printf ("error while xcb_get_geometry call.\n"); + free (xcb_error); + return -1; + } + + memcpy (geo, gg_r, sizeof(xcb_get_geometry_reply_t)); + + free (gg_r); + + return 0; +} + +// turn on external window events tracking +// in this function we ask X server to send us DestroyNotify, UnmapNotify events +// for window specified via @wnd->wnd_id +//======================================================================== +static int +turn_on_track_ext_wnd_events (window_t* wnd) +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t* xcb_error; + + uint32_t value_mask; + uint32_t value_list[1]; + + if (!wnd) return -1; + + value_mask = XCB_CW_EVENT_MASK; + value_list[0] = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + + // ask X server to send us DestroyNotify, UnmapNotify and other notify events + // for @wnd->wnd_id window + cookie = xcb_change_window_attributes_checked (xcb_dpy, wnd->wnd_id, value_mask, value_list); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_change_window_attributes call.\n"); + free (xcb_error); + return -1; + } + + printf (" turn on events tracking for external window: 0x%06x.\n\n", wnd->wnd_id); + + return 0; +} + +// TODO: +//======================================================================== +static void +unreparent_wnd (xcb_window_t wnd_id, xcb_window_t parent_id) +{ + xcb_destroy_window (xcb_dpy, parent_id); +} + +// create new window @parent_id with parent like parent of @wnd_id, +// with same geometry, visual, border width and depth, and reparent +// @wnd_id to new parent @parent_id +//======================================================================== +static int +reparent_wnd (xcb_window_t wnd_id, xcb_window_t* parent_id) +{ + xcb_get_geometry_reply_t geo; + + xcb_get_window_attributes_cookie_t get_attr_c; + xcb_get_window_attributes_reply_t* get_attr_r; + xcb_generic_error_t* xcb_errors = NULL; + + xcb_query_tree_cookie_t query_tree_c; + xcb_query_tree_reply_t* query_tree_r; + + xcb_void_cookie_t cookie; + int res; + + if (!parent_id) return -1; + + get_attr_c = xcb_get_window_attributes (xcb_dpy, wnd_id); + get_attr_r = xcb_get_window_attributes_reply (xcb_dpy, get_attr_c, &xcb_errors); + if (xcb_errors) + { + printf ("error while xcb_get_window_attributes call.\n"); + free (xcb_errors); + return -1; + } + + // for determine parent of window, identifiable by wnd_id + query_tree_c = xcb_query_tree (xcb_dpy, wnd_id); + query_tree_r = xcb_query_tree_reply (xcb_dpy, query_tree_c, &xcb_errors); + if (xcb_errors) + { + printf ("error while query_tree call.\n"); + free (xcb_errors); + goto fail_1; + } + + res = get_wnd_geometry (wnd_id, &geo); + if (res < 0) + goto fail_2; + + *parent_id = xcb_generate_id (xcb_dpy); + + cookie = xcb_create_window_checked (xcb_dpy, geo.depth, *parent_id, query_tree_r->parent, + geo.x, geo.y, geo.width, geo.height, geo.border_width, + get_attr_r->_class, get_attr_r->visual, 0, NULL); + if ((xcb_errors = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_create_window call.\n"); + free (xcb_errors); + goto fail_2; + } + + cookie = xcb_reparent_window_checked (xcb_dpy, wnd_id, *parent_id, 0, 0); + if ((xcb_errors = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_reparent_window call.\n"); + xcb_destroy_window (xcb_dpy, *parent_id); + free (xcb_errors); + goto fail_2; + } + + xcb_map_window (xcb_dpy, wnd_id); + + free (query_tree_r); + free (get_attr_r); + + return 0; + + // we cann't reparent window +fail_2: + free (query_tree_r); +fail_1: + free (get_attr_r); + return -1; +} + +// this function determines for which windows hw layers will be used, for composition +// Note: only this function can change @wnd->use_hw_layer field !!! +//======================================================================== +static void +_reorder_hw_layers_stack (void) +{ + window_t* wnd; + int i = 0; + + // iterate over grabbed windows list, from topmost window to lowermost window + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + // if window isn't hidden, if user allowed to use hw layer for this window + if (wnd->wnd_id != xcb_screen->root && !wnd->is_hidden && !wnd->off_hw_layer && i < (wm.max_amount_hw_layers - 1)) + { + i++; + wnd->use_hw_layer = 1; + } + else + { + // in this branch, only one window can has use_hw_layer field set to 1 + // this is window pushed out from hw layers stack, so we must save it + // and, then, when we will do composition for window that push, we also + // will do composition for pushed out window + if (wnd->use_hw_layer) + wm.pushed_from_hw_stack = wnd; + + wnd->use_hw_layer = 0; // all other window will be composited via sw way, except root wnd + } + } + + // Note: if some window was mapped it can result to push out lowest window (in hw layers stack) + // from hw layers stack. And if window was unmapped (hidden) in can result to push in + // window (for which wasn't available hw layer previously) to hw layer stack. +} + +// this function fills wm.hwc_info array used for hw composition (hwc_set_drawable request) +// we must call this function when: +// we have grabbed window - new grabbed window must be set on topmost hw layer +// wnd hierarchy is changed - to show this changes +// wnd geometry has been changed - to show this changes +// wnd was unmapped (hidden) - to show this changes +// NOTE: only this function can change wm.hwc_info array !!! +//======================================================================== +static void +set_hw_layers_stack (void) +{ + window_t* wnd; + int i = 0; + + _reorder_hw_layers_stack (); + + // reset hw layers stack + memset (wm.hwc_info, 0, wm.max_amount_hw_layers * sizeof(xcb_hwc_draw_info_t)); + + // iterate over grabbed windows list, from topmost window to lowermost window + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + // root is lowermost window in list + if (wnd->wnd_id == xcb_screen->root) + memcpy (&wm.hwc_info[i++], &wnd->hwc_info, sizeof(wnd->hwc_info)); + else if (wnd->use_hw_layer && i < (wm.max_amount_hw_layers - 1)) + memcpy (&wm.hwc_info[i++], &wnd->hwc_info, sizeof(wnd->hwc_info)); + } + + // set current amount of utilized hw layers to use in hwc_set_drawable request + wm.cur_amount_hw_layers = i; +} + +// init/reinit info for set window on hw layer +// wnd - id of window which hwc_info must be changed +// return -1 if fail +//======================================================================== +static int +init_hwc_info (window_t* wnd) +{ + xcb_get_geometry_reply_t geo; + int res; + + if (!wnd) return -1; + + res = get_wnd_geometry (wnd->wnd_id, &geo); + if (res < 0) return -1; + + wnd->hwc_info.drawable = wnd->wnd_id; + + wnd->hwc_info.srcX = 0; + wnd->hwc_info.srcY = 0; + wnd->hwc_info.srcWidth = geo.width; + wnd->hwc_info.srcHeight = geo.height; + + wnd->hwc_info.dstX = geo.x; + wnd->hwc_info.dstY = geo.y; + wnd->hwc_info.dstWidth = geo.width; + wnd->hwc_info.dstHeight = geo.height; + + wnd->hwc_info.composite_methods = XCB_HWC_COMPOSITE_METHOD_DEFAULT; + + return 0; +} + +// ungrab window - forget about window +// undo operations were done when window was grabbed +//======================================================================== +static void +ungrab_wnd (xcb_window_t wnd_id) +{ + window_t* wnd; + int res = 0; + + // iterate over grabbed windows list + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + if (wnd->wnd_id == wnd_id) + { + res = 1; + break; + } + } + + // check if we are asked to ungrab ungrabbed window + if (!res) return; + + // if we here we must ungrab window + + printf ("ungrab window: 0x%06x\n", wnd_id); + + wm.amount--; + + // just undo what have done when window grabbing was + xcb_damage_destroy (xcb_dpy, wnd->damage); + xcb_free_pixmap (xcb_dpy, wnd->wnd_content); + xcb_composite_unredirect_window (xcb_dpy, wnd->parent_id, XCB_COMPOSITE_REDIRECT_MANUAL); + xcb_destroy_window (xcb_dpy, wnd->parent_id); + list_del (&wnd->list_member); + free (wnd); + + // we destroy (unlink) window, so we must reset hw layers stack to new condition + if (!wm.cmd_opts.not_use_hw_layers) + set_hw_layers_stack (); +} + +// grab window - 'grab' means allocate window_t structure, add it to list of grabbed windows, +// fill it fields, reparent window, redirect parent window, obtain pixmap refered to off-screen +// storage of parent window, ask notice about damage on window, turn on external window events +// tracking and prepare hw layer stack +// wnd_id - id of window to grab +//======================================================================== +static void +grab_wnd (xcb_window_t wnd_id) +{ + window_t* wnd; + xcb_generic_error_t* xcb_error; + xcb_void_cookie_t cookie; + int res; + + // iterate over grabbed windows list, maybe we have grabbed window already + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + if (wnd->wnd_id == wnd_id) return; + } + + printf ("grab window: 0x%06x\n", wnd_id); + + // if we here, window hasn't be grabbed yet, so grap it + wnd = (window_t*)calloc (1, sizeof(window_t)); + + // add window to begin of grabbed windows list + // so we will obtain stack-ala list + list_add (&wnd->list_member, &wm.wnds_list_head); + + wnd->wnd_id = wnd_id; + + // prepare information to use in hwc_set_drawable request + res = init_hwc_info (wnd); + if (res < 0) + goto fail_1; + + // we mustn't reparent, redirect, create damage, obtain off-screen pixmap + // and reoder hw layers stack for root window + if (wnd->wnd_id != xcb_screen->root) + { + cookie = xcb_grab_server_checked (xcb_dpy); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_grab_server call.\n"); + free(xcb_error); + goto fail_1; + } + + res = reparent_wnd (wnd->wnd_id, &wnd->parent_id); + if (res < 0) + goto fail_1; + + cookie = xcb_composite_redirect_window_checked (xcb_dpy, wnd->parent_id, XCB_COMPOSITE_REDIRECT_MANUAL); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_composite_redirect_window call.\n"); + free(xcb_error); + goto fail_2; + } + + printf (" window: 0x%06x (child wnd: 0x%06x) has been redirected to off-screen storage.\n", + wnd->parent_id, wnd->wnd_id); + + xcb_map_window (xcb_dpy, wnd->parent_id); + + // obtain pixmap refered to off-screen storage of redirected window + wnd->wnd_content = xcb_generate_id (xcb_dpy); + cookie = xcb_composite_name_window_pixmap_checked (xcb_dpy, wnd->parent_id, wnd->wnd_content); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_composite_name_window_pixmap call.\n"); + free(xcb_error); + goto fail_3; + } + + printf (" off-screen storage: 0x%06x for window: 0x%06x (child wnd: 0x%06x) has been obtained.\n", + wnd->wnd_content, wnd->parent_id, wnd->wnd_id); + + // we track damage notification for window user know about which, not for it parent ! + wnd->damage = xcb_generate_id (xcb_dpy); + cookie = xcb_damage_create_checked (xcb_dpy, wnd->damage, wnd->wnd_id, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_damage_create call.\n"); + free(xcb_error); + goto fail_4; + } + + printf (" damage object: 0x%06x for window: 0x%06x (child wnd: 0x%06x) has been created.\n", + wnd->damage, wnd->parent_id, wnd->wnd_id); + + cookie = xcb_ungrab_server_checked (xcb_dpy); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_ungrab_server call.\n"); + free(xcb_error); + goto fail_5; + } + + // we must do this due to inability to set XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT and + // XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY flags together (for root wnd), so we must track + // events for all windows, via turn on tracking events for each window itself, no for + // their parent, as we did for MAP_REQUEST event + res = turn_on_track_ext_wnd_events (wnd); + if (res < 0) goto fail_5; + } + + // we have grabbed new window, so we must reset hw layers stack to new condition + if (!wm.cmd_opts.not_use_hw_layers) + set_hw_layers_stack (); + + wm.amount++; + + return; + + // we cann't grab this window +fail_5: + xcb_damage_destroy (xcb_dpy, wnd->damage); +fail_4: + xcb_free_pixmap (xcb_dpy, wnd->wnd_content); +fail_3: + xcb_composite_unredirect_window (xcb_dpy, wnd->parent_id, XCB_COMPOSITE_REDIRECT_MANUAL); +fail_2: + unreparent_wnd (wnd->wnd_id, wnd->parent_id); +fail_1: + list_del (&wnd->list_member); + free (wnd); + + return; +} + +// +//======================================================================== +/*static void +show_wnd (xcb_window_t wnd_id) +{ + +}*/ + +// when X11 client issue UnmapRequest for @wnd_id window we must remove this window +// from composition process, it isn't ungrabbing, we only 'hide' window from +// compositor and accordingly from user +//======================================================================== +static void +hide_wnd (xcb_window_t wnd_id) +{ + window_t* wnd; + xcb_generic_error_t* xcb_error; + xcb_void_cookie_t cookie; + int res = 0; + + // iterate over grabbed windows list + list_for_each_entry (wnd, &wm.wnds_list_head, list_member) + { + if (wnd->wnd_id == wnd_id) + { + res = 1; + break; + } + } + + // check if we are asked to hide ungrabbed window + if (!res) return; + + // if we here we must hide window + + printf ("hide window: 0x%06x\n", wnd_id); + + // hide window to avoid it to be composition participant + wnd->is_hidden = 1; + + // destroy damage to avoid unneccessary damage events, for this hidden window, handling + cookie = xcb_damage_destroy_checked (xcb_dpy, wnd->damage); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_damage_destroy call.\n"); + free(xcb_error); + } + + // we hide window, so we must reset hw layers stack to new condition + if (!wm.cmd_opts.not_use_hw_layers) + set_hw_layers_stack (); +} + +// check does @wnd_id window exist +// return -1 if doesn't, 0 otherwise +//======================================================================== +static int +check_wnd_existing (xcb_window_t wnd_id) +{ + xcb_generic_error_t* xcb_error; + xcb_void_cookie_t cookie; + + cookie = xcb_unmap_window_checked (xcb_dpy, wnd_id); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + free(xcb_error); + return -1; + } + + return 0; +} + +// handle X11 protocol events for children of root window +//======================================================================== +static void +handle_x11_events (xcb_generic_event_t* xcb_event) +{ + xcb_generic_error_t* xcb_error; + xcb_void_cookie_t cookie; + int ret; + + if (!xcb_event) return; + + switch (xcb_event->response_type) + { + #ifdef _DEBUG_ + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t* client_msg; + client_msg = (xcb_client_message_event_t*)xcb_event; + + DEBUG_OUT ("client message for window: 0x%06x, type: %u, format: %hhu.\n", + client_msg->window, client_msg->type, client_msg->format); + } + break; + #endif + + // when another X11 client call MapRequest we are notified about this, and only we + // can issue real MapRequest request + case XCB_MAP_REQUEST: + { + xcb_map_request_event_t* map_request; + map_request = (xcb_map_request_event_t*)xcb_event; + + printf ("somebody try to map window: 0x%06x.\n", map_request->window); + + grab_wnd (map_request->window); + + // after new window was grabbed we must composite it, only it + composite_drawable (map_request->window); + } + break; + + // when X11 client unmaps window, via xcb_unmap_window or just close sX11 connection, + // for example, just kills app's process + case XCB_UNMAP_NOTIFY: + { + break; + xcb_unmap_notify_event_t* unmap_notify; + unmap_notify = (xcb_unmap_notify_event_t*)xcb_event; + + printf ("unmap notify: window: 0x%06x, event: 0x%06x.\n", unmap_notify->window, + unmap_notify->event); + + hide_wnd (unmap_notify->window); + + // check does window still exist (UnmapNotify can be sent just before DestroyNotify, + // so we must not call composite_all() for XCB_UNMAP_NOTIFY handler, we will call it + // for XCB_DESTROY_NOTIFY handler) + xcb_grab_server (xcb_dpy); + + ret = check_wnd_existing (unmap_notify->window); + if (ret < 0) + { + printf (" window: 0x%06x still doesn't exist :-).\n", unmap_notify->window); + xcb_ungrab_server (xcb_dpy); + break; + } + + // after window was hidden we must composite all, currently... + composite_all (); + + // TODO: it is very expensive way... + xcb_ungrab_server (xcb_dpy); + } + break; + + // when X11 client destroys window, via xcb_destroy_window or just close sX11 connection, + // for example, just kills app's process + case XCB_DESTROY_NOTIFY: + { + xcb_destroy_notify_event_t* destroy_notify; + destroy_notify = (xcb_destroy_notify_event_t*)xcb_event; + + printf ("destroy notify: window: 0x%06x, event: 0x%06x.\n", destroy_notify->window, + destroy_notify->event); + + ungrab_wnd (destroy_notify->window); + + // after window was ungrabbed we must composite all, currently... + composite_all (); + } + break; + + default: + { + // handle damage events + // when client app has finished draw operations we receive damage notification + if (xcb_event->response_type == wm.damage_ext->first_event + XCB_DAMAGE_NOTIFY) + { + xcb_damage_notify_event_t* damage_ev; + damage_ev = (xcb_damage_notify_event_t*)xcb_event; + + DEBUG_OUT ("damage notification for wnd: 0x%06x, area: %hd %hd [%hux%hu], geo: %hd %hd [%hux%hu].\n", damage_ev->drawable, + damage_ev->area.x, damage_ev->area.y, damage_ev->area.width, damage_ev->area.height, + damage_ev->geometry.x, damage_ev->geometry.y, damage_ev->geometry.width, damage_ev->geometry.height); + + cookie = xcb_damage_subtract_checked (xcb_dpy, damage_ev->damage, XCB_NONE, XCB_NONE); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_damage_subtract call.\n"); + free(xcb_error); + } + + // we do composite only for window damage event has received for which + composite_drawable (damage_ev->drawable); + } + } + } +} + +// find all windows, which are root window's childrens and print information about them +// return -1 if fail +//======================================================================== +static int +find_all_window_ids (xcb_window_t** wnds_list, u_int32_t* wnds_amount) +{ + xcb_query_tree_cookie_t q_c; + xcb_query_tree_reply_t* q_r; + xcb_generic_error_t* xcb_errors = NULL; + int i; + + if (!wnds_list || !wnds_amount) return -1; + + q_c = xcb_query_tree (xcb_dpy, xcb_screen->root); + q_r = xcb_query_tree_reply (xcb_dpy, q_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("error while query_tree call.\n"); + return -1; + } + + // don't free *wnds_list, it points on no dynamic allocated memory + *wnds_list = xcb_query_tree_children (q_r); + *wnds_amount = xcb_query_tree_children_length (q_r); + + // print info ----------------------------------- + + printf (" RootWindow: 0x%06x.\n", xcb_screen->root); + printf (" root_return: 0x%06x.\n", q_r->root); + printf (" parent_return: 0x%06x.\n", q_r->parent); + printf (" nchildren_return: %u.\n", *wnds_amount); + + free (q_r); + + printf ("\nnumber window's id window's name\n"); + printf (" 0 0x%06x root\n", xcb_screen->root); + + for (i = 0; i < *wnds_amount; i++) + { + xcb_get_property_cookie_t gp_c; + xcb_get_property_reply_t* gp_r; + char* window_name; + + gp_c = xcb_get_property (xcb_dpy, 0, (*wnds_list)[i], XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 0); + gp_r = xcb_get_property_reply (xcb_dpy, gp_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + window_name = NULL; + } + else + { + if(!xcb_get_property_value_length (gp_r)) + window_name = NULL; + else + window_name = xcb_get_property_value (gp_r); + + free (gp_r); + } + + printf (" %d ", i + 1); + printf (" 0x%06x ", (*wnds_list)[i]); + printf (" %s\n", window_name); + + if (window_name) + free (window_name); + + } + printf ("\n"); + + return 0; +} + +// fill ai structure by using information about external windows +// ask X server to give us messages, which will be generated to external +// windows (for interception them) +//================================================================== +static int +grab_external_wnds (void) +{ + xcb_window_t* wnds_list = NULL; + uint32_t wnds_amount = 0; + int i, res; + + xcb_window_t* wnds_list_cp = NULL; + + printf ("\ngrab root window:\n "); + + // add root window to list of grabbed windows + grab_wnd (xcb_screen->root); + + printf ("grab all external windows:\n"); + + res = find_all_window_ids (&wnds_list, &wnds_amount); + if (res < 0) return -1; + + // if no external windows are on this time we don't grab them :-) + if (!wnds_amount) return 0; + + // we must do copy, because wnds_list points to array + // allocated inside xcb, so it will be rewritten on next xcb_query_tree call + // we mustn't free memory wnds_list points into, because it is not dynamic + // allocated memory + wnds_list_cp = (xcb_window_t*)calloc (wnds_amount, sizeof(xcb_window_t)); + memcpy (wnds_list_cp, wnds_list, wnds_amount * sizeof(xcb_window_t)); + + for (i = 0; i < wnds_amount; i++) + grab_wnd (wnds_list_cp[i]); + + free (wnds_list_cp); + + return 0; +} + +// prepare HWC extension for use +// return -1 if fail, otherwise amount of available layers(planes) +//======================================================================== +static int +hwc_ext_prepare (xcb_connection_t* c, xcb_screen_t* screen) +{ + xcb_hwc_query_version_cookie_t q_c; + xcb_hwc_query_version_reply_t* q_r; + xcb_generic_error_t* xcb_errors = NULL; + + xcb_hwc_open_cookie_t o_c; + xcb_hwc_open_reply_t* o_r; + + xcb_void_cookie_t s_i_c; + uint32_t max_layer; + + if (!c || !screen) return -1; + + // TODO: define XCB_HWC_MAJOR_VERSION is set to 1, but current version of hwc extension, + // supported by X server is 2 + q_c = xcb_hwc_query_version (c, 2, XCB_HWC_MINOR_VERSION); + q_r = xcb_hwc_query_version_reply (c, q_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("error while HWC query_version call.\n"); + return -1; + } + else if (q_r->major_version != 2 || q_r->minor_version != XCB_HWC_MINOR_VERSION) + { + printf ("HWC extension versions mismatch (between server: %u.%u and client: %u.%u).\n", + q_r->major_version, q_r->minor_version, XCB_HWC_MAJOR_VERSION, XCB_HWC_MINOR_VERSION); + free (q_r); + return -1; + } + + printf ("HWC extension: %d.%d.\n", q_r->major_version, q_r->minor_version); + + free (q_r); + + o_c = xcb_hwc_open (c, 0); + o_r = xcb_hwc_open_reply (c, o_c, &xcb_errors); + if (xcb_errors) + { + free (xcb_errors); + printf ("error while HWC open call.\n"); + return -1; + } + + max_layer = o_r->maxlayer; + free (o_r); + + printf (" hwc_open: maximum amount of hardware layers available to use: %u.\n", max_layer); + + // TODO: no define for mask for this call (third parameter), + // and I'm not sure about second parameter + s_i_c = xcb_hwc_select_input_checked (c, screen->root, 1); + + if ((xcb_errors = xcb_request_check (c, s_i_c))) + { + printf ("error while xcb_hwc_select_input call.\n"); + free(xcb_errors); + return -1; + } + + return max_layer; +} + +// prepare DAMAGE extension for use +// return NULL if fail +//================================================================== +static const xcb_query_extension_reply_t* +damage_ext_prepare (xcb_connection_t* c) +{ + xcb_damage_query_version_cookie_t q_c; + xcb_damage_query_version_reply_t* q_r; + xcb_generic_error_t* xcb_errors = NULL; + const xcb_query_extension_reply_t* damage_ext; + + if (!c) return NULL; + + // for determine damage's events + damage_ext = xcb_get_extension_data (c, &xcb_damage_id); + if (!damage_ext) + { + printf ("error while xcb_get_extension_data call.\n"); + return NULL; + } + + q_c = xcb_damage_query_version (c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); + q_r = xcb_damage_query_version_reply (c, q_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("error while DAMAGE extension query_version call.\n"); + return NULL; + } + else if (q_r->major_version != XCB_DAMAGE_MAJOR_VERSION || q_r->minor_version != XCB_DAMAGE_MINOR_VERSION) + { + printf ("DAMAGE extension versions mismatch (between server: %u.%u and client: %u.%u).\n", + q_r->major_version, q_r->minor_version, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); + free (q_r); + return NULL; + } + + printf ("DAMAGE extension: %d.%d.\n", q_r->major_version, q_r->minor_version); + printf (" major_opcode: %hhu, first_event: %hhu, first_erorr: %hhu.\n", + damage_ext->major_opcode, damage_ext->first_event, damage_ext->first_error); + + free (q_r); + + return damage_ext; +} + +// prepare COMPOSITE extension for use +// return -1 if fail +//======================================================================== +static int +composite_ext_prepare (xcb_connection_t* c) +{ + xcb_composite_query_version_cookie_t q_c; + xcb_composite_query_version_reply_t* q_r; + xcb_generic_error_t* xcb_errors = NULL; + + if (!c) return -1; + + q_c = xcb_composite_query_version (c, XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MINOR_VERSION); + q_r = xcb_composite_query_version_reply (c, q_c, &xcb_errors); + + if (xcb_errors) + { + free (xcb_errors); + printf ("error while COMPOSITE query_version call.\n"); + return -1; + } + else if (q_r->major_version != XCB_COMPOSITE_MAJOR_VERSION || q_r->minor_version != XCB_COMPOSITE_MINOR_VERSION) + { + printf ("COMPOSITE extension versions mismatch (between server: %u.%u and client: %u.%u).\n", + q_r->major_version, q_r->minor_version, XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MINOR_VERSION); + free (q_r); + return -1; + } + + printf ("COMPOSITE extension: %d.%d.\n", q_r->major_version, q_r->minor_version); + + free (q_r); + + return 0; +} + +// create root_buffer to composite on it contents of windows (to avoid flickering) +// after all window contents will be on root_buffer composite it to root window to show +// also we ask X server to send us MapRequest, ConfigureRequest, CirculateRequest and +// ResizeRequest events for all root's children. +// (these request are sent when somebody try to map, reconfigure or resize root's children +// window, and we can decide what to do whith this request. +// for example: when somebody issues MapRequest via xcb_map_window, real map isn't occured, but +// MapRequest event is sent us and we can properly react on it.) +// also fill root_buffer pixmap and root window +// return -1 if fail +//======================================================================== +static int +prepare_root (void) +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t* xcb_error; + + uint32_t value_mask; + uint32_t value_list[1]; + + wm.root_buffer = xcb_generate_id (xcb_dpy); + cookie = xcb_create_pixmap_checked (xcb_dpy, xcb_screen->root_depth, wm.root_buffer, xcb_screen->root, + xcb_screen->width_in_pixels, xcb_screen->height_in_pixels); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_create_pixmap call.\n"); + free(xcb_error); + return -1; + } + + value_mask = XCB_CW_EVENT_MASK; + value_list[0] = XCB_EVENT_MASK_RESIZE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; + + // this call must be done after any windows create call !!! + // also we ask X server to send us MapRequest, ConfigureRequest, CirculateRequest and + // ResizeRequest events for all root's children. + cookie = xcb_change_window_attributes_checked (xcb_dpy, xcb_screen->root, value_mask, value_list); + if ((xcb_error = xcb_request_check (xcb_dpy, cookie))) + { + printf ("error while xcb_change_window_attributes call.\n"); + free(xcb_error); + return -1; + } + + return 0; +} + +// +//======================================================================== +static xcb_gcontext_t +create_gc (xcb_connection_t* c, xcb_screen_t* screen, uint32_t foreground) +{ + xcb_gcontext_t gc; + xcb_void_cookie_t cookie; + xcb_generic_error_t* error; + uint32_t value[2]; + + if (!c || !screen) + { + printf ("error while create_gc call.\n"); + exit (EXIT_FAILURE); + } + + value[0] = XCB_GX_COPY; + value[1] = foreground; + + gc = xcb_generate_id (c); + cookie = xcb_create_gc_checked (c, gc, screen->root, XCB_GC_FUNCTION | XCB_GC_FOREGROUND, value); + xcb_flush (c); + + if ((error = xcb_request_check (c, cookie))) + { + printf ("error while xcb_create_gc call.\n"); + free(error); + exit (EXIT_FAILURE); + } + + return gc; +} + +// +//================================================================== +static void +usage (char *argv) +{ + printf ("This application is simple sw/hw compositor manager. It can be used to test " + "ddx's side of hwc, dri2 and dri3/present extensions.\n" + "You can launch clients apps and then launch this compositor manager, or you " + "can launch firstly compositor manager and then\nclient apps.\n\n"); + + printf ("%s usage : \n", argv); + printf (" %s (options) (parameters)\n", argv); + printf (" (options) :\n"); + printf (" -no_hw : don't use hw layers to composition (only software composition)\n"); +} + +// parse command line +// returns -1 if failed +//================================================================== +static int +check_options (int argc, char *argv[]) +{ + int j = 0; + + if (!argv) return -1; + + // check if we want to know how use this app + for (j = 1; j < argc; j++) + { + if (!strcmp (argv[j], "--help")) + { + usage (argv[0]); + return -1; + } + } + + // check if we don't want to use hardware layers + for (j = 1; j < argc; j++) + { + if (!strcmp (argv[j], "--no_hw")) + { + wm.cmd_opts.not_use_hw_layers = 1; // we will do only software composition + break; + } + } + + return 0; +} + +// +//======================================================================== +int +main (int argc, char *argv[]) +{ + xcb_generic_event_t* xcb_event = NULL; + int res; + + // parse cmdline + res = check_options (argc, argv); + if (res < 0) goto fail; + + // open the connection to the X server + xcb_dpy = xcb_connect (NULL, NULL); + res = xcb_connection_has_error (xcb_dpy); + if (res > 0) + { + printf ("error while open X display.\n"); + goto fail; + } + + // get first screen of display + xcb_screen = xcb_setup_roots_iterator (xcb_get_setup (xcb_dpy)).data; + if (!xcb_screen) + { + printf ("error while obtain first screen of X display.\n"); + goto fail; + } + + wm.gc_soft_composite = create_gc (xcb_dpy, xcb_screen, 0x00); + wm.gc_fill_root_buf = create_gc (xcb_dpy, xcb_screen, 0x555555); + + res = prepare_root (); + if (res < 0) goto fail; + + // prepare extensions needed for this window manager + res = composite_ext_prepare (xcb_dpy); + if (res < 0) goto fail; + + wm.damage_ext = damage_ext_prepare (xcb_dpy); + if (!wm.damage_ext) goto fail; + + res = hwc_ext_prepare (xcb_dpy, xcb_screen); + if (res < 0) goto fail; + + wm.max_amount_hw_layers = res; + + // if we haven't available hardware layers, we will use only software compositing + if (wm.max_amount_hw_layers) + wm.hwc_info = calloc (wm.max_amount_hw_layers, sizeof(xcb_hwc_draw_info_t)); + + // init list of grabbed windows + list_init (&wm.wnds_list_head); + + // grab external windows + res = grab_external_wnds (); + if (res < 0) goto fail; + + // in this point we have list of grabbed windows, arranged by stack order position, + // so we can do compositing (software or hardware depend on not_use_hw_layers flag and amount of + // available hardware layers) + + composite_all (); + + // events handle loop + + while (1) + { + xcb_flush (xcb_dpy); + xcb_event = xcb_wait_for_event (xcb_dpy); + if (!xcb_event) + { + printf ("event returned by xcb_wait_for_event is NULL.\n"); + goto fail; + } + + DEBUG_OUT ("event: %hhu.\n", xcb_event->response_type); + + handle_x11_events (xcb_event); + free (xcb_event); + } + +fail: + printf ("abnormal exit.\n"); + + if (wm.gc_fill_root_buf) + xcb_free_gc (xcb_dpy, wm.gc_fill_root_buf); + + if (wm.gc_soft_composite) + xcb_free_gc (xcb_dpy, wm.gc_soft_composite); + + if (xcb_dpy) + xcb_disconnect (xcb_dpy); + + return 1; +} diff --git a/tests/functional/hwc_test/hwc_sample/hwc-sample.h b/tests/functional/hwc_test/hwc_sample/hwc-sample.h new file mode 100644 index 0000000..f41a302 --- /dev/null +++ b/tests/functional/hwc_test/hwc_sample/hwc-sample.h @@ -0,0 +1,69 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +//#include <X11/X.h> +#include <X11/Xlib.h> +//#include <X11/Xutil.h> +//#include <X11/Xatom.h> +//#include <X11/Xos.h> +//#include <X11/cursorfont.h> +//#include <X11/extensions/hwc.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <pthread.h> +#include <sys/types.h> + +#define AMOUNT_OF_PLANES 4 + +// hwc_set values: +// 1 - set pixmap(s) +// 2 - set window(s) (internal or external) +// 3 - loop +// 4 - move window +// 5 - resize window + +typedef struct _ThreadData +{ + //Pixmap pixmap; + int width; + int height; + + pthread_t thread; +} ThreadData; + +struct app_info +{ + int maxLayer; + int count; + int wnd_to_change; + //Drawable draws[AMOUNT_OF_PLANES]; + //XRectangle dst[AMOUNT_OF_PLANES]; + //XRectangle src[AMOUNT_OF_PLANES]; + int hwc_set; // picked work mode + int use_root; + int hwc_unset; + int hwc_loop; + int redirect; // whether X server does redirect to each created window or not + int not_use_hw_layers; // whether we want to use hardware layers to make compositing or not + int set_all_ext_wnds; // whether we want to use all existing external windows or not + int set_spec_ext_wnds; // only specified wnds + int num_of_ext_wnds; // amount of all/specified external windows to want to set + //Window* ext_wnd_ids; // array of specified external windows id's +}; + +extern struct app_info ai; +//extern Window root, change_size, move_btn, change_stack_order; +extern int wd1, ht1; +extern int dispsize_width, dispsize_height; + +extern void* thread1_hwcsample (void *data); +void change_focus (void); +void hwc_set (void); +void hwc_movew (void); +void hwc_resizew (void); + +int launch_clients (char* clients[], int num_of_clients); + +#endif diff --git a/tests/functional/hwc_test/hwc_sample/hwc-thread.c b/tests/functional/hwc_test/hwc_sample/hwc-thread.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/functional/hwc_test/hwc_sample/hwc-thread.c diff --git a/tests/functional/hwc_test/hwc_sample/list.h b/tests/functional/hwc_test/hwc_sample/list.h new file mode 100644 index 0000000..51a841d --- /dev/null +++ b/tests/functional/hwc_test/hwc_sample/list.h @@ -0,0 +1,24 @@ +#ifndef _LIST_H +#define _LIST_H + +#include <X11/X.h> +#include <xorg/list.h> + +// just because without xorg_ prefix macros look more pretty +// Note: this isn't bridge between linux kernel list and xorg list. +#define list_head xorg_list +#define list_entry xorg_list_entry +#define list_add xorg_list_add +#define list_append xorg_list_append +#define list_del xorg_list_del +#define list_for_each_entry xorg_list_for_each_entry + +// be carefully, this macros checked only in gdb +#define list_for_each_prev_entry(pos, head, member) \ + for (pos = __container_of((head)->prev, pos, member);\ + &pos->member != (head);\ + pos = __container_of(pos->member.prev, pos, member)) + +#define list_init xorg_list_init + +#endif diff --git a/tests/functional/hwc_test/launch.sh b/tests/functional/hwc_test/launch.sh new file mode 100755 index 0000000..9009e96 --- /dev/null +++ b/tests/functional/hwc_test/launch.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# restart X server without Enlightenment +echo -n "restart X server..." + +pkill X +startx --only 2> /dev/null +sleep 2 + +echo " ok." + +echo -n "close all windows..." + +# sometimes, after startx --only, we have few strange windows, +# we must close them to proper work of hwc-sample and square_bubbles +square_bubbles -cls > /dev/null + +echo " ok." + +echo "start apps:" + +snowflake -geo 200x200 100 100& +snowflake -geo 200x200 200 200& +snowflake -geo 200x200 500 500& + +echo " snowflake -geo 200x200 100 100" +echo " snowflake -geo 200x200 200 200" +echo " snowflake -geo 200x200 500 500" + +sleep 1 + +echo -n "start square_bubbles..." +square_bubbles -m -s > /dev/null & +echo " ok." + +echo " sleep 15 sec" +sleep 15 + +echo -n "start hwc_sample..." +hwc_sample -redir -setr > /dev/null & +echo " ok." + +echo " sleep 15 sec" +sleep 15 + +pkill square_bubbles +pkill hwc_sample +pkill snowflake diff --git a/tests/functional/hwc_test/snowflake.c b/tests/functional/hwc_test/snowflake.c new file mode 100644 index 0000000..ee1fee0 --- /dev/null +++ b/tests/functional/hwc_test/snowflake.c @@ -0,0 +1,327 @@ +/************************************************************************** + + rotated snowflake + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Roman Peresipkyn <r.peresipkyn@samsung.com> + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implements rotated snowflake for using in semiautomatic tests for +// keep safe ddx driver devoloping + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> +#include <math.h> +#include <time.h> + +#include "aux_apps.h" +#include "dri2_dri3.h" + + +#define DEPTH (24) + +#define ROTATE_SPEED (-180.0) // degrees per second +#define MARGIN (10) +#define SNOWFLAKE_POINTS (36) + +typedef struct point_t +{ + double x; + double y; +} point; + +typedef struct matrix_t +{ + double a1; + double a2; + double b1; + double b2; +} matrix; + +xcb_connection_t* dpy = NULL; +xcb_screen_t* screen = NULL; +xcb_rectangle_t wnd_pos = {100, 100, 150, 150}; + +// work behavior flags: (see aux_apps.c for details) + +// mode of work (dri2/dri3+present) +int mode = DRI3_PRESENT_MODE; // by default dri3 + present will be used +int stop; +double rotate_speed = ROTATE_SPEED; +uint32_t present_bufs = 3; +uint32_t swap_interval = 1; +uint32_t root_parent = 1; + +// matrix for rotation +matrix current_rot; + +// our mesh +const point snowflake[SNOWFLAKE_POINTS] = { + // up-down + {0.0, -4.0 * 0.25}, {0.0, 4.0 * 0.25}, + + // left-right + {-4.0 * 0.25, 0.0}, {4.0 * 0.25, 0.0}, + + // upleft-downright + {-3.0 * 0.25, 3.0 * 0.25}, {3.0 * 0.25, -3.0 * 0.25}, + + // upright-downleft + {3.0 * 0.25, 3.0 * 0.25}, {-3.0 * 0.25, -3.0 * 0.25}, + + // branch up-right + {2.0 * 0.25, 3.0 * 0.25}, {2.0 * 0.25, 2.0 * 0.25}, {3.0 * 0.25, 2.0 * 0.25}, + + // branch up + {-1.0 * 0.25, 4.0 * 0.25}, {0.0, 3.0 * 0.25}, {1.0 * 0.25, 4.0 * 0.25}, + + // branch up-left + {-2.0 * 0.25, 3.0 * 0.25}, {-2.0 * 0.25, 2.0 * 0.25}, {-3.0 * 0.25, 2.0 * 0.25}, + + // branch left + {-4.0 * 0.25, 1.0 * 0.25}, {-3.0 * 0.25, 0.0 * 0.25}, {-4.0 * 0.25, -1.0 * 0.25}, + + // branch down-left + {-3.0 * 0.25, -2.0 * 0.25}, {-2.0 * 0.25, -2.0 * 0.25}, {-2.0 * 0.25, -3.0 * 0.25}, + + // branch down + {-1.0 * 0.25, -4.0 * 0.25}, {0.0 * 0.25, -3.0 * 0.25}, {1.0 * 0.25, -4.0 * 0.25}, + + // branch down-right + {2.0 * 0.25, -3.0 * 0.25}, {2.0 * 0.25, -2.0 * 0.25}, {3.0 * 0.25, -2.0 * 0.25}, + + // branch right + {4.0 * 0.25, -1.0 * 0.25}, {3.0 * 0.25, 0.0 * 0.25}, {4.0 * 0.25, 1.0 * 0.25}, + + // + {-1.5 * 0.25, 0.0 * 0.25}, {0.0 * 0.25, 1.5 * 0.25}, {1.5 * 0.25, 0.0 * 0.25}, {0.0 * 0.25, -1.5 * 0.25} +}; + +// calculate rotation matrix 'mtrx' for rotate at angle 'alpha' +//=================================================================== +inline void +set_rotation_matrix (double alpha, matrix* mtrx) +{ + mtrx->a1 = cos (alpha); + mtrx->a2 = -sin (alpha); + mtrx->b1 = -mtrx->a2; + mtrx->b2 = mtrx->a1; +} + +// make fps-independent rotation +//=================================================================== +void +rotate (void) +{ + #define REFRESHE_TIME (16.0) // in ms + + static double angle; + struct timespec current; + static struct timespec prev; + double time_diff; + + clock_gettime (CLOCK_REALTIME, ¤t); + + // get difference in nanoseconds + time_diff = (double)( ( current.tv_sec - prev.tv_sec ) * 1000000000 + + ( current.tv_nsec - prev.tv_nsec ) ); + + if (time_diff / 1000000.0 > REFRESHE_TIME) + { + prev = current; + + angle += rotate_speed * 2.0 * M_PI / 360.0 * (REFRESHE_TIME/1000.0); + + set_rotation_matrix (angle, ¤t_rot); + } + + #undef REFRESHE_TIME +} + +// direct accessing memory clear callback +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_clear (const bo_t* bo_e) +{ + xcb_rectangle_t rect; + + if (!bo_e) + { + printf ("raw_clear: invalid parameters.\n"); + return; + } + + rect.x = 0; + rect.y = 0; + rect.width = bo_e->width; + rect.height = bo_e->height; + + // simple draw black rectangle with size as window has + raw_fill_rect (bo_e, 1, &rect, 0xff000000 | screen->black_pixel); +} + +// direct accessing memory draw callback (snowflake drawing) +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_draw (const bo_t* bo_e) +{ + int i; + point temp[SNOWFLAKE_POINTS]; + xcb_point_t xcb_points[SNOWFLAKE_POINTS + 1]; + + if (!bo_e) + { + printf ("raw_draw: invalid parameters.\n"); + return; + } + + for (i = 0; i < SNOWFLAKE_POINTS; i++) + { + // calculate point's coords (in OpenGL world) of mesh taking in account + // current rotate angle (current_rot matrix) + temp[i].x = snowflake[i].x * current_rot.a1 + snowflake[i].y * current_rot.a2; + temp[i].y = snowflake[i].x * current_rot.b1 + snowflake[i].y * current_rot.b2; + + // convert coordinates into X world + xcb_points[i].x = MARGIN + (1.0 / 2.0) * (temp[i].x + 1.0) * (bo_e->width - 2 * MARGIN); + xcb_points[i].y = MARGIN + (-1.0 / 2.0) * (temp[i].y - 1.0) * (bo_e->height - 2 * MARGIN); + } + + raw_draw_line (bo_e, 2, &xcb_points[0], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 2, &xcb_points[2], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 2, &xcb_points[4], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 2, &xcb_points[6], 0xff000000 | screen->white_pixel); + + raw_draw_line (bo_e, 3, &xcb_points[8], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[11], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[14], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[17], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[20], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[23], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[26], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[29], 0xff000000 | screen->white_pixel); + raw_draw_line (bo_e, 3, &xcb_points[29], 0xff000000 | screen->white_pixel); + + xcb_points[SNOWFLAKE_POINTS] = xcb_points[32]; // loop + raw_draw_line (bo_e, 5, &xcb_points[32], screen->white_pixel); + + if (stop) return; + + rotate (); +} + +// parse command line +//======================================================================== +void +cmd_parse_inner (int argc, char* const argv[]) +{ + int i; + + if (argc < 2) return; + + // if we want to set rotate speed. + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-rot_speed") && (argc - i > 1)) + { + sscanf (argv[i + 1], "%lf", &rotate_speed); + break; + } + } + + // call default cl parser to parse another arguments + cmd_parse (argc, argv); + } + +// +//=================================================================== +int +main (int argc, char** argv) +{ + xcb_generic_event_t* event = NULL; + xcb_window_t main_wnd; + draw_funcs_t draw_func; + dri2_dri3_params_t params; + int res; + + cmd_parse_inner (argc, argv); + + dpy = xcb_connect (NULL, NULL); + + // get first screen of display + screen = xcb_setup_roots_iterator (xcb_get_setup (dpy)).data; + + main_wnd = create_window (dpy, 0xff, root_parent, "rotated snowflake"); + + draw_func.raw_clear = raw_clear; + draw_func.raw_draw = raw_draw; + draw_func.xcb_clear = NULL; + draw_func.xcb_draw = NULL; + + params.num_of_bufs = present_bufs; + params.swap_interval = swap_interval; + + // inside init_dri2_dri3 new thread, responsible for dri2/dri3/present events + // handling, will be created + res = init_dri2_dri3 (dpy, &draw_func, mode, main_wnd, ¶ms); + if (res) + { + printf ("init_dri2_dri3: %s.\n", get_last_error()); + exit (1); + } + + printf ("before xcb loop.\n"); + + while (1) + { + xcb_flush (dpy); + event = xcb_wait_for_event (dpy); + if (!event) break; + + switch (event->response_type) + { + case XCB_EXPOSE: + DEBUG_OUT ("XCB_MAP_WINDOW\n"); + break; + + // if we are notified by XCB_BUTTON_PRESS event, we start/stop + // to change our frames, simple draw the same thing + case XCB_BUTTON_PRESS: + stop ^= 1; + break; + + default: + break; + } + } + + return 0; +} diff --git a/tests/functional/hwc_test/square_bubbles/square_bubbles.c b/tests/functional/hwc_test/square_bubbles/square_bubbles.c new file mode 100755 index 0000000..f454f0c --- /dev/null +++ b/tests/functional/hwc_test/square_bubbles/square_bubbles.c @@ -0,0 +1,647 @@ +/************************************************************************** + + square_bubbles + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// This file implements window's manipulation like side-bump bubbles +// through Xlib. This app can be used with hwc-sample to pretty view of HWC work. +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <math.h> +#include <fcntl.h> + +#include <X11/X.h> +#include <X11/Xlib.h> + +#define _DEBUG 1 +#define MAX_WNDS (3) // apart root window +#define SCREEN_WIDTH (720) +#define SCREEN_HEIGHT (1280) +#define ODD_INTERVAL (10) // interval of wnd's size odd changing (in seconds) +#define RESIZE_SPEED (0.8) // radians per second + +typedef enum Direct_t +{ + UP_RIGHT, + DOWN_RIGHT, + UP_LEFT, + DOWN_LEFT +} Direct; + +typedef struct bubbles_t +{ + XRectangle curr_geometry; + XRectangle first_geometry; + u_int32_t x_range; + u_int32_t y_range; + Window id; + Direct direct; + double angle; +} bubbles; + +// Xsever specific variables +Display* dpy; +Window root; +int depth; +int screen; + +// information about all external windows (with names) +Window* root_childrens; +unsigned int nchildrens; + +bubbles square_bubbles[MAX_WNDS]; +unsigned int real_cnt; + +// we can only close all windows by pass '-cls' to this app +int cls; // whether we need to close all windows or not + +int move; // whether we need to move all windows or not +int change_size; // whether we need to change size of all windows or not +int delay = 50; // delay between bubbles's position&size calculation in ms + +int debug = 1; // whether we want to print or not + +int all_wnds = 1; // whether we want to manipulate all windows or not +int odd_size; // whether we want to use odd sizes of windows + +void cmd_parse (int argc, char* argv[]); +int find_all_window_ids (void); +XRectangle get_wnd_geometry (Window win_id); +void shift_wnd (bubbles* s_b); + +// this function returns randomized 32 bit unsigned value +// in order to bypass prevent (static analizator) warning (don't use rand() function) +//======================================================================== +u_int32_t +hand_made_rand (void) +{ + int ret, fd; + u_int32_t rand_number; + + fd = open ("/dev/urandom", O_RDONLY); + if (fd < 0) + return 11; // why 11, why not? + + // simple read 4 random bytes from /dev/urandom + ret = read (fd, &rand_number, sizeof(u_int32_t)); + if (ret < 0 || ret != sizeof(rand_number)) + rand_number = 11; // why 11, why not? + + close (fd); + + return rand_number; +} + +//======================================================================== +int +main (int argc, char* argv[]) +{ + int i; + + cmd_parse (argc, argv); + + if (!change_size && !move) + { + printf ("nothing to do, specify what you want (i.e: square_bubbles -m -s).\n"); + return 1; + } + + // delay is passed in ms, but usleep requires delay in mcs + delay *= 1000; + + dpy = XOpenDisplay ( NULL); + + if (!dpy) + { + printf ("error while XOpenDisplay call.\n"); + return 1; + } + + root = DefaultRootWindow(dpy); + screen = DefaultScreen(dpy); + depth = DefaultDepth(dpy, screen); + + if (all_wnds) + { + // allocate memory for root_childrens[] array + if (find_all_window_ids ()) + { + XCloseDisplay (dpy); + return 0; + } + } + + // we can supply up to MAX_WNDS windows only + real_cnt = nchildrens <= MAX_WNDS ? nchildrens : MAX_WNDS; + + if (!real_cnt) + { + printf ("no windows to manipulate.\n"); + XCloseDisplay (dpy); + return 0; + } + + // prior initialization + for (i = 0; i < real_cnt; i++) + { + square_bubbles[i].id = root_childrens[i]; + square_bubbles[i].curr_geometry = get_wnd_geometry (root_childrens[i]); + square_bubbles[i].direct = hand_made_rand () % 4; + + memcpy (&square_bubbles[i].first_geometry, &square_bubbles[i].curr_geometry, + sizeof(square_bubbles[i].first_geometry)); + square_bubbles[i].x_range = square_bubbles[i].first_geometry.width / 4; + square_bubbles[i].y_range = square_bubbles[i].first_geometry.height / 4; + } + + while (1) + { + for (i = 0; i < real_cnt; i++) + { + shift_wnd (&square_bubbles[i]); + + XMoveResizeWindow (dpy, square_bubbles[i].id, square_bubbles[i].curr_geometry.x, + square_bubbles[i].curr_geometry.y, square_bubbles[i].curr_geometry.width, + square_bubbles[i].curr_geometry.height); + +#ifdef _DEBUG + printf (" move and resize wnd (id: 0x%06lx) to %d,%d [%dx%d].\n", square_bubbles[i].id, + square_bubbles[i].curr_geometry.x, square_bubbles[i].curr_geometry.y, + square_bubbles[i].curr_geometry.width, square_bubbles[i].curr_geometry.height); +#endif + } + XFlush (dpy); + usleep (delay); + } + + if (all_wnds) + XFree ((void*) root_childrens); + else + free ((void*) root_childrens); + + XCloseDisplay (dpy); + + return 0; +} + +// parse command line +//======================================================================== +void +cmd_parse (int argc, char* argv[]) +{ + int i; + + if (argc < 2) return; + + // if we want to obtain help + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-help")) + { + printf ("usage:\n"); + printf (" -help : print this help.\n"); + printf (" -cls : close all root's children windows.\n"); + printf (" -m : move bubbles.\n"); + printf (" -s : change bubbles's size.\n"); + printf (" -delay : delay between bubbles's position&size calculation in ms.\n"); + printf (" -odd : use window's odd sizes.\n"); + printf ("\n"); + break; + } + } + + // if we want to close all windows + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-cls")) + { + cls = 1; + return; + } + } + + // if we want to move windows + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-m")) + { + move = 1; + break; + } + } + + // if we want to change windows's size + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-s")) + { + change_size = 1; + break; + } + } + + // if we want to change delay in ms + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-delay")) + { + if (i + 1 >= argc) + printf ("error while argument parsing. i.e: square_bubbles -m -delay 30\n"); + else + sscanf (argv[i + 1], "%d", &delay); + break; + } + } + + // check if we want to set specified windows + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-set")) + { + // if we have 'square_bubbles -set' + if (i + 1 >= argc) + { + all_wnds = 1; // set all windows + printf ("all named windows will be used.\n"); + } + + // if we have 'square_bubbles -set wnd_ids' + // we simple store all specified numbers (in hex), after '-set' key, as + // a window's ids in allocated array (root_childrens[]) + else + { + all_wnds = 0; // set specified windows + nchildrens = argc - i - 1; + + root_childrens = (Window*) calloc (nchildrens, sizeof(Window)); + + while (++i < argc) + sscanf (argv[i], "%lx", root_childrens++); + + root_childrens -= nchildrens; + + printf ("these named windows will be used:\n"); + + for (i = 0; i < nchildrens; i++) + printf (" wnd's id: 0x%06lx.\n", root_childrens[i]); + } + + break; + } + } + + // if we want to use even/odd size switch + for (i = 1; i < argc; i++) + { + if (!strcmp (argv[i], "-odd")) + { + odd_size = 1; + break; + } + } +} + +// find all windows, which are root window's childrens and have names, and +// print information about them +//======================================================================== +int +find_all_window_ids (void) +{ + int i; + Window root_return; + Window parent_return; + char* window_name = NULL; + + int j; + Window* tmp = NULL; + + // find all windows, which are childrens of root window and store their + // id's in 'tmp[] array' + if (!XQueryTree (dpy, RootWindow(dpy, screen), &root_return, &parent_return, &tmp, &nchildrens)) + { + printf ("error while first XQueryTree call.\n"); + return 1; + } + + if (cls) + { + // destroy all windows + for (i = 0; i < nchildrens; i++) + XDestroyWindow (dpy, tmp[i]); + + XFree ((void*) tmp); + return 1; + } + + // calculate amount of named windows + for (i = 0, j = 0; i < nchildrens; i++) + { + if (XFetchName (dpy, tmp[i], &window_name)) + { + j++; + XFree ((void*) window_name); + } + } + + root_childrens = calloc (j, sizeof(Window)); + + // create array root_childrens[] which consists of wnd's id to named windows only + for (i = 0, j = 0; i < nchildrens; i++) + { + if (XFetchName (dpy, tmp[i], &window_name)) + { + root_childrens[j++] = tmp[i]; + XFree ((void*) window_name); + } + } + + nchildrens = j; + + printf (" RootWindow: 0x%06lx.\n", RootWindow(dpy, screen)); + printf (" root_return: 0x%06lx.\n", root_return); + printf (" parent_return: 0x%06lx.\n", parent_return); + printf (" nchildren_return: %d.\n", nchildrens); + + printf ("\nnumber window's id window's name\n"); + + for (i = 0; i < nchildrens && i < MAX_WNDS; i++) + { + printf (" %d ", i); + printf (" 0x%06lx ", root_childrens[i]); + + XFetchName (dpy, root_childrens[i], &window_name); + printf (" %s\n", window_name); + XFree ((void*) window_name); + } + printf ("\n\n"); + + XFlush (dpy); + + return 0; +} + +// return geometry of specified, by win_id, window +//======================================================================== +XRectangle +get_wnd_geometry (Window win_id) +{ + XRectangle geometry; + XWindowAttributes win_attr; + + XGetWindowAttributes (dpy, win_id, &win_attr); + + geometry.x = win_attr.x; + geometry.y = win_attr.y; + geometry.width = win_attr.width; + geometry.height = win_attr.height; + + return geometry; +} + +// calculate new coordinates +//======================================================================== +void +shift_wnd (bubbles* s_b) +{ + short x_step = 5; + short y_step = 5; + int width; + int height; + + // each ODD_INTERVAL seconds we change wnd's size odd mode + int up_edge = ODD_INTERVAL * 1E6 / delay; + static int cnt_odd; + static int even = 1; + + if (!s_b) return; + + if (change_size) + { + width = s_b->x_range * sin (s_b->angle) + s_b->first_geometry.width; + height = s_b->y_range * sin (s_b->angle) + s_b->first_geometry.height; + + // if we want to use odd size of windows (first condition) + if ((odd_size) && (cnt_odd++ >= up_edge)) + { + cnt_odd = 0; + even ^= 0x01; + printf ("even/odd wnd's size changed.\n"); + } + + // make even w&h + if (even) + { + width &= ~0x01; + height &= ~0x01; + } + // make odd w&h (w&h values is checked in followed code) + else + { + width |= 0x01; + height |= 0x01; + } + + if ((s_b->curr_geometry.x + width <= (SCREEN_WIDTH - 1)) + && (s_b->curr_geometry.y + s_b->curr_geometry.height <= (SCREEN_HEIGHT - 1))) + { + s_b->curr_geometry.width = width; + s_b->curr_geometry.height = height; + s_b->angle += RESIZE_SPEED * delay * 1E-6; + } + } + + if (!move) return; + + switch (s_b->direct) + { + case UP_RIGHT: + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y -= y_step; + + // if bubble has striked in up right corner, + // it will be pushed to up_left direct + + // right side bump + if (s_b->curr_geometry.x + s_b->curr_geometry.width > (SCREEN_WIDTH - 1)) + { + if (s_b->curr_geometry.y <= 0) + { + printf ("UP_RIGHT: right side bump\n"); + s_b->curr_geometry.y = y_step; + } + + s_b->direct = UP_LEFT; + + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y -= y_step; + } + + // top side bump + else if (s_b->curr_geometry.y < 0) + { + if (s_b->curr_geometry.x + s_b->curr_geometry.width >= (SCREEN_WIDTH - 1)) + { + printf ("UP_RIGHT: top side bump\n"); + s_b->curr_geometry.x -= x_step; + } + + s_b->direct = DOWN_RIGHT; + + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y += y_step; + } + break; + + case DOWN_RIGHT: + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y += y_step; + + // if bubble has striked in bottom right corner, + // it will be pushed to down_left direct + + // right side bump + if (s_b->curr_geometry.x + s_b->curr_geometry.width > (SCREEN_WIDTH - 1)) + { + if (s_b->curr_geometry.y + s_b->curr_geometry.height >= (SCREEN_HEIGHT - 1)) + { + printf ("DOWN_RIGHT: right side bump\n"); + s_b->curr_geometry.y = (SCREEN_HEIGHT - 1) - s_b->curr_geometry.height - y_step; + } + + s_b->direct = DOWN_LEFT; + + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y += y_step; + } + + // bottom side bump + else if (s_b->curr_geometry.y + s_b->curr_geometry.height > (SCREEN_HEIGHT - 1)) + { + if (s_b->curr_geometry.x + s_b->curr_geometry.width >= (SCREEN_WIDTH - 1)) + { + printf ("DOWN_RIGHT: bottom side bump\n"); + s_b->curr_geometry.x -= x_step; + } + + s_b->direct = UP_RIGHT; + + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y -= y_step; + } + break; + + case UP_LEFT: + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y -= y_step; + + // if bubble has striked in top left corner, + // it will be pushed to up_right direct + + // left side bump + if (s_b->curr_geometry.x < 0) + { + if (s_b->curr_geometry.y <= 0) + { + printf ("UP_LEFT: left side bump\n"); + s_b->curr_geometry.y = y_step; + } + + s_b->direct = UP_RIGHT; + + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y -= y_step; + } + + // top side bump + else if (s_b->curr_geometry.y < 0) + { + if (s_b->curr_geometry.x <= 0) + { + printf ("UP_LEFT: top side bump\n"); + s_b->curr_geometry.x = x_step; + } + + s_b->direct = DOWN_LEFT; + + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y += y_step; + } + break; + + case DOWN_LEFT: + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y += y_step; + + // if bubble has striked in bottom left corner, + // it will be pushed to down_right direct + + // left side bump + if (s_b->curr_geometry.x < 0) + { + if (s_b->curr_geometry.y + s_b->curr_geometry.height >= (SCREEN_HEIGHT - 1)) + { + printf ("DOWN_LEFT: left side bump\n"); + s_b->curr_geometry.y = (SCREEN_HEIGHT - 1) - s_b->curr_geometry.height - y_step; + } + + s_b->direct = DOWN_RIGHT; + + s_b->curr_geometry.x += x_step; + s_b->curr_geometry.y += y_step; + } + + // bottom side bump + else if (s_b->curr_geometry.y + s_b->curr_geometry.height > (SCREEN_HEIGHT - 1)) + { + if (s_b->curr_geometry.x <= 0) + { + printf ("DOWN_LEFT: bottom side bump\n"); + s_b->curr_geometry.x = x_step; + } + + s_b->direct = UP_LEFT; + + s_b->curr_geometry.x -= x_step; + s_b->curr_geometry.y -= y_step; + } + break; + + default: + printf (" no correct direction.\n"); + break; + } + + if (s_b->curr_geometry.x < 0 || s_b->curr_geometry.y < 0) + { + printf ("error with calculation algorithm.\n"); + exit (1); + } +} diff --git a/tests/functional/hwc_test/wander_stripe.c b/tests/functional/hwc_test/wander_stripe.c new file mode 100644 index 0000000..9195660 --- /dev/null +++ b/tests/functional/hwc_test/wander_stripe.c @@ -0,0 +1,259 @@ +/************************************************************************** + + wander_strip + + Copyright 2010 - 2015 Samsung Electronics co., Ltd. All Rights Reserved. + + Contact: Keith Packard <keithp@keithp.com> + Contact: Sergey Sizonov <s.sizonov@samsung.com> + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sub license, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + **************************************************************************/ + +// this file implements wander_stripe for using in semiautomatic tests for +// keep safe ddx driver devoloping + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <unistd.h> + +#include "aux_apps.h" +#include "dri2_dri3.h" + + +xcb_connection_t* dpy = NULL; +xcb_screen_t* screen = NULL; +xcb_rectangle_t wnd_pos = {100, 100, 200, 200}; +xcb_rectangle_t wander_strip; +xcb_gc_t gc; + +// work behavior flags: (see aux_apps.c for details) + +// mode of work (dri2/dri3+present) +int mode = DRI3_PRESENT_MODE; // by default dri3 + present will be used +int stop; +int x_step = 1; +uint32_t present_bufs = 3; +uint32_t swap_interval = 1; +uint32_t root_parent = 1; + + +// direct accessing memory clear callback +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_clear (const bo_t* bo_e) +{ + xcb_rectangle_t rect; + + if (!bo_e) + { + printf ("raw_clear: invalid parameters.\n"); + return; + } + + rect.x = 0; + rect.y = 0; + rect.width = bo_e->width; + rect.height = bo_e->height; + + // draw white rectangle with size as window has + raw_fill_rect (bo_e, 1, &rect, 0xff000000 | screen->white_pixel); +} + +// direct accessing memory draw callback (wander stripe drawing) +// bo_e - contains all information to do direct render in memory +//=================================================================== +void +raw_draw (const bo_t* bo_e) +{ + static int x; + + if (!bo_e) + { + printf ("raw_draw: invalid parameters.\n"); + return; + } + + wander_strip.height = bo_e->height; + wander_strip.width = bo_e->width / 4; + + // move stripe + if (x_step > 0) + { + if (x + wander_strip.width > bo_e->width) + { + x_step = -x_step; + x += x_step; + } + } + else + { + if (x < 0) + { + x_step = -x_step; + x += x_step; + } + } + + wander_strip.x = x; + + // draw white wrandered rectangle + raw_fill_rect (bo_e, 1, &wander_strip, 0xff0000ff); + + if (!stop) + x += x_step; +} + +// xcb based draw callback (wander stripe drawing) +// drawable - drawable to render into it +// draw_w, draw_h - size of drawable +//=================================================================== +void +xcb_draw (xcb_drawable_t drawable, uint32_t draw_w, uint32_t draw_h) +{ + static int x; + uint32_t gc_mask; + uint32_t gc_values; + xcb_rectangle_t temp_rect; + + // draw white rectangle with size as window has + gc_mask = XCB_GC_FOREGROUND; + gc_values = 0xffffffff; + + xcb_change_gc (dpy, gc, gc_mask, &gc_values); + temp_rect.x = 0; + temp_rect.y = 0; + temp_rect.width = draw_w; + temp_rect.height = draw_h; + xcb_poly_fill_rectangle(dpy, drawable, gc, 1, &temp_rect); + + // draw black wander strip + gc_mask = XCB_GC_FOREGROUND; + gc_values = 0x0; + xcb_change_gc (dpy, gc, gc_mask, &gc_values); + + wander_strip.height = draw_h; + + // move stripe + if (x_step > 0) + { + if (x + wander_strip.width > draw_w) + { + x_step = -x_step; + x += x_step; + } + } + else + { + if (x < 0) + { + x_step = -x_step; + x += x_step; + } + } + + wander_strip.x = x; + + // draw white wrandered rectangle + xcb_poly_fill_rectangle (dpy, drawable, gc, 1, &wander_strip); + + if (!stop) + x += x_step; + + xcb_flush (dpy); +} + +// +//=================================================================== +int +main (int argc, char** argv) +{ + xcb_generic_event_t* event = NULL; + xcb_window_t main_wnd; + draw_funcs_t draw_func; + dri2_dri3_params_t params; + int res; + + cmd_parse (argc, argv); + + dpy = xcb_connect (NULL, NULL); + + // get first screen of display + screen = xcb_setup_roots_iterator (xcb_get_setup (dpy)).data; + + main_wnd = create_window (dpy, 0xff, root_parent, "wander stripe"); + + gc = xcb_generate_id (dpy); + xcb_create_gc (dpy, gc, main_wnd, 0, NULL); + + draw_func.raw_clear = raw_clear; + draw_func.raw_draw = raw_draw; + draw_func.xcb_draw = xcb_draw; + draw_func.xcb_clear = NULL; + + // init for drawing + wander_strip.y = 0; + wander_strip.width = wnd_pos.width / 4; + wander_strip.height = wnd_pos.height; + + params.num_of_bufs = present_bufs; + params.swap_interval = swap_interval; + + // inside init_dri2_dri3 new thread, responsible for dri2/dri3/present events + // handling, will be created + res = init_dri2_dri3 (dpy, &draw_func, mode, main_wnd, ¶ms); + if (res) + { + printf ("init_dri2_dri3: %s.\n", get_last_error()); + exit (1); + } + + printf ("before xcb loop.\n"); + + while (1) + { + xcb_flush (dpy); + event = xcb_wait_for_event (dpy); + if (!event) break; + + switch (event->response_type) + { + case XCB_EXPOSE: + DEBUG_OUT ("XCB_MAP_WINDOW\n"); + break; + + // if we are notified by XCB_BUTTON_PRESS event, we start/stop + // to change our frames, simple draw the same thing + case XCB_BUTTON_PRESS: + stop ^= 1; + break; + + default: + break; + } + } + + return 0; +} diff --git a/tests/functional/pixmap_copy_test/Makefile.am b/tests/functional/pixmap_copy_test/Makefile.am new file mode 100644 index 0000000..13c461f --- /dev/null +++ b/tests/functional/pixmap_copy_test/Makefile.am @@ -0,0 +1,9 @@ +if HAVE_FT + bin_PROGRAMS = pixmap_copy + +pixmap_copy_CFLAGS = $(PIXMAP_COPY_TEST_CFLAGS) +pixmap_copy_LDADD = $(PIXMAP_COPY_TEST_LIBS) +pixmap_copy_SOURCES = \ +@top_srcdir@/tests/functional/pixmap_copy_test/pixmap_copy.c + +endif diff --git a/tests/functional/pixmap_copy_test/pixmap_copy.c b/tests/functional/pixmap_copy_test/pixmap_copy.c new file mode 100644 index 0000000..b6bbbcc --- /dev/null +++ b/tests/functional/pixmap_copy_test/pixmap_copy.c @@ -0,0 +1,122 @@ +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> + +#define ERROR(str, arg...) \ + do \ + { \ + printf(str, ##arg); \ + exit(1); \ + } \ + while(0) \ + +#define WIDTH 720 +#define HEIGHT 1280 + +extern int errno; + +int main(int argc, char **argv) +{ + Display *display; + Window window; + Visual *visual; + Pixmap pixmap, pixmap2; + GC gr_context; + XGCValues gr_values; + XSetWindowAttributes attributes; + int depth = 0; + int screen = 0; + int num = 0; + time_t start, current; + +/* Connect to X Server */ + if ((display = XOpenDisplay(NULL)) == NULL) + ERROR("Can't connect X server: %s\n", strerror(errno)); + +/* Get default screen, visual etc */ + screen = XDefaultScreen(display); + visual = XDefaultVisual(display,screen); + depth = XDefaultDepth(display,screen); + attributes.background_pixel = XWhitePixel(display,screen); + + printf("depth %d\n", depth); + +/* Create window */ + window = XCreateWindow(display, XRootWindow(display,screen), + 0, 0, WIDTH, HEIGHT, 0, depth, InputOutput, + visual , CWBackPixel, &attributes); + +/* Chose events we are intrested in */ + XSelectInput(display, window, ExposureMask | KeyPressMask); + XFlush(display); + +/* Create pxmap */ + pixmap = XCreatePixmap(display, window, WIDTH, HEIGHT, depth); + +/* Create graphic contexts */ + gr_values.foreground = 0xAABBCC; + gr_context = XCreateGC(display, pixmap, GCForeground, &gr_values); + + XFillRectangle(display, pixmap, gr_context, 0, 0, WIDTH, HEIGHT); + XCopyArea(display, pixmap, window, gr_context, 0, 0, WIDTH, HEIGHT, 0, 0); + +/* Display the window on screen*/ + XMapWindow(display, window); + XFlush(display); + + +/*--------------- pixmap -> window -----------------*/ + + printf("-------------------------\n"); + printf("wait 2\n"); + sleep(2); + printf("start\n\n"); + + num = 0; + start = time(NULL); + while (time(¤t) < start + 5) + { + XCopyArea(display, pixmap, window, gr_context, 0, 0, WIDTH, HEIGHT, 0, 0); + ++num; + XFlush(display); + } + + printf("pixmap -> window: %d\n", num); + printf("-------------------------\n\n"); + + pixmap2 = XCreatePixmap(display, window, WIDTH, HEIGHT, depth); + XFillRectangle(display, pixmap2, gr_context, 0, 0, WIDTH, HEIGHT); + XFlush(display); + + +/*--------------- pixmap -> pixmap -----------------*/ + + printf("-------------------------\n"); + printf("wait 2\n"); + sleep(2); + printf("start\n\n"); + + num = 0; + start = time(NULL); + while (time(¤t) < start + 5) + { + XCopyArea(display, pixmap, pixmap2, gr_context, 0, 0, WIDTH, HEIGHT, 0, 0); + ++num; + XFlush(display); + } + + printf("pixmap -> pixmap: %d\n", num); + printf("-------------------------\n"); + + +/* Close connection with X */ + XCloseDisplay(display); + + return 0; +} + diff --git a/tests/functional/xv_test/Makefile.am b/tests/functional/xv_test/Makefile.am new file mode 100644 index 0000000..67f3fd6 --- /dev/null +++ b/tests/functional/xv_test/Makefile.am @@ -0,0 +1,12 @@ +if HAVE_FT +bin_PROGRAMS = test_xv +test_xv_CFLAGS = @XV_TEST_CFLAGS@ \ +-I@top_srcdir@/tests/functional/xv_test \ +-I@top_srcdir@/src/xv +test_xv_LDADD = @XV_TEST_LIBS@ +test_xv_SOURCES = \ +@top_srcdir@/tests/functional/xv_test/xv_test.c \ +@top_srcdir@/tests/functional/xv_test/data.c \ +@top_srcdir@/tests/functional/xv_test/xcb_api.c \ +@top_srcdir@/tests/functional/xv_test/test_xv_resize.c +endif diff --git a/tests/functional/xv_test/data.c b/tests/functional/xv_test/data.c new file mode 100644 index 0000000..08ca1d4 --- /dev/null +++ b/tests/functional/xv_test/data.c @@ -0,0 +1,242 @@ +#include <stdlib.h> +#include <stdio.h> +#include "data.h" +#include <xcb/shm.h> +#include <xcb/dri2.h> +#include <tbm_bufmgr.h> +#include <xf86drm.h> +#include <assert.h> +#include <sys/shm.h> +#include <fcntl.h> +#include "xv_types.h" +#include <string.h> + +static tbm_bufmgr bufmgr_p = NULL; +typedef struct +{ + int fd; + error_s error; +} dri_fd_s; + +static dri_fd_s +open_dri2 (xcb_connection_t *c, xcb_screen_t *screen) +{ + xcb_dri2_connect_cookie_t dri2_conn_ck; + xcb_dri2_connect_reply_t *dri2_conn_reply_p = NULL; + xcb_dri2_authenticate_cookie_t dri2_auth_ck; + xcb_dri2_authenticate_reply_t *dri2_auth_reply_p = NULL; + xcb_generic_error_t *error_p = NULL; + int error = -1; + dri_fd_s ret = {}; + char *device_name_p = NULL; + drm_magic_t drm_magic; + xcb_window_t window_id = screen->root; + + dri2_conn_ck = xcb_dri2_connect(c, window_id, XCB_DRI2_DRIVER_TYPE_DRI); + dri2_conn_reply_p = xcb_dri2_connect_reply(c, dri2_conn_ck, &error_p); + if (error_p) + { + ret.error.error_str = "Can't connect to DRI2 driver"; + ret.error.error_code = error_p->error_code; + goto close; + } + + device_name_p = strndup(xcb_dri2_connect_device_name (dri2_conn_reply_p), + xcb_dri2_connect_device_name_length (dri2_conn_reply_p)); + + ret.fd = open(device_name_p, O_RDWR); + + if (ret.fd < 0) + { + ret.error.error_str = "Can't open DRI2 file descriptor"; + ret.error.error_code = ret.fd; + goto close; + } + + error = drmGetMagic(ret.fd, &drm_magic); + if (error < 0) + { + ret.error.error_str = "Can't get drm magic"; + ret.error.error_code = error; + goto close; + } + + dri2_auth_ck = xcb_dri2_authenticate(c, window_id, drm_magic); + dri2_auth_reply_p = xcb_dri2_authenticate_reply(c, dri2_auth_ck, &error_p); + if (error_p) + { + ret.error.error_str = "Can't auth DRI2"; + ret.error.error_code = error_p->error_code; + goto close; + } + +close: + if (error_p) + free(error_p); + if (dri2_conn_reply_p) + free(dri2_conn_reply_p); + if (dri2_auth_reply_p) + free(dri2_auth_reply_p); + if (device_name_p) + free(device_name_p); + return ret; +} + +static shm_data_s +open_shm(xcb_connection_t *c, size_t data_size) +{ + xcb_generic_error_t *error_p = NULL; + shm_data_s ret_shm = {}; + + ret_shm.shm_seg_id = xcb_generate_id (c); + + int shm_id = shmget (IPC_PRIVATE, data_size, IPC_CREAT | 0777); + if (shm_id == -1) + { + ret_shm.error.error_str = "shm alloc error"; + ret_shm.error.error_code = shm_id; + goto close; + } + + ret_shm.shm_seg_addr = shmat (shm_id, NULL, 0); + if (!ret_shm.shm_seg_addr) + { + ret_shm.error.error_str = "shm attach error"; + goto close; + } + xcb_void_cookie_t xcb_shm_c = + xcb_shm_attach_checked (c, ret_shm.shm_seg_id, shm_id, 0); + error_p = xcb_request_check (c, xcb_shm_c); + if (error_p) + { + ret_shm.error.error_str = "xcb_shm attach error"; + goto close; + } +close: + shmctl (shm_id, IPC_RMID, NULL); + if (error_p) + free(error_p); + return ret_shm; +} + +static tbm_bo +get_data_random (size_t size, tbm_bufmgr bufmgr) +{ + FILE *fp = fopen ("/dev/urandom", "r"); + if (fp == NULL) + { + return NULL; + } + + tbm_bo tbo = tbm_bo_alloc (bufmgr, size, TBM_BO_DEFAULT); + if (tbo == NULL) + { + return NULL; + } + + /* copy raw data to tizen buffer object */ + tbm_bo_handle bo_handle = tbm_bo_map (tbo, TBM_DEVICE_CPU, TBM_OPTION_WRITE); + size_t error = fread (bo_handle.ptr, 1, size , fp); + tbm_bo_unmap (tbo); + fclose (fp); + if (error == 0) + { + tbm_bo_unref(tbo); + tbo = NULL; + } + return tbo; +} + +shm_data_s +get_image_random (xcb_connection_t *p_xcb_conn, xcb_screen_t *screen, + size_t *sizes, int num_planes) +{ + shm_data_s ret = {0,}; + XV_DATA_PTR xv_data = NULL; + if (bufmgr_p == NULL) + { + dri_fd_s dri2 = open_dri2 (p_xcb_conn, screen); + if (dri2.error.error_str) + { + ret.error = dri2.error; + goto close_l; + } + bufmgr_p = tbm_bufmgr_init (dri2.fd); + if (!bufmgr_p) + { + ret.error.error_str = "Can't open TBM connection\n"; + ret.error.error_code = 0; + goto close_l; + } + } + + if (sizes == NULL) + { + ret.error.error_str = "Sizes argument is NULL"; + goto close_l; + } + + if (num_planes > 3 || num_planes < 1) + { + ret.error.error_str = "Wrong num_planes argument"; + goto close_l; + } + size_t full_size = 0; + int i; + for (i = 0; i < num_planes; i++) + { + full_size += sizes[i]; + } + ret = open_shm(p_xcb_conn, full_size); + if (ret.error.error_str) + goto close_l; + + xv_data = ret.shm_seg_addr; + XV_INIT_DATA (xv_data); + tbm_bo temp_bo = NULL; + switch (num_planes) + { + case 3: + temp_bo = get_data_random (sizes[2], bufmgr_p); + if (temp_bo == NULL) + { + ret.error.error_str = "Can't alloc tbm object"; + goto close_l; + } + xv_data->CrBuf = tbm_bo_export(temp_bo); + case 2: + temp_bo = get_data_random (sizes[1], bufmgr_p); + if (temp_bo == NULL) + { + ret.error.error_str = "Can't alloc tbm object"; + goto close_l; + } + xv_data->CbBuf = tbm_bo_export(temp_bo); + case 1: + temp_bo = get_data_random (sizes[0], bufmgr_p); + if (temp_bo == NULL) + { + ret.error.error_str = "Can't alloc tbm object\n"; + goto close_l; + } + xv_data->YBuf = tbm_bo_export(temp_bo); + break; + } + + return ret; +close_l: + // TODO: Close DRI + // TODO: Close tbm + if (xv_data) + { + if (xv_data->YBuf != 0) + tbm_bo_unref(tbm_bo_import(bufmgr_p, xv_data->YBuf)); + if (xv_data->CbBuf != 0) + tbm_bo_unref(tbm_bo_import(bufmgr_p, xv_data->CbBuf)); + if (xv_data->CrBuf != 0) + tbm_bo_unref(tbm_bo_import(bufmgr_p, xv_data->CrBuf)); + } + + return ret; +} + diff --git a/tests/functional/xv_test/data.h b/tests/functional/xv_test/data.h new file mode 100644 index 0000000..81329c0 --- /dev/null +++ b/tests/functional/xv_test/data.h @@ -0,0 +1,18 @@ +#ifndef DATA_H +#define DATA_H +#include <xcb/xcb.h> +#include <xcb/shm.h> +#include <xcb/xv.h> +#include "xv_test.h" + +typedef struct +{ + xcb_shm_seg_t shm_seg_id; + void *shm_seg_addr; + error_s error; +} shm_data_s; + +shm_data_s get_image_random (xcb_connection_t *xcb_conn_p, xcb_screen_t *screen, + size_t * sizes, int num_planes); + +#endif // DATA_H diff --git a/tests/functional/xv_test/test_xv_resize.c b/tests/functional/xv_test/test_xv_resize.c new file mode 100644 index 0000000..0c77dd7 --- /dev/null +++ b/tests/functional/xv_test/test_xv_resize.c @@ -0,0 +1,520 @@ +#include <xcb/xcb.h> +#include <xcb/xv.h> +#include <xcb/shm.h> +#include <xcb/dri2.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <xf86drm.h> +#include <fcntl.h> +#include <unistd.h> +#include <tbm_bufmgr.h> +#include <assert.h> +#include "xv_test.h" +#include "data.h" +#include "xcb_api.h" + +typedef struct +{ + xcb_atom_t atom_hflip; + xcb_atom_t atom_vflip; + xcb_atom_t atom_rotate; + xcb_atom_t atom_buffer; + error_s error; +} test_atom_s; + +typedef struct +{ + size_t steps; + rectangle_s *src_crop_p; + rectangle_s *dst_crop_p; +} route_map_s; + +typedef struct +{ + error_s error; + route_map_s route_map; +} get_route_map_s; + +typedef struct +{ + xcb_xv_port_t xv_open_port; + test_atom_s atoms; + xcb_connection_t *xcb_conn_p; + xcb_screen_t *screen_p; + xcb_window_t window_id; + xcb_gcontext_t gc_id; + route_map_s route_map; +} test_data_s; + +static test_data_s *st_test_data_p = NULL; + +static error_s test_xv_resize_prepare(xcb_connection_t *xcb_conn_p, + xcb_screen_t * screen_p, rule_s rule); + +static error_s test_xv_resize_run(xcb_connection_t *xcb_conn_p, + xcb_screen_t * screen_p, rule_s rule); + +static error_s test_xv_resize_close(xcb_connection_t *xcb_conn_p, + xcb_screen_t * screen_p, rule_s rule); + +test_atom_s +create_atoms (xcb_connection_t *xcb_conn_p) +{ + char const *atom_name_a[4] = { + "_USER_WM_PORT_ATTRIBUTE_HFLIP", + "_USER_WM_PORT_ATTRIBUTE_VFLIP", + "_USER_WM_PORT_ATTRIBUTE_ROTATION", + "XV_RETURN_BUFFER"}; + xcb_intern_atom_cookie_t atom_ck_a[4] = {}; + xcb_intern_atom_reply_t *atom_reply_p = NULL; + test_atom_s ret = {}; + xcb_generic_error_t *error_p = NULL; + int i; + for (i = 0; i < 4; i++) + { + atom_ck_a[i] = + xcb_intern_atom (xcb_conn_p, 0, (uint16_t) strlen(atom_name_a[i]), + atom_name_a[i]); + } + for (i = 0; i < 4; i++) + { + atom_reply_p = + xcb_intern_atom_reply (xcb_conn_p , atom_ck_a[i], &error_p); + + if (error_p) + { + ret.error.error_str = "Can't register atom"; + ret.error.error_code = error_p->error_code; + goto close_l; + } + if (atom_reply_p == NULL) + { + ret.error.error_str = "Can't register atom"; + goto close_l; + } + switch (i) + { + case 0: + ret.atom_hflip = atom_reply_p->atom; + break; + case 1: + ret.atom_vflip = atom_reply_p->atom; + break; + case 2: + ret.atom_rotate = atom_reply_p->atom; + break; + case 3: + ret.atom_buffer = atom_reply_p->atom; + break; + } + free(atom_reply_p); + atom_reply_p = NULL; + } + +close_l: + + if (error_p) + free(error_p); + if (atom_reply_p) + free(atom_reply_p); + return ret; +} + +get_route_map_s +create_route_map (rule_s *rules) +{ + get_route_map_s ret = {}; + rectangle_s *src_crop_p = NULL; + rectangle_s *dst_crop_p = NULL; + if (rules == NULL) + { + ret.error.error_str = "Wrong input arguments"; + goto close_l; + } + size_t steps_width = (rules->max_width - rules->min_width)/10; + size_t steps_height = (rules->max_height - rules->max_height)/10; + size_t steps = (max(steps_width, steps_height))*2; + + src_crop_p = calloc(steps, sizeof(rectangle_s)); + if (!src_crop_p) + { + ret.error.error_str = "Can't alloc memory"; + goto close_l; + } + dst_crop_p = calloc(steps, sizeof(rectangle_s)); + if (!dst_crop_p) + { + ret.error.error_str = "Can't alloc memory"; + goto close_l; + } +// pace_dst_crop.height = (uint32_t) +// ((double)(rules->max_size.height - rules->min_size.height)/ +// (rules->steps * 2)); +// pace_dst_crop.width = (uint32_t) +// ((double)(rules->max_size.width - rules->min_size.width)/ +// (rules->steps * 2)); +// pace_src_crop.height = (uint32_t) +// ((double)(rules->image_height - rules->min_size.height)/ +// (rules->steps * 2)); +// pace_src_crop.width = (uint32_t) +// ((double)(rules->image_width - rules->min_size.width)/ +// (rules->steps * 2)); + if (!rules->src_change) + { + src_crop_p[0].x = 0; + src_crop_p[0].y = 0; + src_crop_p[0].width = rules->image_width; + src_crop_p[0].height = rules->image_height; + } + else + { + src_crop_p[0].x = 0; + src_crop_p[0].y = 0; + src_crop_p[0].width = rules->min_width; + src_crop_p[0].height = rules->min_height; + } + + if (!rules->dst_change) + { + dst_crop_p[0].x = 0; + dst_crop_p[0].y = 0; + dst_crop_p[0].width = rules->image_width; + dst_crop_p[0].height = rules->image_height; + } + else + { + dst_crop_p[0].x = 0; + dst_crop_p[0].y = 0; + dst_crop_p[0].width = rules->min_width; + dst_crop_p[0].height = rules->min_height; + } + int i; + for (i = 1; i < (steps/2); i++) + { + if (!rules->src_change) + { + src_crop_p[i].x = 0; + src_crop_p[i].y = 0; + src_crop_p[i].width = rules->image_width; + src_crop_p[i].height = rules->image_height; + } + else + { + src_crop_p[i].x = 0; + src_crop_p[i].y = 0; + src_crop_p[i].width = + ((src_crop_p[i-1].width + 10) <= rules->image_width) ? + (src_crop_p[i-1].width + 10) : src_crop_p[i-1].width; + src_crop_p[i].height = + ((src_crop_p[i-1].height + 10) <= rules->image_height) ? + (src_crop_p[i-1].height + 10) : src_crop_p[i-1].height; + } + if (!rules->dst_change) + { + dst_crop_p[i].x = 0; + dst_crop_p[i].y = 0; + dst_crop_p[i].width = rules->image_width; + dst_crop_p[i].height = rules->image_height; + } + else + { + dst_crop_p[i].x = 0; + dst_crop_p[i].y = 0; + dst_crop_p[i].width = + ((dst_crop_p[i-1].width + 10) <= rules->max_width) ? + (dst_crop_p[i-1].width + 10) : dst_crop_p[i-1].width; + dst_crop_p[i].height = + ((dst_crop_p[i-1].height + 10) <= rules->max_height) ? + (dst_crop_p[i-1].height + 10) : dst_crop_p[i-1].height; + } + } + for (i = (steps/2); i < steps; i++) + { + if (!rules->src_change) + { + src_crop_p[i].x = 0; + src_crop_p[i].y = 0; + src_crop_p[i].width = rules->image_width; + src_crop_p[i].height = rules->image_height; + } + else + { + src_crop_p[i].x = 0; + src_crop_p[i].y = 0; + src_crop_p[i].width = + ((src_crop_p[i-1].width - 10) >= rules->min_width) ? + (src_crop_p[i-1].width - 10) : src_crop_p[i-1].width; + src_crop_p[i].height = + ((src_crop_p[i-1].height - 10) >= rules->min_height) ? + (src_crop_p[i-1].height - 10) : src_crop_p[i-1].height; + } + if (!rules->dst_change) + { + dst_crop_p[i].x = 0; + dst_crop_p[i].y = 0; + dst_crop_p[i].width = rules->image_width; + dst_crop_p[i].height = rules->image_height; + } + else + { + dst_crop_p[i].x = 0; + dst_crop_p[i].y = 0; + dst_crop_p[i].width = + ((dst_crop_p[i-1].width - 10) >= rules->min_width) ? + (dst_crop_p[i-1].width - 10) : dst_crop_p[i-1].width; + dst_crop_p[i].height = + ((dst_crop_p[i-1].height - 10) >= rules->min_height) ? + (dst_crop_p[i-1].height - 10) : dst_crop_p[i-1].height; + } + } + + ret.route_map.src_crop_p = src_crop_p; + ret.route_map.dst_crop_p = dst_crop_p; + ret.route_map.steps = steps; + return ret; +close_l: + if (src_crop_p) + free(src_crop_p); + if (dst_crop_p) + free(dst_crop_p); + return ret; +} +test_case_s +test_xv_resize_init (void) +{ + test_case_s ret = {}; + ret.prepare_test = test_xv_resize_prepare; + ret.run_test = test_xv_resize_run; + ret.close_test = test_xv_resize_close; + return ret; +} + + +static error_s +test_xv_resize_prepare(xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p, rule_s rule) +{ + error_s ret = {}; + st_test_data_p = malloc(sizeof(test_data_s)); + DBG; + if (!st_test_data_p) + { + ret.error_str = "Can't alloc memory for test_data_s"; + ret.error_code = 0; + goto close_l; + } + st_test_data_p->xcb_conn_p = xcb_conn_p; + st_test_data_p->screen_p = screen_p; + DBG; + get_xv_port_s xv_port = open_adaptor (xcb_conn_p, screen_p->root); + if (xv_port.error.error_str) + { + ret = xv_port.error; + goto close_l; + } + DBG; + st_test_data_p->xv_open_port = xv_port.xv_port_id; + st_test_data_p->atoms = create_atoms (xcb_conn_p); + if(st_test_data_p->atoms.error.error_str) + { + ret = st_test_data_p->atoms.error; + goto close_l; + } + DBG; + + get_window_s window = create_window(xcb_conn_p, screen_p); + if (window.error.error_str) + { + ret = window.error; + goto close_l; + } + st_test_data_p->window_id = window.window_id; + get_gc_s gc = create_gc (xcb_conn_p, st_test_data_p->window_id, screen_p); + if (gc.error.error_str) + { + ret = gc.error; + goto close_l; + } + st_test_data_p->gc_id = gc.gc_id; + + get_route_map_s route = create_route_map (&rule); + + if (route.error.error_str) + { + ret = route.error; + goto close_l; + } + + st_test_data_p->route_map = route.route_map; + return ret; + +close_l: + DBG; + if (st_test_data_p) + free(st_test_data_p); +/* TODO: close port */ + st_test_data_p = NULL; + return ret; +} +static +error_s +test_xv_resize_run(xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p, rule_s rule) +{ + + DBG; + xcb_generic_error_t *error_p = NULL; + error_s ret = {}; + shm_data_s shm; + if (st_test_data_p == NULL) + { + ret.error_str = "Wrong argument"; + return ret; + } + frame_attr_s frame[st_test_data_p->route_map.steps]; + + DBG; + get_image_attr_s image_attr = + get_image_attr (xcb_conn_p, st_test_data_p->xv_open_port, + rule.image_fourcc, rule.image_width, rule.image_height); + DBG; + if (image_attr.error.error_str) + { + ret = image_attr.error; + goto close_l; + } + /* get raw data */ + + size_t max_j = rule.count_uniqes_frames; + DBG; + int i,j = 0; + for (i = 0; i < st_test_data_p->route_map.steps; i++) + { + + if (i == 0) + printf("Generate data:\n"); + if (i < max_j) + { + shm = get_image_random (xcb_conn_p, screen_p, + image_attr.image.sizes, + image_attr.image.num_planes); + if (shm.error.error_str) + { + ret = shm.error; + goto close_l; + } + frame[i].segment = shm.shm_seg_id; + printf("%f%%\n", ((double)i/max_j)); + } + else + { + if (j == max_j) + { + j = 0; + } + frame[i].segment = frame[j].segment; + j++; + } + + frame[i].image_p = &image_attr.image; + frame[i].src_crop_p = &st_test_data_p->route_map.src_crop_p[i]; + frame[i].dst_crop_p = &st_test_data_p->route_map.dst_crop_p[i]; + + } + DBG; + xcb_void_cookie_t map_window_ck = + xcb_map_window_checked (xcb_conn_p, st_test_data_p->window_id); + xcb_void_cookie_t put_image_ck = + put_image (xcb_conn_p, st_test_data_p->xv_open_port, + st_test_data_p->window_id, st_test_data_p->gc_id, &frame[0]); + j = 1; + + error_p = xcb_request_check (xcb_conn_p, map_window_ck); + if (error_p) + { + ret.error_str = "Can't make transparent window"; + ret.error_code = error_p->error_code; + goto close_l; + } + + xcb_generic_event_t *e; + xcb_client_message_event_t* msg; + DBG; + while (1) + { + e = xcb_poll_for_event(xcb_conn_p); + while (e) + { + switch (e->response_type) + { + case XCB_CLIENT_MESSAGE: + msg = (xcb_client_message_event_t *)e; + if (msg->type == st_test_data_p->atoms.atom_buffer) + { + unsigned int keys[3] = {0, }; + keys[0] = msg->data.data32[0]; + keys[1] = msg->data.data32[1]; + keys[2] = msg->data.data32[2]; + printf ("receive: %d, %d, %d (<= Xorg)\n", + keys[0], keys[1], keys[2]); + } + + break; + case XCB_EXPOSE: + break; + default: + break; + } + free(e); + e = xcb_poll_for_event(xcb_conn_p); + } + + error_p = xcb_request_check (xcb_conn_p, put_image_ck); + if (error_p) + { + ret.error_str = "Can't draw image frame"; + ret.error_code = error_p->error_code; + goto close_l; + } + if (j >= st_test_data_p->route_map.steps) + j = 0; + put_image_ck = put_image (xcb_conn_p, st_test_data_p->xv_open_port, + st_test_data_p->window_id, st_test_data_p->gc_id, + &frame[j]); + printf("src:(%dwx%dh) dst: (%dwx%dh)\n", + frame[j].src_crop_p->width, frame[j].src_crop_p->height, + frame[j].dst_crop_p->width, frame[j].dst_crop_p->height); + j++; + usleep(1000000/rule.frame_per_second); + } + +close_l: + if (error_p) + free(error_p); + return ret; +} + +static error_s +test_xv_resize_close(xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p, rule_s rule) +{ + error_s ret = {}; + return ret; +} + +#if 0 +xcb_xv_list_image_formats_reply_t *list = + xcb_xv_list_image_formats_reply (conn, + xcb_xv_list_image_formats (conn, a->base_id), NULL); +if (list == NULL) + return NULL; + +/* Check available XVideo chromas */ +xcb_xv_query_image_attributes_reply_t *attr = NULL; +unsigned rank = UINT_MAX; + +for (const xcb_xv_image_format_info_t *f = + xcb_xv_list_image_formats_format (list), + *f_end = + f + xcb_xv_list_image_formats_format_length (list); + f < f_end; + f++) +#endif diff --git a/tests/functional/xv_test/test_xv_resize.h b/tests/functional/xv_test/test_xv_resize.h new file mode 100644 index 0000000..fdff351 --- /dev/null +++ b/tests/functional/xv_test/test_xv_resize.h @@ -0,0 +1,6 @@ +#ifndef TEST_XV_RESIZE_H +#define TEST_XV_RESIZE_H +#include "xv_test.h" +test_case_s test_xv_resize_init (void); + +#endif // TEST_XV_RESIZE_H diff --git a/tests/functional/xv_test/xcb_api.c b/tests/functional/xv_test/xcb_api.c new file mode 100644 index 0000000..4f5b013 --- /dev/null +++ b/tests/functional/xv_test/xcb_api.c @@ -0,0 +1,258 @@ +#include "xcb_api.h" +get_image_attr_s +get_image_attr(xcb_connection_t * xcb_conn_p, xcb_xv_port_t xv_port, + uint32_t fourcc_id, uint16_t width, uint16_t height) +{ + get_image_attr_s ret = {}; + xcb_generic_error_t *error_p = NULL; + xcb_xv_query_image_attributes_reply_t * xv_attr_reply_p = NULL; + + if (!xcb_conn_p || xv_port == XCB_XV_BAD_PORT) + { + ret.error.error_str = "Wrong arguments"; + ret.error.error_code = 0; + goto close; + } + + xcb_xv_query_image_attributes_cookie_t xv_attr_ck = + xcb_xv_query_image_attributes (xcb_conn_p /**< */, + xv_port /**< */, + fourcc_id /**< */, + width /**< */, + height /**< */); + xv_attr_reply_p = + xcb_xv_query_image_attributes_reply (xcb_conn_p /**< */, + xv_attr_ck /**< */, + &error_p /**< */); + if (error_p) + { + ret.error.error_str = "Can't query image attr"; + ret.error.error_code = error_p->error_code; + goto close; + } + + if (xv_attr_reply_p->data_size == 0) + { + ret.error.error_str = "Wrong input data format"; + ret.error.error_code = 0; + goto close; + } + + const uint32_t *offsets= + xcb_xv_query_image_attributes_offsets (xv_attr_reply_p); + const uint32_t *pitches= + xcb_xv_query_image_attributes_pitches (xv_attr_reply_p); + ret.image.num_planes = xv_attr_reply_p->num_planes; + ret.image.full_size = xv_attr_reply_p->data_size; + ret.image.fourcc_id = fourcc_id; + ret.image.width = xv_attr_reply_p->width; + ret.image.height = xv_attr_reply_p->height; + switch (ret.image.num_planes) + { + case 3: + ret.image.sizes[0] = offsets[1]; + ret.image.sizes[1] = offsets[2] - offsets[1]; + ret.image.sizes[2] = ret.image.full_size - offsets[2]; + break; + case 2: + ret.image.sizes[1] = ret.image.full_size - offsets[1]; + ret.image.sizes[0] = offsets[1]; + break; + case 1: + ret.image.sizes[0] = ret.image.full_size; + break; + default: + ret.error.error_str = "Wrong input data format"; + goto close; + break; + } + int i; + for (i = 0; i < ret.image.num_planes; ++i) + { + ret.image.offsets[i] = offsets[i]; + ret.image.pitches[i] = pitches[i]; + } + +close: + + if (error_p) + free(error_p); + if (xv_attr_reply_p) + free(xv_attr_reply_p); + + return ret; +} + +xcb_void_cookie_t +put_image (xcb_connection_t * xcb_conn_p, xcb_xv_port_t xv_port, + xcb_window_t window_id, xcb_gcontext_t gc_id, frame_attr_s *frame_p) +{ + xcb_void_cookie_t ret = {}; + ret = xcb_xv_shm_put_image_checked ( + xcb_conn_p, xv_port, window_id, gc_id, + frame_p->segment, frame_p->image_p->fourcc_id, 0, + frame_p->src_crop_p->x, frame_p->src_crop_p->y, + frame_p->src_crop_p->width, frame_p->src_crop_p->height, + frame_p->dst_crop_p->x, frame_p->dst_crop_p->y, + frame_p->dst_crop_p->width, frame_p->dst_crop_p->height, + frame_p->image_p->width, frame_p->image_p->height, 0); + return ret; +} + +get_xv_port_s +open_adaptor (xcb_connection_t *xcb_conn_p, xcb_window_t window_id) +{ + xcb_generic_error_t *error_p = NULL; + get_xv_port_s ret = {}; + xcb_xv_adaptor_info_iterator_t xv_adaptor_iter; + xcb_xv_grab_port_reply_t *xv_grab_port_p = NULL; + xcb_xv_query_adaptors_cookie_t xv_adaptor_ck = + xcb_xv_query_adaptors (xcb_conn_p, window_id); + xcb_xv_query_adaptors_reply_t *xv_adaptors_list_p = + xcb_xv_query_adaptors_reply (xcb_conn_p, xv_adaptor_ck, &error_p); + if (error_p) + { + ret.error.error_str = "Can't query xv adaptor"; + ret.error.error_code = error_p->error_code; + goto close; + } + + for (xv_adaptor_iter = xcb_xv_query_adaptors_info_iterator (xv_adaptors_list_p); + xv_adaptor_iter.rem > 0; + xcb_xv_adaptor_info_next (&xv_adaptor_iter)) + { + if (!(xv_adaptor_iter.data->type & XCB_XV_TYPE_IMAGE_MASK)) + continue; + int i; + for (i = 0; i < xv_adaptor_iter.data->num_ports; i++) + { + xcb_xv_port_t xv_port = xv_adaptor_iter.data->base_id + i; + xcb_xv_grab_port_cookie_t xv_grab_port_c = + xcb_xv_grab_port (xcb_conn_p, xv_port, XCB_CURRENT_TIME); + xv_grab_port_p = + xcb_xv_grab_port_reply (xcb_conn_p, xv_grab_port_c, &error_p); + if (error_p) + { + ret.error.error_str = "Can't query xv adaptor"; + ret.error.error_code = error_p->error_code; + goto close; + } + + if (xv_grab_port_p && xv_grab_port_p->result == 0) + { + ret.xv_port_id = xv_port; + break; + } + } + if (ret.xv_port_id != 0) + { + break; + } + } + +close: + if (xv_adaptors_list_p) + free(xv_adaptors_list_p); + if (xv_grab_port_p) + free(xv_grab_port_p); + if (error_p) + free(error_p); + + return ret; +} + +get_gc_s +create_gc (xcb_connection_t *xcb_conn_p, xcb_drawable_t drawable_id, xcb_screen_t * screen_p) +{ + get_gc_s ret = {}; + xcb_void_cookie_t gc_ck; + xcb_generic_error_t *error_p = NULL; + uint32_t values[2]; + ret.gc_id = xcb_generate_id (xcb_conn_p); + uint32_t mask = XCB_GC_GRAPHICS_EXPOSURES; + values[0] = 0; +// values[0] = screen_p->white_pixel; + gc_ck = xcb_create_gc_checked (xcb_conn_p, ret.gc_id, drawable_id, mask, values); + + error_p = xcb_request_check (xcb_conn_p, gc_ck); + if (error_p) + { + ret.error.error_str = "Can't create graphic context"; + ret.error.error_code = error_p->error_code; + goto close; + } +close: + if (error_p) + free(error_p); + return ret; +} + +get_window_s +create_window (xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p) +{ + get_window_s ret = {}; + ret.window_id = xcb_generate_id (xcb_conn_p); + uint32_t mask; + uint32_t values[3]; + xcb_generic_error_t *error_p = NULL; + mask = XCB_CW_EVENT_MASK; +// values[0] = screen_p->black_pixel; + values[0] = XCB_EVENT_MASK_EXPOSURE; + xcb_void_cookie_t window_ck = + xcb_create_window_checked (xcb_conn_p, + screen_p->root_depth, + ret.window_id, screen_p->root, + 0, 0, screen_p->width_in_pixels, + screen_p->height_in_pixels, + 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen_p->root_visual, + mask, values); + + error_p = xcb_request_check (xcb_conn_p, window_ck); + if (error_p) + { + ret.error.error_str = "Can't create window"; + ret.error.error_code = error_p->error_code; + goto close; + } + +close: + if (error_p) + free(error_p); + return ret; +} + +static xcb_format_t * +find_format_by_depth (const xcb_setup_t *setup, uint8_t depth) +{ + xcb_format_t *fmt = xcb_setup_pixmap_formats(setup); + xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(setup); + for(; fmt != fmtend; ++fmt) + if(fmt->depth == depth) + return fmt; + return 0; +} + +#if 1 +xcb_void_cookie_t +make_transparent (xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p, + xcb_drawable_t drawable_id, xcb_gcontext_t gc_id) +{ + xcb_format_t *fmt = find_format_by_depth(xcb_get_setup (xcb_conn_p), 32); + + uint32_t base = 1920 * fmt->bits_per_pixel; + uint32_t pad = fmt->scanline_pad >> 3; + uint32_t b = base + pad - 1; + /* faster if pad is a power of two */ + if (((pad - 1) & pad) == 0) + b = b & -pad; + else + b = b - b % pad; + uint32_t size = 1080 * b; + uint8_t *data = (uint8_t *)calloc (1, size); + return + xcb_put_image_checked (xcb_conn_p, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable_id, gc_id, + screen_p->width_in_pixels, screen_p->height_in_pixels, 0, 0, + 0, 32, size, data); +} +#endif diff --git a/tests/functional/xv_test/xcb_api.h b/tests/functional/xv_test/xcb_api.h new file mode 100644 index 0000000..5eaf9e5 --- /dev/null +++ b/tests/functional/xv_test/xcb_api.h @@ -0,0 +1,80 @@ +#ifndef XV_API_H +#define XV_API_H +#include <xcb/xcb.h> +#include <xcb/xv.h> +#include <xcb/xfixes.h> + +#include <xcb/xcb_util.h> +#include "xv_test.h" + +typedef struct +{ + uint32_t width; + uint32_t height; +} size_s; + +typedef struct +{ + int32_t x; + int32_t y; + uint32_t width; + uint32_t height; +} rectangle_s; + +typedef struct +{ + uint32_t fourcc_id; + uint16_t num_planes; + size_t full_size; + size_t sizes[3]; + uint32_t offsets[3]; + uint32_t pitches[3]; + uint32_t width; + uint32_t height; +} image_attr_s; + +typedef struct +{ + rectangle_s *src_crop_p; + rectangle_s *dst_crop_p; + image_attr_s *image_p; + xcb_shm_seg_t segment; +} frame_attr_s; + +typedef struct +{ + xcb_xv_port_t xv_port_id; + error_s error; +} get_xv_port_s; + +typedef struct +{ + error_s error; + image_attr_s image; +} get_image_attr_s; + +typedef struct +{ + xcb_window_t window_id; + error_s error; +} get_window_s; + +typedef struct +{ + xcb_gcontext_t gc_id; + error_s error; +} get_gc_s; + +get_gc_s create_gc (xcb_connection_t *xcb_conn_p, xcb_drawable_t drawable_id, xcb_screen_t * screen_p); +get_window_s create_window (xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p); + + +get_image_attr_s get_image_attr(xcb_connection_t * xcb_conn_p, xcb_xv_port_t xv_port, + uint32_t fourcc_id, uint16_t width, uint16_t height); +get_xv_port_s open_adaptor (xcb_connection_t *xcb_conn_p, xcb_window_t window_id); +xcb_void_cookie_t put_image (xcb_connection_t * xcb_conn_p, xcb_xv_port_t xv_port, + xcb_window_t window_id, xcb_gcontext_t gc_id, frame_attr_s *frame_p); + +xcb_void_cookie_t make_transparent (xcb_connection_t *xcb_conn_p, xcb_screen_t * screen_p, + xcb_drawable_t drawable_id, xcb_gcontext_t gc_id); +#endif // XV_API_H diff --git a/tests/functional/xv_test/xv_test.c b/tests/functional/xv_test/xv_test.c new file mode 100644 index 0000000..e6929c6 --- /dev/null +++ b/tests/functional/xv_test/xv_test.c @@ -0,0 +1,219 @@ +#include "xv_test.h" +#include <xcb/xcb.h> +#include <xcb/xv.h> +#include <xcb/shm.h> +#include <xcb/dri2.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <xcb/dri2.h> +#include <xf86drm.h> +#include <fcntl.h> +#include "xv_types.h" +#include <tbm_bufmgr.h> +#include "data.h" +#include <sys/time.h> +#include "test_xv_resize.h" + +typedef struct +{ + size_t num_tests; + test_case_s * test_case_array; + error_s * error_array; +} test_map_s; + +error_s +check_xv (xcb_connection_t *xcb_conn_p) +{ + xcb_xv_query_extension_cookie_t xv_cookie; + xcb_xv_query_extension_reply_t *xv_reply = NULL; + xcb_generic_error_t * error_p = NULL; + error_s ret = {}; + xv_cookie = xcb_xv_query_extension (xcb_conn_p); + xv_reply = xcb_xv_query_extension_reply (xcb_conn_p, xv_cookie, &error_p); + if (error_p) + { + ret.error_str = "Can't find xv extension"; + ret.error_code = error_p->error_code; + } + + if (xv_reply) + free(xv_reply); + if (error_p) + free(error_p); + return ret; +} + +error_s +check_shm (xcb_connection_t *xcb_conn_p) +{ + xcb_shm_query_version_cookie_t shm_cookie; + xcb_shm_query_version_reply_t *shm_reply_p = NULL; + xcb_generic_error_t * error_p = NULL; + error_s ret = {}; + shm_cookie = xcb_shm_query_version (xcb_conn_p); + shm_reply_p = xcb_shm_query_version_reply (xcb_conn_p, shm_cookie, &error_p); + if (error_p) + { + ret.error_str = "Can't find shm extension"; + ret.error_code = error_p->error_code; + } + + if (shm_reply_p) + free(shm_reply_p); + if (error_p) + free(error_p); + return ret; +} + +error_s +check_dri2 (xcb_connection_t *xcb_conn_p) +{ + error_s ret = {}; + xcb_generic_error_t *error_p = NULL; + xcb_dri2_query_version_cookie_t xcb_dri2_ck = + xcb_dri2_query_version (xcb_conn_p, + XCB_DRI2_MAJOR_VERSION, + XCB_DRI2_MINOR_VERSION); + xcb_dri2_query_version_reply_t * xcb_dri2_reply_p = + xcb_dri2_query_version_reply (xcb_conn_p, + xcb_dri2_ck, + &error_p); + if (error_p) + { + ret.error_str = "Can't find dri2 extension"; + ret.error_code = error_p->error_code; + } + + if (xcb_dri2_reply_p) + free(xcb_dri2_reply_p); + if (error_p) + free(error_p); + return ret; +} + +rule_s +make_rule (xcb_connection_t *xcb_conn_p, xcb_screen_t *screen_p) +{ + rule_s ret = {}; + ret.count_frame_per_change = 2; + ret.count_uniqes_frames = 10; + ret.frame_per_second = 30; + ret.max_width = screen_p->width_in_pixels; + ret.max_height = screen_p->height_in_pixels; + ret.dst_change = 1; + ret.src_change = 0; + ret.min_height = 100; + ret.min_width = 100; + ret.image_width = 640; + ret.image_height = 480; + ret.image_fourcc = FOURCC_SR32; + return ret; +} + +test_map_s +init_tests (void) +{ + test_map_s ret = {}; + ret.num_tests = 1; + ret.test_case_array = calloc(ret.num_tests, sizeof(test_case_s)); + if (ret.test_case_array == NULL) + { + fprintf(stderr, "Can't alloc memory for test"); + ret.num_tests = 0; + goto error_close; + } + + ret.error_array = calloc(ret.num_tests, sizeof(error_s)); + if (ret.error_array == NULL) + { + fprintf(stderr, "Can't alloc memory for test"); + ret.num_tests = 0; + goto error_close; + } + ret.test_case_array[0] = test_xv_resize_init(); + return ret; +error_close: + if (ret.test_case_array) + free(ret.test_case_array); + if (ret.error_array) + free(ret.error_array); + + return ret; +} + +int main () +{ + xcb_screen_iterator_t screen_iter; + xcb_connection_t *xcb_conn_p; + const xcb_setup_t *setup; + xcb_screen_t *screen_p; + int screen_number; + test_map_s tests = {}; + rule_s rule_p = {}; + error_s ret; + /* getting the connection */ + xcb_conn_p = xcb_connect (NULL, &screen_number); + if (!xcb_conn_p) + { + fprintf(stderr, "ERROR: can't connect to an X server\n"); + } + ret = check_xv (xcb_conn_p); + exit_if_fail (ret); + ret = check_shm (xcb_conn_p); + exit_if_fail (ret); + ret = check_dri2 (xcb_conn_p); + exit_if_fail (ret); + DBG; + /* getting the current screen */ + setup = xcb_get_setup (xcb_conn_p); + screen_p = NULL; + screen_iter = xcb_setup_roots_iterator (setup); + for (; screen_iter.rem != 0; --screen_number, xcb_screen_next (&screen_iter)) + if (screen_number == 0) + { + screen_p = screen_iter.data; + break; + } + if (!screen_p) { + fprintf (stderr, "ERROR: can't get the current screen\n"); + xcb_disconnect (xcb_conn_p); + return -1; + } + rule_p = make_rule (xcb_conn_p, screen_p); + tests = init_tests(); + if (tests.num_tests == 0) + { + fprintf (stderr, "Nothing to test. num_tests = 0\n"); + return 0; + } + int i; + DBG; + for (i = 0; i < tests.num_tests; i++) + { + ret = tests.test_case_array[i].prepare_test(xcb_conn_p, screen_p, rule_p); + if (ret.error_str) + { + tests.error_array[i] = ret; + continue; + } + ret = tests.test_case_array[i].run_test(xcb_conn_p, screen_p, rule_p); + if (ret.error_str) + { + tests.error_array[i] = ret; + continue; + } + ret = tests.test_case_array[i].close_test(xcb_conn_p, screen_p, rule_p); + if (ret.error_str) + { + tests.error_array[i] = ret; + continue; + } + } + for (i = 0; i < tests.num_tests; i++) + { + fprintf(stderr, "%s %d\n", tests.error_array[i].error_str, + tests.error_array[i].error_code); + } + return 0; +} diff --git a/tests/functional/xv_test/xv_test.h b/tests/functional/xv_test/xv_test.h new file mode 100644 index 0000000..3c33e27 --- /dev/null +++ b/tests/functional/xv_test/xv_test.h @@ -0,0 +1,111 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> + +#include <pthread.h> +#include <sys/types.h> +#include <sys/shm.h> +#include <xcb/xcb.h> + +#include <xf86drm.h> +#include <tbm_bufmgr.h> + +#include "xv_types.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define exit_if_fail(error_s) \ + {if (error_s.error_str) {\ + fprintf (stderr, "Error: '%s' code: '%d'\n", \ + error_s.error_str, error_s.error_code); \ + exit(-1);}} + +#define C(b,m) (((b) >> (m)) & 0xFF) +#define B(c,s) ((((unsigned int)(c)) & 0xff) << (s)) +#define FOURCC(a,b,c,d) (B(d,24) | B(c,16) | B(b,8) | B(a,0)) +#define FOURCC_STR(id) C(id,0), C(id,8), C(id,16), C(id,24) +#define FOURCC_RGB565 FOURCC('R','G','B','P') +#define FOURCC_RGB32 FOURCC('R','G','B','4') +#define FOURCC_I420 FOURCC('I','4','2','0') +#define FOURCC_SR16 FOURCC('S','R','1','6') +#define FOURCC_SR32 FOURCC('S','R','3','2') +#define FOURCC_S420 FOURCC('S','4','2','0') + +#define FOURCC_SN12 FOURCC('S','N','1','2') +#define XVIMAGE_SN12 \ + { \ + FOURCC_SN12, \ + XvYUV, \ + LSBFirst, \ + {'S','N','1','2', \ + 0x00,0x00,0x00,0x10,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71}, \ + 12, \ + XvPlanar, \ + 2, \ + 0, 0, 0, 0, \ + 8, 8, 8, \ + 1, 2, 2, \ + 1, 2, 2, \ + {'Y','U','V', \ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, \ + XvTopToBottom \ + } + +#define DBG \ +{printf("%s %d\n", __FUNCTION__, __LINE__);} + +typedef struct +{ + uint32_t min_width; + uint32_t min_height; + uint32_t max_width; + uint32_t max_height; + int src_change; + int dst_change; + uint32_t image_width; + uint32_t image_height; + uint32_t image_fourcc; + size_t count_frame_per_change; + size_t frame_per_second; + size_t count_uniqes_frames; + /* TODO: More rules */ +} rule_s; + +typedef struct +{ + uint8_t error_code; + char const * error_str; +} error_s; + +typedef error_s (*prepare_test_case) (xcb_connection_t *, xcb_screen_t *, rule_s); +typedef error_s (*run_test_case) (xcb_connection_t *, xcb_screen_t *, rule_s); +typedef error_s (*close_test_case)(xcb_connection_t *, xcb_screen_t *, rule_s); + +typedef struct _test_case_s +{ + prepare_test_case prepare_test; + run_test_case run_test; + close_test_case close_test; +} test_case_s; +#endif |