summaryrefslogtreecommitdiff
path: root/spectro/ccwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'spectro/ccwin.c')
-rw-r--r--spectro/ccwin.c693
1 files changed, 693 insertions, 0 deletions
diff --git a/spectro/ccwin.c b/spectro/ccwin.c
new file mode 100644
index 0000000..3c0d97f
--- /dev/null
+++ b/spectro/ccwin.c
@@ -0,0 +1,693 @@
+
+/*
+ * Argyll Color Correction System
+ * ChromeCast Display target patch window
+ *
+ * Author: Graeme W. Gill
+ * Date: 8/9/14
+ *
+ * Copyright 2013, 2014 Graeme W. Gill
+ * All rights reserved.
+ *
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef NT
+# include <winsock2.h>
+#endif
+#ifdef UNIX
+# include <sys/types.h>
+# include <ifaddrs.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# ifdef __FreeBSD__
+# include <sys/socket.h>
+# endif /* __FreeBSD__ */
+#endif
+#include "copyright.h"
+#include "aconfig.h"
+#include "icc.h"
+#include "numsup.h"
+#include "cgats.h"
+#include "conv.h"
+#include "dispwin.h"
+#include "conv.h"
+#include "mongoose.h"
+#include "ccast.h"
+#include "ccwin.h"
+#include "render.h"
+
+#undef WRITE_PNG /* [und] Write each test patch to "ccwin.png" */
+#undef CCTEST_PATTERN /* [und] Create dev. spatial test pattern */
+#undef SEND_TEST_FILE /* [und] Send this file name to ChromeCast instead of pattern */
+#undef DO_TIMING /* [und] Print rendering timing */
+#undef DEBUG /* [und] */
+
+#define DDITHER 1 /* 0 = no dither - quantize to PNG 8 bit RGB value */
+ /* [def] 1 = use error diffusion dithering with ccast quant model */
+ /* 2 = use crafted 4x4 dither cell */
+
+#define VWIDTH 1920.0 /* Video stream and display size */
+#define VHEIGHT 1080.0
+
+#define IWIDTH 1280.0 /* This is the native still image framebuffer size for ChromeCasts */
+#define IHEIGHT 720.0
+
+//#define STANDALONE_TEST
+
+#ifdef DEBUG
+#define errout stderr
+# define debug(xx) fprintf(errout, xx )
+# define debug2(xx) fprintf xx
+# define debugr(xx) fprintf(errout, xx )
+# define debugr2(xx) fprintf xx
+# define debugrr(xx) fprintf(errout, xx )
+# define debugrr2(xx) fprintf xx
+# define debugrr2l(lev, xx) fprintf xx
+#else
+#define errout stderr
+# define debug(xx)
+# define debug2(xx)
+# define debugr(xx) if (p->ddebug) fprintf(errout, xx )
+# define debugr2(xx) if (p->ddebug) fprintf xx
+# define debugrr(xx) if (callback_ddebug) fprintf(errout, xx )
+# define debugrr2(xx) if (callback_ddebug) fprintf xx
+# define debugrr2l(lev, xx) if (callback_ddebug >= lev) fprintf xx
+#endif
+
+/* ================================================================== */
+
+/* Chromwin context and (possible) web server */
+typedef struct _chws {
+ int verb;
+ int ddebug;
+
+ int direct; /* End PNG directly, rather than using web server */
+
+ struct mg_context *mg; /* Mongoose context (if needed) */
+ char *ws_url; /* Web server URL for accessing server */
+
+// double hoff, voff; /* Input position of test square */
+ double x, y; /* position of test square in pixels */
+ double w, h; /* size of test square in pixels */
+ int pno; /* Index to generate a sequence of URLs */
+ unsigned char *ibuf; /* Memory image of .png file */
+ size_t ilen;
+
+ ccast *cc; /* ChromeCast */
+
+ /* Update the png image */
+ int (*update)(struct _chws *p, unsigned char *ibuf, size_t ilen, double *bg);
+
+ /* Destroy ourselves */
+ void (*del)(struct _chws *p);
+
+} chws;
+
+static void chws_del(chws *p) {
+
+ if (p->mg != NULL)
+ mg_stop(p->mg);
+
+ if (p->cc != NULL)
+ p->cc->del(p->cc);
+
+ if (p->ibuf != NULL)
+ free(p->ibuf);
+
+ if (p->ws_url != NULL)
+ free(p->ws_url);
+
+ free(p);
+}
+
+/* Change the .png being served */
+/* Return nz on error */
+static int chws_update(chws *p, unsigned char *ibuf, size_t ilen, double *bg) {
+ char url[200];
+
+ debug("\nUpdate png\n");
+
+ if (p->ibuf != NULL)
+ free(p->ibuf);
+
+ p->ibuf = ibuf;
+ p->ilen = ilen;
+
+ /* Send the PNG swatch direct */
+ if (p->direct) {
+ double x, y, w, h;
+ /* Convert x,y,w,h to relative rather than pixel size */
+
+ debugr2((errout,"Got x %f y %f w %f h %f\n", p->x, p->y, p->w, p->h));
+
+ // Convert from quantized to direct loader parameters
+ if (p->w < IWIDTH)
+ x = p->x/(IWIDTH - p->w);
+ else
+ x = 0.0;
+ if (p->h < IHEIGHT)
+ y = p->y/(IHEIGHT - p->h);
+ else
+ y = 0.0;
+ w = p->w/(0.1 * IWIDTH);
+ h = p->h/(0.1 * IWIDTH);
+
+ debugr2((errout,"Sending direct x %f y %f w %f h %f\n", x, y, w, h));
+
+ if (p->cc->load(p->cc, NULL, p->ibuf, p->ilen, bg, x, y, w, h)) {
+ debugr2((errout,"ccwin_update direct load failed\n"));
+ return 1;
+ }
+
+ /* Using web server */
+ } else {
+
+#ifdef SEND_TEST_FILE
+ sprintf(url, "%s%s",p->ws_url, SEND_TEST_FILE);
+#else
+ sprintf(url, "%stpatch_%d.png",p->ws_url, ++p->pno);
+#endif
+ if (p->cc->load(p->cc, url, NULL, 0, NULL, 0.0, 0.0, 0.0, 0.0)) {
+ debugr2((errout,"ccwin_update server load failed\n"));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Web server event handler - return the current .png image */
+static void *ccwin_ehandler(enum mg_event event,
+ struct mg_connection *conn) {
+ const struct mg_request_info *request_info = mg_get_request_info(conn);
+ chws *p = (chws *)mg_get_user_data(conn);
+ char *cp;
+ char sbuf[200];
+
+ debugr2((errout,"ccwin_ehandler()\n"));
+
+ if (event != MG_NEW_REQUEST) {
+ return NULL;
+ }
+
+ debugr2((errout,"Event: uri = '%s'\n",request_info->uri));
+
+#ifdef SEND_TEST_FILE
+#pragma message("############################# ccwin.c SEND_TEST_FILE defined ! ##")
+ return NULL;
+#endif
+
+ if (p->ibuf != NULL && p->ilen > 0
+ && (cp = strrchr(request_info->uri, '.')) != NULL
+ && strcmp(cp, ".png") == 0) {
+
+ debugr2((errout,"Event: Loading %s\n",request_info->uri));
+
+ debugr2((errout,"Returning current png size %d bytes\n",(int)p->ilen));
+ sprintf(sbuf,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: image/png\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ ,(int)p->ilen);
+
+ mg_write(conn, sbuf, strlen(sbuf));
+ mg_write(conn, p->ibuf, p->ilen);
+
+ } else {
+ debugr2((errout,"Bad request or png - returning 404\n"));
+ sprintf(sbuf,
+ "HTTP/1.0 404 Not Found\r\n"
+ "\r\n"
+ "<html><body><h1>Error 404 - Not Found</h1></body></html>");
+
+ mg_write(conn, sbuf, strlen(sbuf));
+ }
+
+ return "yes";
+}
+
+chws *new_chws(
+ccast_id *cc_id, /* ChromeCast to open */
+double width, double height, /* Width and height as % */
+double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */
+int verb, int ddebug) {
+ chws *p;
+ const char *options[3];
+ char port[50];
+ int portno = 0; /* Port number allocated */
+ int forcedef = 0; /* Force default reciever app. */
+
+ if ((p = (chws *)calloc(sizeof(chws), 1)) == NULL) {
+ error("new_chws: calloc failed");
+ return NULL;
+ }
+
+ p->verb = verb;
+ p->ddebug = ddebug;
+
+ p->update = chws_update;
+ p->del = chws_del;
+
+ /* We make sure we round the test patch size and */
+ /* location to integer resolution so that we can know */
+ /* it's exact relationship to the upsampled pixel locations. */
+
+ /* Setup window size and position */
+ /* The default size is 10% of the width */
+ p->w = floor(width/100.0 * 0.1 * IWIDTH + 0.5);
+ if (p->w > IWIDTH)
+ p->w = IWIDTH;
+ p->h = floor(height/100.0 * 0.1 * IWIDTH + 0.5);
+ if (p->h > IHEIGHT)
+ p->h = IHEIGHT;
+
+ // Make offset be on an even pixel boundary, so that we know
+ // the up-filter phase.
+ p->x = floor((hoff * 0.5 + 0.5) * (IWIDTH - p->w) + 0.5);
+ p->y = floor((voff * 0.5 + 0.5) * (IHEIGHT - p->h) + 0.5);
+ if (((int)p->x) & 1)
+ p->x++;
+ if (((int)p->y) & 1)
+ p->y++;
+
+ if (verb) printf("Opening ChromeCast '%s'\n",cc_id->name);
+
+#ifdef SEND_TEST_FILE
+ forcedef = 1;
+#endif
+
+ /* Connect to the chrome cast */
+ if ((p->cc = new_ccast(cc_id, forcedef)) == NULL) {
+ error("new_ccast: failed");
+ chws_del(p);
+ return NULL;
+ }
+
+ p->direct = p->cc->get_direct_send(p->cc);
+
+ if (!p->direct) {
+ /* Create a web server */
+ options[0] = "listening_ports";
+// sprintf(port,"%d", 0); /* Use any available */
+ sprintf(port,"%d", 8081); /* Use fixed port for Linux firewall rule */
+ options[1] = port;
+ options[2] = NULL;
+
+ p->mg = mg_start(&ccwin_ehandler, (void *)p, options);
+
+ if ((p->ws_url = mg_get_url(p->mg)) == NULL) {
+ debugr2((errout, "mg_get_url() failed\n"));
+ chws_del(p);
+ return NULL;
+ }
+ if (p->ddebug)
+ printf("Created .png server at '%s'\n",p->ws_url);
+ }
+
+ return p;
+}
+
+
+/* ================================================================== */
+
+/* Get RAMDAC values. ->del() when finished. */
+/* Return NULL if not possible */
+static ramdac *ccwin_get_ramdac(dispwin *p) {
+ debugr("webdisp doesn't have a RAMDAC\n");
+ return NULL;
+}
+
+/* Set the RAMDAC values. */
+/* Return nz if not possible */
+static int ccwin_set_ramdac(dispwin *p, ramdac *r, int persist) {
+ debugr("webdisp doesn't have a RAMDAC\n");
+ return 1;
+}
+
+/* ----------------------------------------------- */
+/* Install a display profile and make */
+/* it the default for this display. */
+/* Return nz if failed */
+int ccwin_install_profile(dispwin *p, char *fname, ramdac *r, p_scope scope) {
+ debugr("webdisp doesn't support installing profiles\n");
+ return 1;
+}
+
+/* Un-Install a display profile */
+/* Return nz if failed, */
+int ccwin_uninstall_profile(dispwin *p, char *fname, p_scope scope) {
+ debugr("webdisp doesn't support uninstalling profiles\n");
+ return 1;
+}
+
+/* Get the currently installed display profile. */
+/* Return NULL if failed. */
+icmFile *ccwin_get_profile(dispwin *p, char *name, int mxlen) {
+ debugr("webdisp doesn't support getting the current profile\n");
+ return NULL;
+}
+
+/* ----------------------------------------------- */
+
+/* Change the window color. */
+/* Return 1 on error, 2 on window being closed */
+static int ccwin_set_color(
+dispwin *p,
+double r, double g, double b /* Color values 0.0 - 1.0 */
+) {
+ chws *ws = (chws *)p->pcntx;
+ int j;
+ double orgb[3]; /* Previous RGB value */
+ double kr, kf;
+ int update_delay = 0;
+
+ debugr("ccwin_set_color called\n");
+
+ if (p->nowin) {
+ debugr("ccwin_set_color: nowin - give up\n");
+ return 1;
+ }
+
+ orgb[0] = p->rgb[0]; p->rgb[0] = r;
+ orgb[1] = p->rgb[1]; p->rgb[1] = g;
+ orgb[2] = p->rgb[2]; p->rgb[2] = b;
+
+ for (j = 0; j < 3; j++) {
+ if (p->rgb[j] < 0.0)
+ p->rgb[j] = 0.0;
+ else if (p->rgb[j] > 1.0)
+ p->rgb[j] = 1.0;
+ p->r_rgb[j] = p->s_rgb[j] = p->rgb[j];
+ if (p->out_tvenc) {
+ p->r_rgb[j] = p->s_rgb[j] = ((235.0 - 16.0) * p->s_rgb[j] + 16.0)/255.0;
+
+ /* For video encoding the extra bits of precision are created by bit shifting */
+ /* rather than scaling, so we need to scale the fp value to account for this. */
+ if (p->pdepth > 8)
+ p->r_rgb[j] = (p->s_rgb[j] * 255 * (1 << (p->pdepth - 8)))
+ /((1 << p->pdepth) - 1.0);
+ }
+ }
+
+ /* This is probably not actually thread safe... */
+ p->ncix++;
+
+#if DDITHER != 1
+# pragma message("############################# ccwin.c DDITHER != 1 ##")
+#endif
+
+ /* Turn the color into a png file */
+ {
+ /* We want a raster of IWIDTH x IHEIGHT pixels for web server, */
+ /* or p->w x p->h for PNG direct. */
+ render2d *r;
+ prim2d *rct;
+ depth2d depth = bpc8_2d;
+#if DDITHER == 1
+ int dither = 0x8002; /* 0x8002 = error diffuse FG only */
+#elif DDITHER == 2
+ int dither = 0x4000; /* 0x4000 = no dither but don't average pixels */
+ /* so as to allow pattern to come through. */
+#else
+ int dither = 0; /* Don't dither in renderer */
+#endif
+ double hres = 1.0; /* Resoltion in pix/mm */
+ double vres = 1.0; /* Resoltion in pix/mm */
+ double iw, ih; /* Size of page in mm (pixels) */
+ double bg[3]; /* Background color */
+ color2d c;
+ unsigned char *ibuf; /* Memory image of .png file */
+ size_t ilen;
+#ifdef DO_TIMING
+ int stime;
+#endif
+
+ if (ws->direct) {
+ iw = ws->w; /* Requested size */
+ ih = ws->h;
+ } else {
+ iw = IWIDTH;
+ ih = IHEIGHT; /* Size of page in mm */
+ }
+
+ if (p->blackbg) {
+ bg[0] = 0.0;
+ bg[1] = 0.0;
+ bg[2] = 0.0;
+ } else {
+ bg[0] = 0.5;
+ bg[1] = 0.5;
+ bg[2] = 0.5;
+ }
+
+ debugr2((errout, "ccwin_set_color iw %f ih %f\n",iw,ih));
+
+ if ((r = new_render2d(iw, ih, NULL, hres, vres, rgb_2d,
+ 0, depth, dither,
+#if DDITHER == 1
+ ccastQuant,
+#else
+ NULL,
+#endif
+ NULL)) == NULL) {
+ error("ccwin: new_render2d() failed");
+ }
+
+ /* Set the background color */
+ c[0] = bg[0];
+ c[1] = bg[1];
+ c[2] = bg[2];
+ r->set_defc(r, c);
+
+ c[0] = p->r_rgb[0];
+ c[1] = p->r_rgb[1];
+ c[2] = p->r_rgb[2];
+ if (ws->direct)
+ r->add(r, rct = new_rect2d(r, 0.0, 0.0, ws->w, ws->h, c)) ;
+ else
+ r->add(r, rct = new_rect2d(r, ws->x, ws->y, ws->w, ws->h, c)) ;
+
+#if DDITHER == 2 /* Use dither pattern */
+ {
+ double rgb[3];
+ double dpat[CCDITHSIZE][CCDITHSIZE][3];
+ double (*cpat)[MXPATSIZE][MXPATSIZE][TOTC2D];
+ int i, j;
+
+ /* Get a chrome cast dither pattern to match target color */
+ for (i = 0; i < 3; i++)
+ rgb[i] = p->r_rgb[i] * 255.0;
+ get_ccast_dith(dpat, rgb);
+
+ if ((cpat = malloc(sizeof(double) * MXPATSIZE * MXPATSIZE * TOTC2D)) == NULL)
+ error("ccwin: malloc of dither pattern failed");
+
+ for (i = 0; i < CCDITHSIZE; i++) {
+ for (j = 0; j < CCDITHSIZE; j++) {
+ int k = (((int)IHEIGHT-2) - j) % CCDITHSIZE; /* Flip to origin bot left */
+ (*cpat)[i][k][0] = dpat[i][j][0]/255.0; /* (HEIGHT-2 is correct!) */
+ (*cpat)[i][k][1] = dpat[i][j][1]/255.0;
+ (*cpat)[i][k][2] = dpat[i][j][2]/255.0;
+ }
+ }
+
+ set_rect2d_dpat((rect2d *)rct, cpat, CCDITHSIZE, CCDITHSIZE);
+ }
+#endif /* DDITHER == 2 */
+
+#ifdef CCTEST_PATTERN
+#pragma message("############################# ccwin.c TEST_PATTERN defined ! ##")
+ if (getenv("ARGYLL_CCAST_TEST_PATTERN") != NULL) {
+ verbose(0, "Writing test pattern to '%s'\n","testpattern.png");
+ if (r->write(r, "testpattern.png", 1, NULL, NULL, png_file))
+ error("ccwin: render->write failed");
+ }
+#else /* !CCTEST_PATTERN */
+# ifdef WRITE_PNG /* Write it to a file so that we can look at it */
+# pragma message("############################# spectro/ccwin.c WRITE_PNG is enabled ######")
+ if (r->write(r, "ccwin.png", 1, NULL, NULL, png_file))
+ error("ccwin: render->write failed");
+# endif /* WRITE_PNG */
+#endif /* !CCTEST_PATTERN */
+
+#ifdef DO_TIMING
+ stime = msec_time();
+#endif
+ if (r->write(r, "MemoryBuf", 1, &ibuf, &ilen, png_mem))
+ error("ccwin: render->write failed");
+#ifdef DO_TIMING
+ stime = msec_time() - stime;
+ printf("render->write took %d msec\n",stime);
+#endif
+ if (ws->update(ws, ibuf, ilen, bg))
+ error("ccwin: color update failed");
+ p->ccix = p->ncix;
+ }
+
+ /* If update is notified asyncronously ... */
+// while(p->ncix != p->ccix) {
+// msec_sleep(50);
+// }
+//printf("#################################################################\n");
+//printf("################# RGB update notified ################\n");
+//printf("#################################################################\n");
+
+ /* Allow for display update & instrument delays */
+ update_delay = dispwin_compute_delay(p, orgb);
+ debugr2((errout, "ccwin_set_color delaying %d msec\n",update_delay));
+ msec_sleep(update_delay);
+
+ return 0;
+}
+
+/* Set/unset the blackground color flag */
+/* Return nz on error */
+static int ccwin_set_bg(dispwin *p, int blackbg) {
+ p->blackbg = blackbg;
+
+ return 0;
+}
+
+
+/* ----------------------------------------------- */
+/* Set the shell set color callout */
+void ccwin_set_callout(
+dispwin *p,
+char *callout
+) {
+ debugr2((errout,"ccwin_set_callout called with '%s'\n",callout));
+
+ p->callout = strdup(callout);
+}
+
+/* ----------------------------------------------- */
+/* Destroy ourselves */
+static void ccwin_del(
+dispwin *p
+) {
+ chws *ws;
+
+ debugr("ccwin_del called\n");
+
+ if (p == NULL)
+ return;
+
+ ws = (chws *)p->pcntx;
+
+ if (ws != NULL)
+ ws->del(ws);
+
+ if (p->name != NULL)
+ free(p->name);
+ if (p->description != NULL)
+ free(p->description);
+ if (p->callout != NULL)
+ free(p->callout);
+
+ free(p);
+}
+
+/* ----------------------------------------------- */
+
+/* Create a web display test window, default grey */
+dispwin *new_ccwin(
+ccast_id *cc_id, /* ChromeCast to open */
+double width, double height, /* Width and height in mm. (TV width assumed to b 1000mm) */
+double hoff, double voff, /* Offset from center in fraction of screen, range -1.0 .. 1.0 */
+int nowin, /* NZ if no window should be created - RAMDAC access only */
+int native, /* X0 = use current per channel calibration curve */
+ /* X1 = set native linear output and use ramdac high precn. */
+ /* 0X = use current color management cLut (MadVR) */
+ /* 1X = disable color management cLUT (MadVR) */
+int *noramdac, /* Return nz if no ramdac access. native is set to X0 */
+int *nocm, /* Return nz if no CM cLUT access. native is set to 0X */
+int out_tvenc, /* 1 = use RGB Video Level encoding */
+int blackbg, /* NZ if whole screen should be filled with black */
+int verb, /* NZ for verbose prompts */
+int ddebug /* >0 to print debug statements to stderr */
+) {
+ dispwin *p = NULL;
+ char *cp;
+ chws *ws = NULL;
+ const char *options[3];
+
+ debug("new_ccwin called\n");
+
+ if ((p = (dispwin *)calloc(sizeof(dispwin), 1)) == NULL) {
+ if (ddebug) fprintf(stderr,"new_ccwin failed because malloc failed\n");
+ return NULL;
+ }
+
+ /* !!!! Make changes in dispwin.c & madvrwin.c as well !!!! */
+ p->name = strdup("Web Window");
+ p->width = width;
+ p->height = height;
+ p->nowin = nowin;
+ p->native = native;
+ p->out_tvenc = out_tvenc;
+ p->blackbg = blackbg;
+ p->ddebug = ddebug;
+ p->get_ramdac = ccwin_get_ramdac;
+ p->set_ramdac = ccwin_set_ramdac;
+ p->install_profile = ccwin_install_profile;
+ p->uninstall_profile = ccwin_uninstall_profile;
+ p->get_profile = ccwin_get_profile;
+ p->set_color = ccwin_set_color;
+ p->set_bg = ccwin_set_bg;
+ p->set_update_delay = dispwin_set_update_delay;
+ p->set_settling_delay = dispwin_set_settling_delay;
+ p->enable_update_delay = dispwin_enable_update_delay;
+ p->set_callout = ccwin_set_callout;
+ p->del = ccwin_del;
+
+ if (noramdac != NULL)
+ *noramdac = 1;
+ p->native &= ~1;
+
+ if (nocm != NULL)
+ *nocm = 1;
+ p->native &= ~2;
+
+ p->rgb[0] = p->rgb[1] = p->rgb[2] = 0.5; /* Set Grey as the initial test color */
+
+ dispwin_set_default_delays(p);
+
+ p->ncix = 1;
+
+ p->pdepth = 8; /* Assume this by API */
+ p->edepth = 8;
+
+ /* Basic object is initialised, so create connection to ChromeCast */
+ if ((ws = new_chws(cc_id, width, height, hoff, voff, verb, ddebug)) == NULL) {
+ if (ddebug) fprintf(stderr,"new_ccwin failed - new_chws() failed\n");
+ return NULL;
+ }
+
+ /* Extra delay ccast adds after confirming load */
+ p->extra_update_delay = ws->cc->get_load_delay(ws->cc) / 1000.0;
+
+ p->pcntx = (void *)ws;
+
+ /* Create a suitable description */
+ {
+ char buf[100];
+ sprintf(buf,"ChromeCast '%s'",cc_id->name);
+ p->description = strdup(buf);
+ }
+
+ // Set a default first color
+ if (ccwin_set_color(p, 128.0, 128.0, 128.0)) {
+ if (ddebug) fprintf(stderr,"new_ccwin set_color()\n");
+ p->del(p);
+ return NULL;
+ }
+
+ debugr("new_ccwin: return sucessfully\n");
+
+ return p;
+}
+